Jpa之关联对象(双向多对一,一对多)

1.对象设计

双向多对一,一对多,表的结构应该是一样的,建立两个关联关系,可以从多方找到一方,亦可以从一方找到多方。
Employee 实体

@Entity
@Setter
@Getter
public class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ManyToOne
    @JoinColumn(name = "department_id")
    private Department department;
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Department 实体

@Getter
@Setter
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany()//建立关联一对多
    @JoinColumn(name = "dept_id")//指定关联列的名字
    private List employees = new ArrayList<>();

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

2.表结构

![E}PW@OAA$5JBZ}}U~3M6BX.png
从图中可以看出,这并不是双向关系,而是两个单向关系,因为两个外键列的名字不一样,此时在多方有两个外键分别对应单向多对一和单向一对多。
解决办法:
因为多对一可以自动生成外键列,一对多必须加上@JoinColumn注解指定外键列,否则会生成中间表,浪费性能。此时使外键列的名字相同即可。

@Entity
@Setter
@Getter
public class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ManyToOne
    @JoinColumn(name = "dept_id")
    private Department department;
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
@Getter
@Setter
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany()//建立关联一对多
    @JoinColumn(name = "dept_id")//指定关联列的名字
    private List employees = new ArrayList<>();

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

生成的表结构:


$KI8}HF~X4CR9L%7N}DGASM.png

可以看出这是正常的表结构

3.保存

(1):先保存部门,再保存员工

@Test
    public void testSave(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //准备一方
        Department department = new Department();
        department.setName("策划部");
        //准备多方
        Employee e1 = new Employee();
        e1.setName("alice");
        Employee e2 = new Employee();
        e2.setName("yukki");
        //建立关联关系
        department.getEmployees().add(e1);
        department.getEmployees().add(e2);
        e1.setDepartment(department);
        e2.setDepartment(department);
        //持久化
        entityManager.getTransaction().begin();
        entityManager.persist(department);
        entityManager.persist(e1);
        entityManager.persist(e2);
        entityManager.getTransaction().commit();
        entityManager.close();
    }

(2):先保存部门,再保存员工

@Test
    public void testSave(){
        EntityManager entityManager = JPAUtil.createEntityManager();
        //准备一方
        Department department = new Department();
        department.setName("策划部");
        //准备多方
        Employee e1 = new Employee();
        e1.setName("alice");
        Employee e2 = new Employee();
        e2.setName("yukki");
        //建立关联关系
        department.getEmployees().add(e1);
        department.getEmployees().add(e2);
        e1.setDepartment(department);
        e2.setDepartment(department);
        //持久化
        entityManager.getTransaction().begin();
        entityManager.persist(e1);
        entityManager.persist(e2);
        entityManager.persist(department);
        entityManager.getTransaction().commit();
        entityManager.close();
    }

4.SQL分析

(1):先保存部门,再保存员工

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

执行了5条SQL
(2):先保存员工,再保存部门

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

执行了7条SQL,添加员工信息,没有部门信息,添加部门,此时更新建立关联关系,多方要维护关系,一方也要维护关系,就多执行了两句SQL。

5.正确配置

一方放弃关系维护,将关系维护权交给多方!不需要添加@JoinColumn(name = "dept_id")外键列信息!在@OneToMany注解中添加参数mappedBy = "多方中的属性"!

@Getter
@Setter
@Entity
public class Department {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany(mappedBy = "department")
    private List employees = new ArrayList<>();

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

6.SQL分析

(1):先保存员工,再保存部门

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

(2):先保存部门,再保存员工

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Employee (department_id, name) values (?, ?)

7.查询

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

8.执行的SQL

Hibernate : SELECT
    department0_.id AS id1_0_0_,
    department0_. NAME AS name2_0_0_
FROM
    Department department0_
WHERE
    department0_.id =?

根据一方查找多方的时候,并没有使用多方的信息,此时并没有发送查询多方的SQL,说明是延迟加载!

9.小结

1.双向多对一,一对多的配置需要一方放弃关系维护,将关系维护权交给多方,在@OneToMany注解中添加参数mappedBy = "多方中的属性"。
2.先添加一方,再添加多方,可以减少SQL发送,提高性能!
3.双向多对一,一对多默认是延迟加载!

你可能感兴趣的:(Jpa之关联对象(双向多对一,一对多))