jpa-关系类型(一对一、多对一、一对多、多对多)

原文地址:https://blog.csdn.net/dm_vincent/article/details/52877296

此篇为转载,学好jpa弄清关系映射是关键,大佬总结的已经很好了,仔细阅读,受益匪浅,在此基础上简单画了一个导图,仅供参考。

以下为正文↓↓↓↓↓↓

一、分类

根据关系中目标对象的数量,可以将关系简单分为两种。

  • 单值映射(Single-Valued Mapping)
  • 集合映射(Collection-Viewed Mapping)

二、单值映射

2.1 一对一(One-to-One)

单向关系的示例代码

@Entity
public class Employee {
    @Id 
    private int id;

    private String name;

    @OneToOne
    @JoinColumn(name="WSPACE_ID")
    private Workspace workspace;
    // ...
}

@Entity
public class Workspace {
    @Id 
    private int id;

    private String location;
}

@JoinColumn的注解,可理解成外键。而这个外键的列名则是通过@JoinColumn注解中的name属性进行声明。同时,还需要注意的是当@OneToOne@JoinColumn一起使用的时候,这个外键所在的列实际上还需要满足唯一性的约束。

一对一双向关系示例

@Entity
public class Employee {
    @Id 
    private int id;

    private String name;

    @OneToOne
    @JoinColumn(name="WSPACE_ID")
    private Workspace workspace;
    // ...
}

@Entity
public class Workspace {
    @Id 
    private int id;

    private String location;

    @OneToOne(mappedBy = "workspace")
    private Employee employee;
    // ...
}

如果不考虑规范的话,更加直观地定义双向一对一的方式应该是这样的:

@Entity
public class Employee {
    @Id 
    private int id;

    private String name;

    @OneToOne
    @JoinColumn(name="WSPACE_ID")
    private Workspace workspace;
    // ...
}

@Entity
public class Workspace {
    @Id 
    private int id;

    private String location;

    @OneToOne
    @JoinColumn(name="E_ID")
    private Employee employee;
    // ...
}

这种定义方式当然可以,但是这样定义的两个一对一关系之间就没有了联系,它们实际上定义了两个单向的一对一关系。JPA不会认为这两个一对一关系实际上共同构成了一个双向的一对一关系。体现在表结构上,就是在Workspace对应的表结构中,也会有一个外键列用来引用Employee的相应记录。

因此,如果你希望定义的是一个双向的一对一关系,还是遵守JPA的规范,正确地使用@OneToOne注解及其mappedBy属性和用于定义外键列的@JoinColumn注解吧。

最后补充一点,由于@JoinColumn是用来定义外键关系的,那么拥有该注解的一方可以被称为关系中的所有方(Owning Side),而被引用的一方则被称为反转方(Inverse Side)。所以如果是定义双向关系的话,在反转方一般就需要使用mappedBy属性了。

2.2 多对一(Many-to-One)
@Entity
public class Employee {
    @Id 
    private int id;

    @ManyToOne
    @JoinColumn(name="DEPT_ID")
    private Department department;
    // ...
}

使用@ManyToOne注解来定义一个多对一关系。对于多对一关系而言,它一般需要一个@JoinColumn来帮助定义其对应的外键信息。因此根据上面的定义,多对一关系中的”多”方,总是可以被视为此段关系中的所有方(Owning Side),而响应的”一”方,在这种情况下可以被视为反转方(Inverse Side)。因此,在上面的例子中,Employee的表结构中就会有一个名为DEPT_ID的列作为引用Department记录的外键列。

三、集合映射

3.1一对多(One-to-Many)
@Entity
public class Department {
    @Id 
    private int id;

    private String name;
    @OneToMany(mappedBy="department")
    private Collection<Employee> employees;
    // ...
}

一对多这种关系一般是不会单独出现的,比如我们只想定义从Department指向Employee的一层关系。假设一个Department实例中引用了五个Employee实例,在程序中使用集合类型来描述这层关系很方便,也没有什么障碍。但是考虑一下换到数据库表结构中,应该如何描述这种结构呢?显然在Department对应的表中是没法描述的,毕竟一行是无法引用多个(并且数量还不定)其它表结构中的记录的。所以一对多一般伴随着多对一作为双向关系出现,而且由于@JoinColumn总是会出现在”多”方,因此我们需要在@OneToMany注解中使用mappedBy属性来声明这一点,毕竟此时的”一”方式反转方(Inverse Side),在反转方中就需要使用mappedBy属性,这是JPA规范中的一部分。

那么是不是一定要使用mappedBy来表示这个一对多关系是一个双向关系中的一部分呢?也不一定。
如果你只想定义一个单向的一对多关系,那么可以不使用mappedBy属性,此时JPA会推断你想使用联接表(Join Table)来完成关系的定义,虽然这个联接表有其默认的命名规范,但是显式地声明它通常是更明智的方案,比如下面这样:

@Entity
public class Department {
    @Id 
    private int id;

    private String name;

    @OneToMany
    @JoinTable(name="DEPT_EMP",
        joinColumns=@JoinColumn(name="DEPT_ID"),
        inverseJoinColumns=@JoinColumn(name="EMP_ID"))
    private Collection<Employee> employees;
    // ...
}

Department和Employee的一对多关系会通过表DEPT_EMP进行管理。@JoinTable注解的joinColumns属性声明了联接表中引用此关系中的所有方的外键列名为DEPT_ID,inverseJoinColumns属性声明了联接表中引用此关系中的反转方的外键名为EMP_ID。

3.1多对多(Many-to-Many)
@Entity
public class Employee {
    @Id 
    private int id;

    private String name;

    @ManyToMany
    @JoinTable(name="EMP_PROJ",
        joinColumns=@JoinColumn(name="EMP_ID"),
        inverseJoinColumns=@JoinColumn(name="PROJ_ID"))
    private Collection<Project> projects;
    // ...
}

@Entity
public class Project {
    @Id 
    private int id;

    private String name;

    @ManyToMany(mappedBy="projects")
    private Collection<Employee> employees;
    // ...
}

很显然,这是一个双向多对多的例子。之所以为双向,主要还是取决于在Project类中声明的@ManyToMany(mappedBy=“projects”),如果缺少了这个mappedBy属性的相关定义,那么JPA实现在分析这段代码中出现的两个@ManyToMany的时候,会认为这是两个单向而独立的多对多关系。因此会尝试读取两个联接表:一个是我们显式声明的表EMP_PROJ,另一个则是根据实体类的名字生成的默认联接表Project_Employee。显然生成两个独立的联接表在通常情况下并非我们的目的,所以在使用多对多关系的时候,一定要注意mappedBy属性的声明。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值