为啥@Transactional 会自动保存到数据库
Posted
技术标签:
【中文标题】为啥@Transactional 会自动保存到数据库【英文标题】:Why does @Transactional save automatically to database为什么@Transactional 会自动保存到数据库 【发布时间】:2014-02-28 10:07:51 【问题描述】:我有一个用@Transactional 注释的方法。我从我的数据库中检索一个对象,更改一个字段,然后从该方法返回。在不保存我的对象的情况下,数据库无论如何都会更新,这很奇怪。
您能告诉我如何避免这种行为吗?
【问题讨论】:
如果方法不是事务性会发生什么? 我实际上需要@Transactional 来避免 LazyInitializationException 【参考方案1】:这是正常的 JPA 行为。
一旦通过find()
左右检索到一个对象,该对象就被认为是附加的,或者属于一个持久化上下文。一旦退出该方法,@Transactional
就会触发 Spring 事务管理方面,它将每个“脏”对象刷新到数据库并提交事务。由于您的对象已经在持久化上下文和事务的上下文中更改,因此即使不需要显式调用 save 方法,更改也会保存到数据库中。
如果你想改变你的对象而不影响数据库,你有两个选择:
-
从
@Transactional
注解的方法返回后更新字段
如果使用该方法,请在实体管理器上调用detach
【讨论】:
感谢您的回答,我现在明白它是如何工作的;)【参考方案2】:这种行为是事务性的主要目的之一。
在事务方法即将返回之前,事务提交,这意味着对托管实体的所有更改都会刷新到数据库中。
如果发生错误,事务将回滚,这意味着不会向数据库提交任何更改。
当您尝试访问延迟加载的属性(可能是来自实体的集合)时,您可能会收到LazyInitializationException
。当您从数据库中获取实体时,延迟加载的属性不会被实例化。
如果您在事务中访问延迟加载的属性,持久性提供程序将创建一个查询,实例化结果并将其附加到“父”实体。
编辑:如果您希望加载惰性属性并能够更改实体而不将更改持久化到数据库,您可以使用惰性属性的 fetch join 获取实体。
em.createQuery("SELECT e FROM MyEntity e JOIN FETCH e.lazyProp");
然后继续使用@orid 描述的方法之一。
如果您不使用 fetch 连接,则需要在事务中访问延迟加载的属性:
myEntity.getLazyProp().size();
注意对size()
的调用。调用 getter 是不够的,因为您将获得一个代理。您需要执行需要来自属性的实际数据的操作。
【讨论】:
说得很好。您对该主题的参考资料有什么建议吗? 谢谢,凯文。我可以推荐的唯一资源是Pro JPA 2 Book,因为这是我用于学习 JPA 的资源。第 6 章,EntityManager / Transaction Management 可能是最好的查阅地点。在 Hibernate 和 Eclipselink 文档中也可以找到相同的知识。 感谢 kosjta 的回答非常有用,我的想法是不调用更新数据就不会提交到数据库。再次感谢:) 不客气 :) 是的,容器管理的 JPA 在幕后做了一些不太明显的技巧。我真的可以推荐我上面链接的 Pro JPA 2 书。 你好kosjta,我回来只是为了告诉你一件事。即使我使用 readOnly=true 来避免 LazyInitializationException 并且同时不更新数据库,实际上数据库无论如何都会更新!有什么想法吗?以上是关于为啥@Transactional 会自动保存到数据库的主要内容,如果未能解决你的问题,请参考以下文章
尽管存在`@Transactional`,为啥这些数据库修改不回滚?