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

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

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

从图中可以看出,研究所和社交账号有一对一的关系,部门和社交账号也有一对一的关系,我们选用研究所和社交账号的关系来说明问题。

Bidirectional @OneToOne

研究所实体

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

@Entity(name = "Institute")
@Table(name = "institutes")
public class Institute implements Serializable {
    @Id
    @GeneratedValue
    private Long id;

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

    @OneToOne( mappedBy = "institute", cascade = CascadeType.ALL,
        fetch = FetchType.LAZY, optional = false)
    private SocialProfile socialProfile;

    private String name;

    public Institute(){}

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

    public void setSocialProfile(SocialProfile socialProfile) {
        if (null == socialProfile){
            if (this.socialProfile!=null) {
                this.socialProfile.setInstitute(null);
            }
        } else {
            socialProfile.setInstitute(this);
        }
        this.socialProfile = socialProfile;
    }

    public void addDepartment(Department department){
        this.departments.add(department);
        department.setInstitute(this);
    }

    public void removeDepartment(Department department) {
        this.departments.remove(department);
        department.setInstitute(null);
    }

    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;
    }
}

这里增加了一个社交账号的属性,采用延迟加载策略。自定义了一个setSocialProfile()的方法。

社交账号实体

import javax.persistence.*;
import java.io.Serializable;

@Entity( name = "SocialProfile")
@Table(name = "socialprofiles")
public class SocialProfile implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    private String shortName;

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

    public SocialProfile(){}

    public SocialProfile(String shortName) {
        this.shortName = shortName;
    }

    public long getId() {
        return id;
    }

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

    public String getShortName() {
        return shortName;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }

    public Institute getInstitute() {
        return institute;
    }

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

测试代码:

@Test
public void testOneToOneSave() {
    Institute institute = new Institute("深圳研究所");
    institute.setSocialProfile(new SocialProfile("深圳研究所-社交账号"));
    InstituteDAO dao = new InstituteDAO();
    dao.save(institute);
}

日志信息:


Hibernate: 
    
    create table institutes (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table socialprofiles (
       id bigint not null,
        shortName varchar(255),
        institute_id bigint,
        primary key (id)
    )

Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    

  
    alter table socialprofiles 
       add constraint FK8o0yil50tmsnyfbqgrlj7b5v0 
       foreign key (institute_id) 
       references institutes

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        institutes
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        socialprofiles
        (institute_id, shortName, id) 
    values
        (?, ?, ?)

可以看出在一对一关系时候,在socialprofiles上添加了一个外键字段institute_id,保存的时候也是将双方关系实体保存。

我们在看看查询

@Test
public void testOneToOneQuery() {
    Institute institute = new Institute("深圳研究所");
    institute.setSocialProfile(new SocialProfile("深圳研究所-社交账号"));
    InstituteDAO dao = new InstituteDAO();
    dao.save(institute);
    long id = institute.getId();
    dao.queryById(id);
}

日志信息:

Hibernate: 
    select
        institute0_.id as id1_1_0_,
        institute0_.name as name2_1_0_ 
    from
        institutes institute0_ 
    where
        institute0_.id=?
Hibernate: 
    select
        socialprof0_.id as id1_2_0_,
        socialprof0_.institute_id as institut3_2_0_,
        socialprof0_.shortName as shortNam2_2_0_ 
    from
        socialprofiles socialprof0_ 
    where
        socialprof0_.institute_id=?

可以看出这里我只是查询研究所信息,但是实际上也将社交账号的信息一并查出,这种方式在一些场景中是合适的,但是有的时候我也许需要延迟加载。因此,我总结如下:

1、这种实现方式在某些场景下是合适的,但是存在两个问题。

2、多了一个外键字段,实际上在一对一关系的时候,外键字段是可以共享的。

3、单向关联的时候延迟加载可行,双向关联的时候延迟加载不可用。

 

双向一对一关联最佳实践

最佳实践的使用方法如下,只需要修改SocialProfile实体就可以啦,代码如下:

import javax.persistence.*;
import java.io.Serializable;

@Entity( name = "SocialProfile")
@Table(name = "socialprofiles")
public class SocialProfile implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    private String shortName;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id")
    private Institute institute;

    public SocialProfile(){}

    public SocialProfile(String shortName) {
        this.shortName = shortName;
    }

    public long getId() {
        return id;
    }

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

    public String getShortName() {
        return shortName;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }

    public Institute getInstitute() {
        return institute;
    }

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

增加使用了一个注解@MapsId,同时将外键映射到id,实际上就是将外键与主键公用。这种方法称为共享主键,也就是一对一的双发实体共享一个主键,在这种情况下,甚至都可以不需要双向关联,有兴趣的可以试试这种情况。

 

 

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

相关推荐

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

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

Git撤销最后一次提交

Git可以使用reset重置来撤销提交。方法一撤销最后一次提交git reset HEAD~1 执行后,状态重置为上一次提交,且撤回提交的文件的状态变回unstaged,即文件没有被git跟踪。示例$ git commit -m 'add test.html' [master ade6d7e] add&nbs

使用ANDROID_ID唯一标识Android设备

使用Settings.Secure#ANDROID_ID 会返回每个用户唯一的64位hex字符串,它是在设备首次boot时生成。我们可以使用它来唯一标识Android设备。import android.provider.Settings.Secure; private String android_id = Secure.getStri

使用Node.js实现一个简单的web服务器

这是使用Node.js写的一个简单Web服务器示例,分为三部分:响应http请求路由url读取静态文件响应新建一个app.js文件作为此web服务器的入口。响应http请求首先我们引入http模块,创建一个http服务器。const http = require('http'); const hostname = '127.0.0.1'

一些有用的pandas代码片段

# 列出dataframe指定列的唯一值 df['Column Name'].unique() # 把列的数据类型转换为数字。如果有非数字值,则会出错。 pd.to_numeric(df['Column Name']) # 把列的数据类型转换为数字,如果非数字值,则会转换为NaN pd.to_numeric(df['Column Na

一段简单的css debugger代码

在做前端开发常常需要给元素添加一些轮廓来debug页面元素的布局,在github上看到一段很简单的js代码,它结合了css,给元素添加轮廓,以便对css,html页面观察dom元素的布局。完整代码如下:/* debug.css | MIT License | zaydek.github.com/debug.css */if (!("is_debugging" in window)) { is

pandas给DataFrame一行一行添加数据

使用pandas的DataFrame有个 简单的功能,先定义pandas的DataFrame,然后按行给DataFrame添加数据。方法一:使用df.loc方法>>> import pandas as pd>>> from numpy.random import randint>>> df = pd.DataFrame(columns=['li

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)?首先解释