不调用更新方法的事务性保存

72

我有一个使用@Transactional注解的方法。我从我的Oracle数据库中检索一个对象,更改一个字段,然后从方法返回。我忘记保存对象,但发现数据库仍然得到了更新。

applicationContext

<tx:annotation-driven />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

我的方法

@Transactional
public void myMethod(long id) {
    MyObject myObj = dao.getMstAttributeById(id);
    myObj.setName("new name");
    //dao.update(myObj);
}

我的问题是为什么 MyObject 被持久化到数据库中?


因为框架检测到对象的值发生了变化并已经为您保存。 - Dmitry B.
1
不调用persist方法,我该怎么防止它?我甚至抛出了异常,但它仍然被持久化了? - John LaFleur
不,如果你抛出异常,它不会持久化。而且你可以控制它,你只需要阅读文档并学习如何使用框架。 - Dmitry B.
1
约翰,你是如何解决这个问题的?我也遇到了同样的问题。 - Senthil Muthiah
5个回答

88
因为Hibernate会自动检测对持久化实体所做的更改并相应地更新数据库。这种行为在Hibernate参考手册的第11章中有所记录。相关部分如下所示:
Hibernate定义并支持以下对象状态:
  • 瞬时态 - 如果一个对象刚刚使用new操作符被实例化,并且没有与Hibernate Session相关联,则该对象是瞬时的。它在数据库中没有持久化表示,也没有分配标识符值。如果应用程序不再持有引用,则瞬时实例将被垃圾回收器销毁。使用Hibernate Session使对象持久化(并让Hibernate处理需要执行的SQL语句以进行此转换)。
  • 持久态 - 持久实例在数据库中具有表示和标识符值。它可能刚刚被保存或加载,但它肯定在Session的范围内。Hibernate将检测到对处于持久状态的对象所做的任何更改,并在工作单元完成时将状态与数据库同步。开发人员不会手动执行UPDATE语句或DELETE语句来使对象变为瞬时态。
  • 游离态 - 游离实例是一种已经持久化的对象,但其Session已关闭。当然,对象的引用仍然有效,并且在此状态下甚至可以修改游离实例。稍后可以重新附加游离实例到新的Session中,使其(以及所有修改)再次持久化。此功能为需要用户思考时间的长时间运行的工作单元提供了编程模型。我们称它们为应用程序事务,即从用户角度看来的工作单元。

2
这很有道理,但是我该如何分离我的实体。我正在使用JPA查询来获取实体,并且我需要操作特定只读操作的子记录,以便响应与请求匹配的JSON。但是我不想删除或更新不适用于该请求的子记录;只需从用户处隐藏即可。 - wheeleruniverse
1
@meriton,只是好奇这是否是正确的编码实践? - NameNotFoundException
@meriton 无可挑剔的答案! - Gaurav

18
如果你正在使用JPA,那么规范规定,如果你的实体处于“托管状态”(这是在活动事务中从DAO获取数据所做的操作),则对其进行的所有更改将在“事务提交”期间反映在数据库中。
换句话说,“是否调用更新操作”并不重要,因为事务提交会将更改刷新到数据库中。

谢谢,我已经搜索了很久,但是一直认为自己在某个配置上出了问题。哎呀,这很有道理,而且很简单。 - John LaFleur
@Piotr,只是好奇这是否是正确的编码实践? - NameNotFoundException

8

我使用@Transactional(readOnly = true)解决了这个问题。


@Transactional(propagation = Propagation.REQUIRED, readOnly = true) 对我有用。 - pserimer

5
我发现防止数据库自动更新是一个两步的过程。
第一步:
getSession().setFlushMode(FlushMode.MANUAL) // [FlushMode.NEVER is depracated in 4.x]

第二步:
getSession().clear(); //This will actually discard all changes

3

对于JPA而言,调用entityManager.detach(entity)可以避免自动刷新。但需要注意的是,分离后的实体将失去ORM魔力,例如延迟获取、级联更新等功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接