目录
现象
最近解决了一个困惑几天的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状态的数据对象,可以复制对象,使用副本
标签:状态,事务,EntityManager,JPA,数据库,实体,查询,对象,自动更新 From: https://www.cnblogs.com/zmj-pr/p/16033530.html