ORMSpring技术笔录资料整理随笔日志

Spring Jpa(Hibernate)数据对象中的级联.

FavoriteLoadingAdd to favorites
引言:当我们使用Hibernate类的ORM框架时,一般都会涉及关联对象,其中一对一关系是比较简单的,也是不容易发生错误的,但也不可避免会涉及到一对多和多对多的配置。本篇并不是介绍相关的配置,主要记录一下在使用JPA时,需要注意的问题。下面通过简单的场景来阐述配置缘由。
 
相关知识

CascadeType.REFRESH:级联刷新,也就是说,当你刚开始获取到了这条记录,那么在你处理业务过程中,这条记录被另一个业务程序修改了(数据库这条记录被修改了),那么你获取的这条数据就不是最新的数据,那你就要调用实体管理器里面的refresh方法来刷新实体,所谓刷新,大家一定要记住方向,它是获取数据,相当于执行select语句的(但不能用select,select方法返回的是EntityManager缓存中的数据,不是数据库里面最新的数据),也就是重新获取数据。
CascadeType.PERSIST:级联持久化,也就是级联保存。比如保存order的时候也保存orderItem,如果在数据库里已经存在与需要保存的orderItem相同的id记录,则级联保存出错。
CascadeType.MERGE: 级联更新,也可以叫级联合并;比如当对象Order处于游离状态时,对对象Order里面的属性作修改,也修改了Order里面的orderItems。
CascadeType.REMOVE:比如当对Order进行删除操作的时候,也会对orderItems对象进行级联删除操作。
如果在应用中,要同时使用这四项的话,可以改成cascade = CascadeType.ALL

 
应用说明
 
这四种级联操作,并不是对所有的操作都起作用,只有当我们调用实体管理器的persist方法的时候,CascadeType.PERSIST才会起作用;同样道理,只有当我们调用实体管理器的merge方法的时候,CascadeType.MERGE才会起作用,其他方法不起作用。同样道理,只有当我们调用实体管理器的remove方法的时候,CascadeType.REMOVE才会起作用。
注意: Query query = em.createQuery(“delete from Person o where o.id=?1”);这种删除会不会起作用呢?是不会起作用的,因为配置里那四项都是针对实体管理器的对应的方法。
 
也有的情况是,在处理关联对象的时候,只是为了在中间表去生成关联记录,根本不需要去更新需要关联的对象。
 
对于技术上来说,使用级联更新和级联删除,能减少代码量和垃圾数据的遗留等问题,但对于一个系统或者互联网平台来说,数据就是价值,数据千万不能无缘无故的消失,无缘无故的变更,有时还难以找回。
所以建议:级联设置为CascadeType.PERSIST即可,不要设置为级联更新和级联删除。如果需要更新或删除,请在逻辑处理中,自行处理即可。
 
实践场景及问题
 
1. 级联保存

这里例举一个简单的场景:同时存在三个对象,一个主对象,两个是关联对象,且保存时,是在一个表单中提交时,可以使用。

 
2. 级联更新

如上所述,级联的对象会在主对象变更时,也随之变更。
如下的开发场景:
    User对象中有关联一个Teacher对象。在编写代码时,关联的时候,为了减少查询,直接获取到id,new出Teacher对象,设置id后,进行关联到User对象上,好处就是减少查询,同时代码简洁。(这里不讨论其他情况)

    现象:发现关联的Teacher对象,数据都被更新了,只剩下一个id。
    有的解决方法就是,将要关联的对象使用id查询出来,再关联。但其实更新User对象时,还是更新了Teacher对象的,只不过看不出变化而已。

常见报错信息:

[ERROR]-org.springframework.dao.InvalidDataAccessApiUsageException: Error occurred while storing entity 

[xxx.xxx.Teacher@2]. An entity copy 
[xxx.xxx.Teacher#4] was already assigned to a different entity 
……
 
报错解析:
 
表面意思:Teacher存储实体时出错,实体复制Teacher(此对象存在于hibernate缓存内部)已被分配给不同的实体。
注:Teacher对象,是当前存储涉及到的对象,这个对象不管是你查的,还是new的,还是session里的,反正都是新的对象。请注意不一定是操作的对象本身关联的,也很有可能是你要关联的对象中也关联了这个对象,同理,如果你要关联的对象中也设置了级联更新,那就没底限了

问题所在就是,对象在缓存中已经存在,现在操作的对象又要对其进行级联分配。所以报两个对象不同,不能复制使用。

解决办法:将关联对象的级联删除去除,即去除CascadeType.MERGE

级联更新的场景有哪些呢?

这里就有些类似级联保存了,一个表单同时保存几个对象,且一个是主对象,其他是关联对象,但也不建议使用级联操作。
 
3. 级联刷新

常用对象中的“级联对象”加上级联刷新。常用且不变的集合,不建议级联刷新,消耗比较大,最好使用相关的缓存策略。需要考虑性能问题,请根据实际情况使用即可。

一般配置为:cascade={CascadeType.REFRESH},fetch=FetchType.LAZY,不会影响系统的正常运作。

 
4. 级联删除

不推荐,因为级联删除,删除一个对象,会将关联的对象一并删除了。正常的系统是不会就将数据给物理删除掉的,正确的做法应该是根据逻辑来判断是否能删除,能删除的手动删除即可,不能删除给予相关的提醒即可。

如有问题,欢迎指出;如需转载,请标明出处,谢谢!