天天看點

spirngboot使用hibernate,完成映射關系及其使用場景探究

   一直對資料庫映射這塊缺乏練習,導緻在實踐中總是卡在一個某個具體的問題上。是以這裡花上一小段時間将映射這塊練習一下,友善日後回顧。

   環境準備:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.automannn</groupId>
    <artifactId>hibernateRelationShipPractice</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>


</project>      

資料源,目錄,以及啟動類:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
package com.automannn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 
 * @time 2018/10/20 16:51
 */
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}      

    一對一單向映射的練習:

設  一個學校有且僅有一個校長,一個校長僅能任職于一個學校。 是以他們就構成了一對一的關系。  

package com.automannn.entity;

import javax.persistence.*;

/**
 * 
 * @time 2018/10/20 17:08
 */
@Entity
public class HeadMaster {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String headMasterName;

    @OneToOne
    private School school;
}      
package com.automannn.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 *
 * @time 2018/10/20 17:07
 */
@Entity
public class School {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String schoolName;
}      

  這個時候,校長是主要方,持有學校的資訊。運作之後查表:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

  在這種場合下,我們可以通過主要方檢視另一方的資訊。即通過校長檢視到相應的學校資訊。 反之,若學校為主要方,那麼相應的也要反轉,這是單向的。  并且他們的聯系是通過外鍵關聯的,但是我看到很多的部落格都說不要使用外鍵,關系的對應通過程式控制。

  一對一雙向映射練習:

package com.automannn.entity;

import javax.persistence.*;

/**
 * @author 
 * @time 2018/10/20 17:08
 */
@Entity
public class HeadMaster {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String headMasterName;

    @OneToOne(mappedBy = "headMaster")
    private School school;
}      
package com.automannn.entity;

import javax.persistence.*;

/**
 * @time 2018/10/20 17:07
 */
@Entity
public class School {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String schoolName;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "headMaster_id")
    private HeadMaster headMaster;
}      

它們的表結構如下:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   要注意,将hibernate在update狀态下,貌似對于删除表結構的操作可能有缺陷,是以導緻我測試浪費了一些時間。

spirngboot使用hibernate,完成映射關系及其使用場景探究

   最簡形式:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

同時,注解的屬性與類中的屬性通過程式上下文聯系起來。  不具有固定性!!!   後面思考了下,這種說法不準确,雖然是上下文,但是屬性變量實際上是起到了一個管道的作用,是以要寫的對應關系還是很好了解的了。

多與一的常見關系映射練習:

  設:  某個班具有多個學生,每個學生隻能屬于一個班。  是以,構成了這樣的一個多與一的映射關系。

   現有一個需求如下:

     需要知道每個班有哪些學生?  那麼可以通過單向一對多,單向多對一考慮。

    單向一對多:

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
 * @author 
 * @time 2018/10/20 16:53
 */
@Entity
public class Clazz {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String clazzName;

    @OneToMany
    private List<Student> studentList;
}      
package com.automannn.entity;

import javax.persistence.*;

/**
 * @author [email protected]
 * @time 2018/10/20 16:52
 */

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

}      

  這種情況下,它生成的表結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   會生成一張隻具有外鍵的中間表。 并不是我們想要的效果。

  這是在具有最簡注解:

spirngboot使用hibernate,完成映射關系及其使用場景探究

的情況。

當我們在Clazz.class修改如下:

spirngboot使用hibernate,完成映射關系及其使用場景探究

  可以發現它的表結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   可以發現一個現象,就是我們的那個joinColumn是在 Clazz.class實體寫的,但是加入的屬性列在Student表中。  因為,隻要是一與多的關系,hibernate都是在多的一方記錄資訊。 

一緻性維護由哪一方完成,因為這裡涉及到性能的問題。但是由于這裡是單向的關系,jpa規範應該是預設由多的一方完成吧,因為在注解中沒找到inverse屬性,同時看網上的解釋也有:

spirngboot使用hibernate,完成映射關系及其使用場景探究

  是以就不再去看了。  另外要注意,mappedBy  與 joinColumn不能同時出現,會報錯。mappedBy隻适用于雙向關系的那種可能。

  最簡配置:

spirngboot使用hibernate,完成映射關系及其使用場景探究

   結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究

以上可以滿足通過班級查詢學生,而且是比較直覺的了解。

那麼,多對一實際也可以滿足這種需求。

  單向一對多的練習:

package com.automannn.entity;

import javax.persistence.*;

/**
 * @author
 * @time 2018/10/20 16:52
 */

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @ManyToOne
    private Clazz clazz;

}      
package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
 * @author 
 * @time 2018/10/20 16:53
 */
@Entity
public class Clazz {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String clazzName;

}      

   它生成的表結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   這種情況下,我們實際上是去查詢學生表,将班級以屬性的方式進行查詢的。

查詢班級表,以清單方式直覺的将本班級學生給查詢出來。

都能夠對兩個表的資訊進行增删該查。 并且表的結構也一樣,隻是查詢的邏輯不同,還有就是性能方面可能有一些不同。

    由于我并沒有很多的實戰經驗,但是我的感覺是,當查詢的請求為主時,完全可以使用單向一對多的方式将所有的資訊給查出來。否則,當以資料更新操作為主的時候,應該使用單向多對一的方式。

Ok,如果有另一個需求,需要知道每個學生所在的班級資訊。  也就是需要從多的一端擷取一的資料。這個時候可以這樣幹,那就是在根本上修改這種一與多的關系,導緻它變長多與多的關系,形成一個回環。  但是這樣很明顯是不明智的。正确的做法應該是使用雙向關系。因為這裡是雙向的,是以也就不存在方向性了,因為它的表結構都是一樣的。

  雙向多與一映射關系練習:

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
 * @author 
 * @time 2018/10/20 16:53
 */
@Entity
public class Clazz {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String clazzName;

    @OneToMany(mappedBy = "clazz")
    private List<Student> studentList;

}      
package com.automannn.entity;

import javax.persistence.*;

/**
 * @aut
 * @time 2018/10/20 16:52
 */

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @ManyToOne
    private Clazz clazz;

}      

    它生成的表結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   注意一下,@ManyToOne是沒有mappedBy屬性的。

    fetch屬性一般預設即可,因為預設的都是推薦的。

     可以說,雙向的這種一與多的關系簡直就是我們的理想狀态了。  并且将維護關系設定為多的一方,那我們就可以很友善,愉快的進行開發哈哈哈。  都說對性能有影響,但是我覺得性能是一個虛的概念,當業務需求根本不可能到達瓶頸的時候,性能問題根本不是問題了。

   很開心愉快的進行了一對一單雙向,一對多單向,多對一單向,一與多雙向的練習。  接下來就進入最後一個的練習。 多對多映射。

多對多映射的練習:

 設:一個老師有多個學生,一個學生可以被多個老師教。  是以他們就構成了一種多對多的關系。

  在這種多對多的關系中,我們經常要通過一方查詢另一方的需求,是以也就不考慮單雙向的問題了。

   但是發現多對多關系中是存在單雙向問題的:

spirngboot使用hibernate,完成映射關系及其使用場景探究
package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
 * @author 
 * @time 2018/10/20 19:29
 */
@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String teacherName;

    @ManyToMany
    private List<Student> student;
}      
package com.automannn.entity;

import com.automannn.before.Clazz;

import javax.persistence.*;

/**
 * @author [email protected]
 * @time 2018/10/20 16:52
 */

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;


}      

生成的表結構如下:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

   這是單向的情況。  接着修改代碼,使之變為雙向,以滿足業務需求。

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
 * @author 
 * @time 2018/10/20 19:29
 */
@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String teacherName;

    @ManyToMany(mappedBy ="teacherList" )
    private List<Student> studentList;
}      
package com.automannn.entity;

import com.automannn.before.Clazz;

import javax.persistence.*;
import java.util.List;

/**
 * @time 2018/10/20 16:52
 */

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @ManyToMany
    private List<Teacher> teacherList;
}      

它的表結構:

spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究
spirngboot使用hibernate,完成映射關系及其使用場景探究

實踐過程的一些反思與總結: