JPA实体映射——一对多关系映射(上)

选中文字可对指定文章内容进行评论啦,绿色背景文字可以点击查看评论额。

依照上一节的案例,我们来拆解一对多的关系怎么使用,首先还是把业务关系图弄出来。

业务案例图

业务分析

从图中我们知道,研究所和部门是一对多关系,今天我们来展示,一对多设计的最佳实践。

我们知道在JPA的实体设计中,一对多的关系可以设计成单向关联,也可以设计成双向关联。今天我们一步一步来实践各种设计,从而总结出最佳实践。

Unidirectional @OneToMany

 

研究所实体

port javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "institutes")
public class Institute implements Serializable {

    @Id
    @GeneratedValue
    private Long id;
    @OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private Set<Department> departments = new HashSet<>(0);

    private String name;

    public Institute(){
    }

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

    public Long getId() {
        return id;
    }

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

    public Set<Department> getDepartments() {
        return departments;
    }

    public void setDepartments(Set<Department> departments) {
        this.departments = departments;
    }

    public String getName() {
        return name;
    }

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

部门实体

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;


@Entity
@Table(name = "departments")
public class Department implements Serializable {

    @Id
    @GeneratedValue
    private Long id = 0L;

    private String name;

    public Department(){}

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

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

研究所DAO

import com.jpa.demo.model.undirectional.Institute;
import com.jpa.demo.utils.JPAUtil;

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

public class InstituteDao {

    private EntityManagerFactory entityManagerFactory = JPAUtil.getEntityManagerFactory();
    public Long save(Institute institute) {
        EntityManager entityManager = null;
        Long id = null;
        try {
            entityManager = this.entityManagerFactory.createEntityManager();
            EntityTransaction tx = entityManager.getTransaction();
            tx.begin();
            entityManager.persist(institute);
            id = institute.getId();
            tx.commit();
        } finally {
            entityManager.close();
        }
        return id;
    }
}

工具类就不展示啦,可以查看https://devnote.pro/noteweb/docs/10000020363496/contents/10000063133496

测试类

import com.jpa.demo.model.undirectional.Department;
import com.jpa.demo.model.undirectional.Institute;
import org.junit.Test;

public class InstituteDaoTest {

    @Test
    public void testSaveInstitute(){
        Institute institute = new Institute("深圳研究所");
        institute.getDepartments().add(new Department("深圳研究所1部"));
        institute.getDepartments().add(new Department("深圳研究所2部"));
        institute.getDepartments().add(new Department("深圳研究所3部"));

        InstituteDao dao = new InstituteDao();
        dao.save(institute);
    }
}

查看日志信息如下:

Hibernate: 
    
    create table departments (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table institutes (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table institutes_departments (
       Institute_id bigint not null,
        departments_id bigint not null,
        primary key (Institute_id, departments_id)
    )

Hibernate: 
    
    alter table institutes_departments 
       drop constraint if exists UK_67unewuyoqel2nu2ef8me1hyv
Hibernate: 
    
    alter table institutes_departments 
       add constraint UK_67unewuyoqel2nu2ef8me1hyv unique (departments_id)
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    
    alter table institutes_departments 
       add constraint FK3lq3buf7j6k7is3q1lurp2j91 
       foreign key (departments_id) 
       references departments
Hibernate: 
    
    alter table institutes_departments 
       add constraint FKlqld2ri9e0qmvhwy6nnany0o9 
       foreign key (Institute_id) 
       references institutes

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        institutes
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)

首先,我们知道单向数据关联,生成了三张表分别是departmentsinstitutes还有institutes_departments

然后在中间表的department_id上建立唯一约束,最后在中间表的字段上建立外键,分别指向各自的表主键。

我在保存数据库的时候,首先在institutes上插入一条数据,然后在departments上插入三条数据,最后插入institutes_departments表对应的数据,这样这个一对多的关系就建立起来。

这种设计显然存在很多问题:

1、生成了一个冗余的中间表

2、发出了多条SQL执行语句

3、建立了两个外键,我们需要更多的缓存

 

下面我们看看单向关系的另一种@OneToMany

Unidirectional @OneToMany with @JoinColumn

这次我们只需要增加一个注解

研究所实体修改

@OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    @JoinColumn(name = "department_id")
    private Set<Department> departments = new HashSet<>(0);

同理,我们执行测试方法后,日志信息如下:

Hibernate: 
    
    create table departments (
       id bigint not null,
        name varchar(255),
        department_id bigint,
        primary key (id)
    )
Hibernate: 
    
    create table institutes (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    
    alter table departments 
       add constraint FK3ttxjsckxb3vwld1idf7a3r29 
       foreign key (department_id) 
       references institutes

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        institutes
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?

从日志信息可以看出,这次只生成了两个表institutesdepartments,并且在departments上多了一个department_id,这个字段实际上就是我们在研究所实体上配置的外键。从SQL执行可以看出,插入的语句没有啥变化,只不过多了三个更新外键字段的语句。总的说来,有所改善,但是效果不是最好。

今天我们暂时学习到这里,当然这里还出有一些有意思的问题需要探讨。我们下一节继续。欢迎讨论。

版权声明:著作权归作者所有。

相关推荐

[译]代码优先的Java 9模块系统教程(一)

Java平台模块系统(JPMS)将模块化带入Java和JVM,并改变了我们在大型应用中的编程方式。 为了充分利用它,我们需要很好地了解,第一步是学习基础知识。 在本教程中,我首先向你展示一个简单的Hello World示例,然后我们用Java 9将一个现有的demo程序模块化。我们将创建模块声明(module-info。java),使用模块路径来编译,打包,

Nginx同一个域名配置多个项目

使用Nginx要在同一个域名下配置多个项目有两种方式:nginx按不同的目录分发给不同的项目启用二级域名,不同的项目分配不同的二级域名nginx按不同的目录分发给不同的项目在nginx的server节点配置server {     listen    80;     server_name example.c

CentOS 7配置安装多个redis实例

1、给新redis实例创建redis配置(复制redis.conf)$ cp /etc/redis.conf /etc/redis-xxx.conf2、修改复制的redis-xxx.conf...#修改pidfile#pidfile /var/run/redis/redis.pidpidfile /var/run/redis/redis-xxx.pid...#dir /var/lib/redis/

雪花算法实现-分布式系统

一、订单id的特殊性订单数据非常庞大,将来一定会做分库分表。那么这种情况下, 要保证id的唯一,就不能靠数据库自增,而是自己来实现算法,生成唯一id。二、雪花算法这里的订单id是通过一个工具类生成的,而工具类所采用的生成id算法,是由Twitter公司开源的snowflake(雪花)算法。三、简单原理雪花算法会生成一个64位的二进制数据,为一个Long型。(转换成字符串后长度最多19位) ,其基本

Linux使用halt命令关闭运行中操作系统

halt命令用来关闭正在运行的Linux操作系统。halt命令会先检测系统的runlevel,若runlevel为0或6,则关闭系统,否则即调用shutdown来关闭系统。 语法 halt [-n] [-w] [-d] [-f] [-i] [-p] 选项说明 -d:不要在wtmp中记录;-f:不论目前的runlevel为何,不调用shutdown即强制关闭系统;-i:在halt之前,关闭全部的

Java反射获取方法的参数名,Maven/IDEA/Eclipse配置

从JDK8开始,反射类添加了Parameter,通过Parameter类,我们可以从.class编译后的文件中获取方法上参数名。获取参数名的方法:Parameter.getName()示例:public static List<String> getParameterNameJava8(Class clazz, String methodName) { List<String&g

JPA中OneToMany关系时,单向关联和双向关联如何使用?

最近因为项目的原因,所以需要使用JPA来对数据进行持久化,因此对这部分内容做了一些总结,特别是对@OneToMany这个注解有了一些学习和理解:我们知道,注解@OneToMany是对一对多关系的一个注解,这里我们需要注意的是:一对多的关系分为两类,一类是单向关系,另一类就是双向关系这里就有一个问题啦?啥叫单向关系(unidirectional),啥叫双向关系(bidirectional)?首先解释

CKEditor5 Observable——绑定多个对象或属性

上一节我们学习了如何绑定属性,今天我们继续学习绑定多个属性或者多个Observable对象。 绑定多个属性如何绑定多个属性,下面我们用代码来说明:const button = new Button(); const command = editor.commands.get( 'bold' ); button.bind( 'isOn', 'isEnabled' ).to( command