上一篇文章讲了spring data JPA实体类中相关注解的使用方法,这次讲下关联关系注解的使用,关联关系注解主要用来做联表的增删改查操作。
项目地址
spring data JPA 中关联关系的注解是与表间的关系相对应的,具体包括: OneToOne (一对一关系),OneToMany(一对多),ManyToOne(多对一),ManyToMany(多对多)。常用的级联操作包括:级联级联新建、级联删除、级联刷新、级联更新。
OneToOne,就是一对一映射,现实生活中比较常见的例子就是一个人有一个身份证,一个丈夫只能有一个老婆
User实体类,对应user表
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
private String telnum;
@Temporal(TemporalType.TIMESTAMP)
private Date signDate;
@OneToOne
private Wallet wallet;
}
Wallet实体类,对应wallet表
@Entity
public class Wallet {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal money;
public Wallet(){}
}
User实体类是关系维护方,其对应的User表会生成外键和关联字段,Wallet是关系被维护方,其对应数据库表字段的id主键值作为User表外键关联值。 Spring data JPA关系维护方负责外键维护。
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
private String telnum;
@Temporal(TemporalType.TIMESTAMP)
private Date signDate;
@OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE},
fetchFetchType.EAGER) //级联保存、更新、删除
@JoinColumn(name = "user_wallet_id" )
private Wallet wallet;
}
上述定义
cascade
属性表示级联操作策略:
CascadeType.PERSIST
:级联新建CascadeType.REMOVE
:级联删除CascadeType.REFRESH
: 级联刷新,即重新同步到数据库中状态,会覆盖掉已经修改但是还没保存的实体类属性CascadeType.MERGE
: 级联更新CascadeType.ALL
:表示选择全部四项fetch
属性表示实体的加载方式,有LAZY
和EAGER
两种取值,默认值为EAGER
optional
属性表示关联的实体是否能够存在null
值,默认为true
,表示可以存在null
值
@JoinColumn(name = "user_wallet_id" )
指定外键字段名字为user_wallet_id
@JoinColumn(name="user_wallet_id", referencedColumnName="money")
中的referencedColumnName指定外键对应到外联表的取值字段为money,默认是从id取值。
Wallet实体类也可以使用OneToOne注解,定义关联关系
@Entity
public class Wallet {
@Id
@GeneratedValue
private Long id;
private BigDecimal balance;
@OneToOne(mappedBy = "wallet")
private User user;
public Wallet() {
}
//... setter,getter省略
}
注意:只有关系维护方才能操作两者的关系,被维护方即使设置了维护方属性进行存储也不会更新外键关联;
1.mappedBy不饿能与@JoinColumn或者JoinTable同时使用。
2.mappedBy的值是指另一方的实体里面属性的字段,而不是数据库字段,也不是实体对象的名字。即另一方配置了@JoinColumn或者@JoinTable注解的属性的字段名称
@OneToMany与@ManyToOne可以相对存在,也可只存在一方。
如班级和学生的关系,从班级角度来说,一个班有多个学生,是一对多的关系;反过来从学生角度,多个学生属于同一个班,是多对一的关系;
JPA规定多的一端为关系维护端。通过@ManyToOne 和 @OneToMany注解设置实体一对多和多对一关系。
User中包含多个Address
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
private String telnum;
@Temporal(TemporalType.TIMESTAMP)
private Date signDate;
@OneToMany(mappedBy = "client",cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, fetch = FetchType.EAGER)
private List<Address> addresses;
public User(){}
}
User为关系被维护端,没有权限维护关系字段,若要维护需要显示的设置好关系
一个Address对应一个User:
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String address;
private String areaCode;
private String telnum;
private String recevier;
@ManyToOne
@JoinColumn(name = "clientid" )
private User client;
public Address(){}
}
Address为关系维护端,可以维护关系字段
@OneToMany,@ManyToOne的属性:
cascade
属性表示级联操作策略:
CascadeType.PERSIST
:级联新建CascadeType.REMOVE
:级联删除CascadeType.REFRESH
: 级联刷新,即重新同步到数据库中状态,会覆盖掉已经修改但是还没保存的实体类属性CascadeType.MERGE
: 级联更新CascadeType.ALL
:表示选择全部四项fetch
属性表示实体的加载方式,有LAZY
和EAGER
两种取值,默认值为EAGER
optional
属性表示关联的实体是否能够存在null
值,默认为true
,表示可以存在null
值
@JoinColumn(name = "user_wallet_id" )
指定外键字段名字为user_wallet_id
@JoinColumn(name="user_wallet_id", referencedColumnName="money")
中的referencedColumnName指定外键对应到外联表的取值字段为money,默认是从id取值。
慎用CascadeType.ALL与CascadeType.REMOVE,避免数据丢失
默认情况下User中的Address属性的 fetch=FetchType.LAZY
, 即延迟加载,获取User对象的时候,其中的Address不会从数据中获取到值,只有显示的调用Address的getter方法后,才会从数据库中获取Address的数据。
凡是被@xxToMany注解(比如@OneToMany、@ManyToMany)标识的属性,默认情况下 fetch=FetchType.LAZY
@xxToOne注解(例如:@OneToOne、@ManyToOne)标识的属性,默认情况下 fetch=FetchType.EAGER
运行程序后,生成表如下:
新建商品实体类Good和标签实体类Tag,二者为多对多关系,一个商品对应多个标签,同样一个标签可有多个商品
实体与实体之间的多对多的关系通过@ManyToMany
注解去设置
Good实体类,对应数据库good表
@Entity
@Data
public class Good {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
@ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}, fetch = FetchType.EAGER)
@JoinTable(
name = "t_tag_good",
joinColumns = @JoinColumn(name = "good_id",referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name="tag_id")
)
@JsonIgnore
private List<Tag>tagsList = new ArrayList<>();
public Good(){}
Tag实体类,对应数据tag表
@Entity
@Data
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String tag;
@ManyToMany(mappedBy = "tagsList")
private List<Good> goodsList = new ArrayList<>();
public Tag(){}
虽然多对多关系中两个实体类都使用@ManyToMany
标注关系,但需要通过mappedBy指定关系被维护方,则另一方就是关系维护方。关系维护方的指定根据实际业务需求来确定。
多对多关系指定后,JPA会根据实体及其关系自动生成实体类对应的表和两个实体之间的关系表。中间关系表:
表名称:t_tag_good
字段 | 类型 | 说明 |
---|---|---|
good_id | bigint | good表的外联字段 |
tag_id | bigint | tag表的外联字段 |
@Entity
@Data
public class Good {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
@ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}, fetch = FetchType.EAGER)
@JoinTable(
name = "t_tag_good",
joinColumns = @JoinColumn(name = "good_id",referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name="tag_id")
)
@JsonIgnore
private List<Tag>tagsList = new ArrayList<>();
public Good(){}
@JoinTable 注解用来控制,多对多的关系中生成的中间关系表。可以自定义一些设置:
name:代表中间关系表的名字
joinColumns:关系维护端的设置 该注解是用在关系维护端的注解,如果用在关系被维护端,则会报错
简单来说,joinColumns是来定义外键的
inverseJoinColumns:关系被维护端设置
运行程序后,生成表如下: