Jpa之關(guān)聯(lián)對象(單向一對多)

1.對象設(shè)計

Employee 實體

@Entity
@Setter
@Getter
public class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;//名稱
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Department 實體

@Getter
@Setter
@ToString
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany//建立關(guān)聯(lián)一對多
    private List<Employee> employees = new ArrayList<>();

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

2.表的結(jié)構(gòu)

![FOOL`_MGPO%PQUZL)OV76V.png
從圖中可以看出,對于一對多的關(guān)聯(lián)方式,jpa內(nèi)部會采取生成一張中間表來建立一方和多方之間的關(guān)系,因為多了一張表,這種方式會浪費性能。
無論是單向一對多,單向多對一,雙向一對多,多對一,表的結(jié)構(gòu)是不變的!應(yīng)該和多對一的結(jié)構(gòu)是一樣的!

@Getter
@Setter
@ToString
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany//建立關(guān)聯(lián)一對多
    @JoinColumn(name = "dept_id")//指定關(guān)聯(lián)列的名字
    private List<Employee> employees = new ArrayList<>();

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

此時使用@JoinColumn(name = "dept_id")指定外鍵,jpa就不會生成中間表!


{3IY_$3PJ$MS3{`UYF487LQ.png

3.實現(xiàn)

向表中添加數(shù)據(jù):
(1):先保存部門,再保存員工

@Test
    public void testsave(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //創(chuàng)建一方
        Department department = new Department();
        department.setName("行政部");
        //創(chuàng)建多方
        Employee e1 = new Employee();
        e1.setName("lucy");
        Employee e2 = new Employee();
        e2.setName("cici");
        department.getEmployees().add(e1);
        department.getEmployees().add(e2);
        //持久化
        entityManager.persist(department);
        entityManager.persist(e1);
        entityManager.persist(e2);
        entityManager.getTransaction().begin();
        entityManager.getTransaction().commit();
        entityManager.close();
    }

(2):先保存員工,再保存部門

 @Test
    public void testsave(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //創(chuàng)建一方
        Department department = new Department();
        department.setName("行政部");
        //創(chuàng)建多方
        Employee e1 = new Employee();
        e1.setName("lucy");
        Employee e2 = new Employee();
        e2.setName("cici");
        department.getEmployees().add(e1);
        department.getEmployees().add(e2);
        //持久化
        entityManager.persist(e1);
        entityManager.persist(e2);
        entityManager.persist(department);
        entityManager.getTransaction().begin();
        entityManager.getTransaction().commit();
        entityManager.close();
    }

4.添加SQL分析

(1):先保存部門,再保存員工

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?

執(zhí)行了5條SQL:

(2):先保存員工,再保存部門

Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?

發(fā)現(xiàn)不管是先保存一方還是多方都會發(fā)送5條SQL,因為外鍵id是由一方來管理,所以它必須執(zhí)行相應(yīng)的update更新操作!

5.查詢的SQL分析

 @Test
    public void testFind(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //獲取一方
        Department department = entityManager.find(Department.class, 1l);
        entityManager.close();
    }

發(fā)送的SQL

Hibernate : SELECT
    employee0_.id AS id1_1_0_,
    employee0_. NAME AS name2_1_0_
FROM
    Employee employee0_
WHERE
    employee0_.id =?

并沒有去執(zhí)行查詢多方的信息,說明默認是延遲加載!

 @Test
    public void testFind(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //獲取一方
        Department department = entityManager.find(Department.class, 1l);
        entityManager.close();
        System.out.println(department.getEmployees());
    }

拋出異常:延遲初始化異常,也說明了默認是延遲加載
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hanfengyi.one2many.Department.employees, could not initialize proxy - no Session

6.判斷集合中的數(shù)據(jù)

通過一方獲取多方,判斷集合是否有數(shù)據(jù)!不能夠直接判斷是否為null,因為在實體中為防止報錯,已經(jīng)初始化了。所以無論如何它都不為null。

@Test
    public void testFind(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //獲取一方
        Department department = entityManager.find(Department.class, 1l);
        if(department.getEmployees().size()!=0){
            System.out.println("有數(shù)據(jù)");
        }
        entityManager.close();
    }

7.集合映射

List集合

 @Test
    public void testLMapping(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        Department department = entityManager.find(Department.class, 1l);
        System.out.println(department.getEmployees().getClass());//class org.hibernate.collection.internal.PersistentBag
        entityManager.close();
        }

Set集合

@Test
    public void testLMapping(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        Department department = entityManager.find(Department.class, 1l);
        System.out.println(department.getEmployees().getClass());//class org.hibernate.collection.internal.PersistentSet
        entityManager.close();
        }

這里是Hibernate為了實現(xiàn)延遲加載而提供的類

org.hibernate.collection.internal.PersistentSet 實現(xiàn)了java.util.Set接口
org.hibernate.collection.internal.PersistentBag 實現(xiàn)了java.util.List接口
我們聲明集合的時候必須使用接口,因為Hibernate是用它自己的List/Set實現(xiàn)類PersistentBag /PersistentSet 來接收數(shù)據(jù)!如果我們直接使用實現(xiàn)類初始化,PersistentBag /PersistentSet 和ArrayList/HashSet是平級的,會拋出異常。

8.List映射

Set:無序不重復(fù)
List:有序可重復(fù)
使用List集合排序的方式:
在集合的上方使用注解 @OrderBy("參數(shù)為多方需要排序的屬性名")

@Getter
@Setter
@ToString
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany//建立關(guān)聯(lián)一對多
    @JoinColumn(name = "dept_id")//指定關(guān)聯(lián)列的名字
    @OrderBy("name")//將name屬性進行排序,默認是升序
    private List<Employee> employees = new ArrayList<>();

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
@Test
    public void testFind(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //獲取一方
        Department department = entityManager.find(Department.class, 1l);
        if(department.getEmployees().size()!=0){
            System.out.println("有數(shù)據(jù)");
            System.out.println(department.getEmployees());
            //結(jié)果:[Employee{id=2, name='cici'}, Employee{id=1, name='lucy'}]
        }
        entityManager.close();
    }

小結(jié):
1.一對多與多對一的表的結(jié)構(gòu)是一樣的,只是站的角度不同。
2.一對多的關(guān)聯(lián)方式使用注解@OneToMany,jpa會采取生成中間表來關(guān)聯(lián)。
3.在一方使用@JoinColumn(name = " ")指定關(guān)聯(lián)列的名字,就不會生成中間表。
4.無論是先保存一方還是先保存多方,執(zhí)行的SQL語句條數(shù)是一樣的!因為外鍵是由一方來處理的,所以一對多的性能很差,推薦使用雙向!
5.一對多默認是延遲加載,因為多方是集合,延遲加載可以很好的節(jié)約性能!
6.集合必須使用接口聲明,因為Hibernate要用到他自己的實現(xiàn)類來接收數(shù)據(jù)!
7.判斷集合中的數(shù)據(jù)不能直接判斷為null,因該判斷它的size是否不等于0!
8.List集合可以使用注解@OrderBy("參數(shù)為多方需要排序的屬性名")對指定的屬性排序!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這部分主要是開源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,566評論 0 2
  • 一:單向多對一 多對一的關(guān)系:在多方表中添加一個外鍵列來維護雙方關(guān)系。 1. 表結(jié)構(gòu)設(shè)計 2. 對象設(shè)計 在多方關(guān)...
    hanfy閱讀 444評論 0 0
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 一、Hibernate 檢索 hibernate 提供5種檢索數(shù)據(jù)的方式導(dǎo)航對象圖檢索方式: 根據(jù)已經(jīng)加載的對象...
    luweicheng24閱讀 439評論 0 0
  • (1)基礎(chǔ)筆試命令考察1.開啟MySQL服務(wù) 2.檢測端口是否運行 3.為MySQL設(shè)置密碼或者修改密碼設(shè)置密碼更...
    秋幻旎蘇閱讀 387評論 0 0

友情鏈接更多精彩內(nèi)容