解决Jpa中实体数据自动变更问题

概述

最近发现个问题,使用jpa对实体进行操作时,即使未调用保存或更新方法,对于实体的相关设值也会自动更新到数据库中

一、问题复现

@RunWith(SpringRunner.class)
    
@SpringBootTest
public class UserTest {

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Test
    public void testUpdateEntity(){
        UserInfo userInfo = userInfoRepository.get(2103031645422940000L);
        userInfo.setFullName("我不是管理员");
    }
}

数据库原始数据如下:
image.png
为testUpdateEntity()添加事务注解,此时会自动将实体的相关变更持久化掉数据库中

    @Test
    @Transactional  //事务注解,添加之后jpa会自动把实体的相关变更持久化到数据库中
    @Commit // 此注解是为了在单元测试中不让事务进行回滚,从而可以在数据库中看到数据的明显变化
    public void testUpdateEntity(){
        UserInfo userInfo = userInfoRepository.get(2103031645422940000L);
        userInfo.setFullName("我不是管理员");
    }

执行testUpdateEntity()方法之后,输出日志如下
image.png
数据库数据变更如下
image.png

二、问题解析

经过查找资料得知,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);
    }

执行结果如下:
image.png
数据库结果:
image.png
未对数据库数据造成影响,成功解决问题。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值