JPA实体映射——多对一关系映射

时间:2022-06-01 16:40:10

前几节我们介绍了一对多的关系和一对一关系,今天我们学习多对一关系以及这种映射方式的最佳实践,先上业务实例图。

JPA实体映射——多对一关系映射

在我们的业务关系图中,部门和研究所实体是多对一的关系,同时我们还是采用双向关联来说明问题

Bidirectional @ManyToOne

部门实体

import javax.persistence.*;
import java.io.Serializable;
import java.util.Objects;
@Entity(name = "Department")
@Table(name = "departments")
public class Department implements Serializable {
    @Id
    @GeneratedValue
    private Long id = 0L;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Institute institute;

    public Department(){}

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setInstitute(Institute institute) {
        this.institute = institute;
    }

    public Department(String name){
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Institute getInstitute() {
        return institute;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Department)) return false;
        Department that = (Department) o;
        return name.equals(that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

DepartmentDAO

import com.jpa.demo.model.bidirectional.Department;
import com.jpa.demo.model.bidirectional.Institute;
import com.jpa.demo.utils.JPAUtil;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;

public class DepartmentDAO {

    private EntityManagerFactory entityManagerFactory = JPAUtil.getEntityManagerFactory();


    public void saveDepartment(Long instituteId) {
        EntityManager entityManager = null;
        Long id = null;
        try {
            entityManager = this.entityManagerFactory.createEntityManager();
            EntityTransaction tx = entityManager.getTransaction();
            tx.begin();
            Institute institute = entityManager.find(Institute.class, instituteId);
            Department department = new Department("深圳研究所-第一部门");
            department.setInstitute(institute);
            entityManager.persist(department);
            tx.commit();
        } finally {
            entityManager.close();
        }
    }
}

测试代码

@Test
public void testSaveDepartment() {
    Institute institute = new Institute("深圳研究所");
    institute.addDepartment(
        new Department("深圳研究所1部")
    );
    InstituteDAO dao = new InstituteDAO();
    dao.save(institute);
    Long instituteId = institute.getId();

    DepartmentDAO departmentDAO = new DepartmentDAO();
    departmentDAO.saveDepartment(instituteId);
}

日志信息

Hibernate: 
    select
        institute0_.id as id1_1_0_,
        institute0_.name as name2_1_0_ 
    from
        institutes institute0_ 
    where
        institute0_.id=?
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        departments
        (institute_id, name, id) 
    values
        (?, ?, ?)

可以看出,我在保存部门信息的时候,同时还查询除了研究所信息,这里可以优化为直接保存部门信息。

只需要修改一行代码

Institute institute = entityManager.getReference(Institute.class, instituteId);

修改成getReference()方法后,就不用去查询研究所的实体啦。

Hibernate: 
   insert 
   into
       departments
       (institute_id, name, id) 
   values
       (?, ?, ?)

 

查询多对一多方的一条记录,查询方法如下:

public void queryDepartment(Long departmentId) {
    EntityManager entityManager = null;
    try {
        entityManager = this.entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        Department department = entityManager.find(Department.class, departmentId);
        department.getInstitute().getName();

        tx.commit();
    } finally {
        entityManager.close();
    }
 
}

测试代码

@Test
public void testQueryDepartment() {
    Institute institute = new Institute("深圳研究所");
    institute.addDepartment(
            new Department("深圳研究所1部")
    );
    InstituteDAO dao = new InstituteDAO();
    dao.save(institute);
    Long instituteId = institute.getId();

    DepartmentDAO departmentDAO = new DepartmentDAO();
    Department department = departmentDAO.saveDepartment(instituteId);
    Long departmentId = department.getId();
    departmentDAO.queryDepartment(departmentId);
}

日志信息:

Hibernate: 
   select
       department0_.id as id1_0_0_,
       department0_.institute_id as institut3_0_0_,
       department0_.name as name2_0_0_ 
   from
       departments department0_ 
   where
       department0_.id=?
Hibernate: 
   select
       institute0_.id as id1_1_0_,
       institute0_.name as name2_1_0_ 
   from
       institutes institute0_ 
   where
       institute0_.id=?

可以看出,为了获取研究所的信息,同时也查询了研究所实体,在此情况下,可以使用另一种方法不用查询研究所实体。

ublic void queryDepartmentByJPQL(Long departmentId) {
        EntityManager entityManager = null;
        try {
            entityManager = this.entityManagerFactory.createEntityManager();
            EntityTransaction tx = entityManager.getTransaction();
            tx.begin();
            Department department = entityManager.createQuery("select dt from Department dt join fetch\n" +
                            "                     dt.institute where dt.id =:id", Department.class)
                    .setParameter("id",departmentId)
                    .getSingleResult();;
            department.getInstitute().getName();
            entityManager.persist(department);
            tx.commit();
        } finally {
            entityManager.close();
        }
    }

测试方法只需要修改一行

departmentDAO.queryDepartmentByJPQL(departmentId);

日志信息如下:

Hibernate: 
   select
       department0_.id as id1_0_0_,
       institute1_.id as id1_1_1_,
       department0_.institute_id as institut3_0_0_,
       department0_.name as name2_0_0_,
       institute1_.name as name2_1_1_ 
   from
       departments department0_ 
   inner join
       institutes institute1_ 
           on department0_.institute_id=institute1_.id 
   where
       department0_.id=?

可以看出,这次只发出了一条SQL,不同于上一种方式发出了两条SQL,优化了性能,同时也取得了相应的值。