JPA在事务结束时自动更新查询数据

现象

最近解决了一个困惑几天的bug,数据库里的某一些记录莫名其妙的被刷新了,排查过代码跟应用日志,可以确定不是代码执行的更新。直到今天看到了一条日志,在事务提交时报错“Column 'user_name' cannot be null”,在出错的事务中,针对这一个表只会执行query不会执行update,而这个报错信息是只有insert或者update时才有可能出现,这就意味着事务中自动在这个表执行了的insert或者update语句。

产生的原因

JPA通过EntityManager对数据库实体类进行管理,而实体对象的状态有new/managed/removed/detached四种状态,如下图所示

  • 瞬时状态(new/transient):
    当一个实体对象最初被创建时,它的状态是New或Transient。在这种状态下,对象还没有与EntityManager关联,并且不存在数据库中。

  • 持久/托管状态(managed):
    当实体对象通过EntityManager的persist()方法持久化到数据库时,它就变成了Managed或Persistent。如果我们更改持久状态对象的值,则在提交事务时自动与数据库同步。由EntityManager从数据库检索的实体对象也处于Managed状态。

  • 游离状态(detached):
    状态Detached表示已经与EntityManager断开连接的实体对象。对象标识符应该在数据库表中,对象不与持久化上下文相关联。当关闭hibernate会话时,实例将失去与持久性管理器的关联。我们把这些物体称为分离的。表明它们的状态不再保证与数据库同步。游离的对象可以在以后的时间点重新附加到新会话,使其以及所有修改再次持久化

  • 移除状态(removed):
    通过在活动事务中使用entitymanager的remove(),也可以将持久状态实体对象标记为要删除。然后,实体对象将其状态从Managed更改为Removed,并在提交期间从数据库中物理删除。

当数据从数据库查出来时,该数据对象处于managed状态中,管理实体对象在活动事务中被修改,该更改由所属的EntityManager检测到,并在事务提交时将更新到数据库
一旦实体对象从数据库中检索出来,它可以在内存中修改将会反映到数据库当中,如下伪代码

  EntityManager em;
  Employee employee = em.find(Employee.class, 1);

  em.getTransaction().begin();
  employee.setNickname("Ram");
  em.getTransaction().commit();

当事务提交时,实体对象在数据库中nickName将会更新为Ram,而代码中并没有调用persist()。

解决方法

避免直接修改处于managed状态的数据对象,可以复制对象,使用副本

热门相关:我的治愈系游戏   B型水多火辣的小姨子   热线女孩   重生野性时代   变身蜘蛛侠