JPA关联关系
1. 单向多对一(使用用户和订单为例)
-
使用注解:
-
@ManyToOne
- 是属性或方法级别的注解,用于定义源实体与目标实体是多对一的关系
- 属性:
targetEntity
:源实体关联的目标实体类型,默认是该成员属性对应的类型,可以缺省cascade
:定义源实体和关联的目标实体间的级联关系。默认没有级联操作。可选值有:CascadeType.PERSIST
:级联新建。若保存实体时,数据库中没有与该实体相关联的实体的那条记录,会在保存实体的同时保存与之相关联的实体CascadeType.REMOVE
:级联删除。删除当前实体时,与它有映射关系的实体也会跟着被删除CascadeType.REFRESH
:级联刷新。在更新前重新获取数据。- 使用场景:你先获取了数据,但是在保存时数据库的数据被修改了,这时候就需要重新获取一次数据(refresh),然后执行
update
操作
- 使用场景:你先获取了数据,但是在保存时数据库的数据被修改了,这时候就需要重新获取一次数据(refresh),然后执行
CascadeType.MERGE
:级联更新。当当前实体的数据改变,会相应地更新关联的实体的数据CascadeType.DETACH
:级联脱管/游离操作。删除实体因为有外键无法删除时,撤销所有相关的外键关联,然后删除@since Java Persistence 2.0
:表示从Java Persistence 2.0开始才有的这个可选值
CascadeType.ALL
:(包含以上五项)
fetch
:定义关联的目标实体的数据的加载方式。FetchType.LAZY
(懒加载) |FetchType.EAGER
(立即加载,默认)
optional
:源实体关联的目标实体是否允许为 null,默认为 true
-
@JoinColumn
-
该注解用于定义外键列,只能标注在实体类型的成员属性或方法上,如果没有声明,则使用该注解的默认值
-
属性值:与
@Column
注解相类似,但是没有了length、precision、scale
,特有属性:-
referencedColumnName:指定要关联哪一列为外键
-
foreignKey:没找到资料,这是源码中 Java doc 中的注释
-
The foreign key constraint specification for the join column. This is used only if table generation is in effect. Default is provider defined.
-
有道词典的翻译:
联接列的外键约束规范。只有在表生成生效时才使用此方法。默认是提供程序定义的。
-
其值的类型是:
@ForeignKey
,这个注解的属性- name:外键名
-
-
-
-
- foreignKeyDefinition:外键类型
- value:是否应用约束,取值有三个
- ConstraintMode.CONSTRAINT:应用约束。默认值
- ConstraintMode.NO_CONSTRAINT:不应用约束
- ConstraintMode.PROVIDER_DEFAULT:使用JPA厂商的默认行为
-
操作注意
- 保存:建议先保存一的一方,再保存多的一方,否则会多执行update语句,因为是一的一方在维护关联关系
- 查询:因为JPA默认是立即加载,所以在查询多的一方的时候,默认会使用左外连接
- 修改:默认是可以级联修改的
- 删除:多的一方可以直接删除,但是一的一方因为有外键约束,所以不能删除
2. 单向一对多(使用用户和订单为例)
- 使用注解
- @OneToMany
- 与 @ManyToOne类似,但是没有
optional
,特有属性:mappedBy
:用在双向关联中。如果关系是双向的,则需定义此参数。用于标注谁来维护外键orphanRemoval
:- 当源实体关联的目标实体被断开时,是否自动删除断开的实例(在数据库中表现为删除表示该实例的行记录),默认为 false。
- 可参考:orphanRemoval 与 CascadeType.REMOVE 的区别
- 与 @ManyToOne类似,但是没有
- @JoinColumn
- @OneToMany
- 操作注意:
- 保存:因为是一的一方在维护关联关系,所以在保存时,一定会多执行update语句
- 查询:
FetchType fetch() default LAZY
默认使用懒加载,发送多条sql,可修改为立即加载 - 修改:默认是可以级联修改的
- 删除:若删除一的一端,默认先将关联外键置空,然后删除记录。可以通过修改@OneToMany的
cascade
改变
3. 双向一对多(使用用户和订单为例)
-
使用注解:
- 一对多和多对一结合即可
-
操作注意:
-
保存:双向一对多双方都在维护外键关系,所以在保存时,如果先保存一的一方,会执行n次update,先保存多的一方,会执行2n次update
-
我们可以设置一的一方不维护关联关系,通过
@OneToMany
的mappedBy
来设置有哪个属性来维护@OneToMany(targetEntity = Order.class, mappedBy = "user") // 表示由Order类的user属性来维护外键,但是设置了mappedBy属性后,就不能设置@JoinColumn或@JoinTable了 private List<Order> orders = new ArrayList<>();
-
-
4. 双向一对一映射
- 使用注解
- @OneToOne
- @JoinColumn
- 操作注意
- 一对一关系需要在外键列添加唯一约束
- 建议设置一方放弃维护关联关系
- 保存:建议先保存没有外键的一方,再保存有外键的一方,这样不会多出update操作
- 查询:
- 若获取维护关联关系(有外键)的一方
- 默认会通过左外连接获取,即:默认是立即加载
- 若修改为懒加载,会发送两次sql语句,但是获取到的关联对象是代理对象
- 若获取不维护关联关系(没有外键)的一方
- 默认会通过左外连接获取,即:默认是立即加载
- 改为懒加载以后,会发现,他发了两次sql语句,同样把关联对象查出来了,所以,修改它的加载策略并没有什么意义
- 若获取维护关联关系(有外键)的一方
- 疑点?
- 为什么有外键的一方使用关联对象时,会多发送一条sql语句?
- 为什么没有外键的一方一定会查出关联对象?
5. 双向多对多映射
-
使用注解
- @ManyToMany:属性还是那些
- @JoinTable:与@Table类似,但是多了下列属性
- joinColumns:表示表中的外键列,该外键参照源实体(本对象)的主键。
- inverseJoinColumns:与joinColumns类似,但是该外键参照目标实体(关联对象)的主键。
- foreignKey:用于生成表时定义
joinColumns
参数的外键约束 - inverseForeignKey:用于生成表时定义
inverseJoinColumns
参数的外键约束
- 操作注意:
- 多对多必须有一方放弃维护外键关系
- 多对多的级联删除要慎重考虑
6. JPA二级缓存
-
因为实际上使用的是实现厂商的二级缓存,所以,
hibernate
怎么配置,在JPA配置文件的properties
里面还怎么配就好了<!-- 二级缓存相关,基于hibernate5 --> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/> <property name="hibernate.cache.use_query_cache" value="true"/>
-
在
class
标签下面添加<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
- 可选项:常用
ENABLE_SELECTIVE
- ALL:所有实体类都将被缓存
- NONE:所有实体类都不被缓存
- UNSPECIFIED:默认值。使用JPA产品的默认值
- ENABLE_SELECTIVE:标识
@Cacheable(true)
注解的实体类将被缓存 - DISABLE_SELECTIVE:缓存除了标识
@Cacheable(false)
注解的所有实体类
- 可选项:常用
-
记得导你使用的缓存产品的jar包
<!-- hibernate-ehcache --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>5.0.7.Final</version> </dependency>
本节代码点击此处