解决Jpa中实体数据自动变更问题 |
您所在的位置:网站首页 › JPA查询倒序第一条 › 解决Jpa中实体数据自动变更问题 |
目录
概述一、问题复现二、问题解析三、解决方案
概述
最近发现个问题,使用jpa对实体进行操作时,即使未调用保存或更新方法,对于实体的相关设值也会自动更新到数据库中 一、问题复现 @RunWith(SpringRunner.class) @SpringBootTest public class UserTest { @Autowired private UserInfoRepository userInfoRepository; @Test public void testUpdateEntity(){ UserInfo userInfo = userInfoRepository.get(2103031645422940000L); userInfo.setFullName("我不是管理员"); } }数据库原始数据如下: 为testUpdateEntity()添加事务注解,此时会自动将实体的相关变更持久化掉数据库中 @Test @Transactional //事务注解,添加之后jpa会自动把实体的相关变更持久化到数据库中 @Commit // 此注解是为了在单元测试中不让事务进行回滚,从而可以在数据库中看到数据的明显变化 public void testUpdateEntity(){ UserInfo userInfo = userInfoRepository.get(2103031645422940000L); userInfo.setFullName("我不是管理员"); }执行testUpdateEntity()方法之后,输出日志如下 数据库数据变更如下 二、问题解析经过查找资料得知,Hibernate实体对象的生命周期分为三种 瞬时状态(Transient) 通过new创建对象后,对象并没有立刻持久化,它并未与数据库中的数据有任何关联,此时Java对象的状态为瞬时状态。Session对于瞬时状态的Java对象是一无所知的,当对象不再被其他对象引用时,它的所有数据也就丢失了,对象将会被Java虚拟机按照垃圾回收机制处理。持久状态(Persistent) 当对象与Session关联,被Session管理时,它就处于持久状态。处于持久状态的对象拥有数据库标识(数据库中的主键值)。那么,对象是什么时候与Session发生关联的呢?有两种方法: 第一种,通过Sesison的查询接口,或者get()方法,或者load()方法从数据库中加载对象的时候,加载的对象是与数据库表中的一条记录关联的,此时对象与加载它的Session发生关联; 第二种,瞬时状态的对象,通过Session的save()方法或SaveOrUpdate()方法时,Java对象也与Session发生关联。 对于处于持久状态的对象,Session会持续跟踪和管理它们,如果对象的内部状态发生了任何变更,Hibernate会选择合适的时机(如事务提交时)将变更固化到数据库中。游离状态 处于持久状态的对象,脱离与其关联的Session的管理后,对象就处于游离状态。 处于游离状态的对象,Session无法保证对象所包含的数据与数据库中的记录一直,因为Hibernate已经无法感知对该对象的任何操作。 Session提供了两个方法(update()、merge()),将处于游离状态的对象,与一个新的Session发生关联。 此时,对象的状态就从游离状态重新转换为持久状态。根据Hibernate实体生命周期可知,testUpdateEntity()中的userInfo处于持久化状态,一旦事务提交时,Hibernate会自动将userInfo的相关变更持久化到数据库中。 三、解决方案既然知道Hibernate在事务提交时会自动将持久化状态的实体的变更持久化到数据库中,那么只要我们让实体脱离持久化状态即可避免这个问题。使用detach()使其脱离持久化状态,如下所示 @Test @Transactional @Commit public void testUpdateEntity(){ UserInfo userInfo = userInfoRepository.get(2103031645422940000L); userInfo.setFullName("我不是管理员111"); userInfoRepository.detach(userInfo); }执行结果如下: 数据库结果: 未对数据库数据造成影响,成功解决问题。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |