为什么在Hibernate中需要事务才能进行只读操作?


107

为什么在Hibernate中需要事务才能进行只读操作?

以下事务是否在数据库中设置了锁定?

从数据库获取示例代码:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

我可以session.close() 代替使用tx.commit()吗?


9
数据库本身需要事务。:您可以了解这里自动提交模式community.jboss.org/wiki/...
Bhesh古隆

@BheshGurung我想我们只需要对写操作进行事务处理
user93796

4
您是否已阅读链接中的“揭穿自动提交神话”部分?
Bhesh Gurung

Answers:


131

您实际上可能有理由将事务标记为只读。

  1. 阅读交易看起来确实很奇怪,在这种情况下,人们通常不会标记交易方法。但是无论如何,JDBC都会创建事务,只是autocommit=true如果没有明确设置其他选项,它将起作用。
  2. 但是,不能保证您的方法不会写入数据库。如果将method标记为@Transactional(readonly=true),Spring会将JDBC事务设置为只读模式,因此您将决定在该事务范围内是否实际上可以写入DB。如果您的体系结构繁琐,并且某些团队成员可能选择将修改查询放在不希望出现的地方,则此标志会将您指向有问题的地方。
  3. DB也可以优化只读事务,但这当然是特定于DB的。例如,MySQL从5.6.4版本开始仅在InnoDB中添加了对此的支持。
  4. 如果您不是直接使用JDBC,而是使用ORM,则可能会出现问题。例如,Hibernate社区说在事务外工作可能会导致不可预知的行为。这是因为Hibernate将打开事务,但不会自行关闭它,因此连接将在未提交事务的情况下返回到连接池。那会发生什么呢?JDBC保持沉默,因此这是特定于实现的(MySQL回滚事务,Oracle afair提交)。这也可以在连接池级别上配置(例如C3P0为您提供了这样的选项,默认情况下为回滚)。
  5. 关于Hibernate的另一件事,在只读事务的情况下,Spring将FlushMode设置为MANUAL,这会导致其他优化,例如不需要脏检查。
  6. 您可能想要覆盖或显式设置事务隔离级别。这也会影响读取事务,因为您要读取或不希望读取未提交的更改,暴露于幻像读取等。

综上所述-您可以双向使用,但是您需要了解后果。


感谢您的答复。我使用的是hibernate(本机为hibernate,不是jpa)。但是hibernate强迫我在执行任何db操作之前先启动事务。 ,如果是,怎么办?
user93796 2012年

只读标志实际上是为连接设置的,而不是为事务本身设置的,因此不能像这样通过Hibernate访问它。您需要使用Spring Transaction支持并使用注释或基于XML的事务配置。通过这样做,Spring可以拥有连接和事务管理的所有权,而不是Hibernate。
Stanislav Bashkyrtsev 2012年

1
很好解释!马克·理查兹(Mark Richards)的另一篇文章很好地解释了事务性注释中的只读标志的陷阱-ibm.com/developerworks/java/library/j-ts1/index.html
Mahesh

48

所有数据库语句都在物理事务的上下文中执行,即使我们没有显式声明事务边界(BEGIN / COMMIT / ROLLBACK)也是如此。

如果未明确声明事务边界,则每个语句将必须在单独的事务(autocommit模式)中执行。除非您的环境无法处理每线程连接绑定,否则这甚至可能导致每个语句打开和关闭一个连接。

将服务声明为as @Transactional将在整个事务期间为您提供一个连接,并且所有语句将使用该单个隔离连接。这比首先不使用显式事务更好。

在大型应用程序上,您可能有许多并发请求,降低数据库连接获取请求率肯定会提高整体应用程序性能。

JPA不会对读取操作强制执行事务。只有在您忘记启动事务上下文的情况下,写操作才会引发事务必需的异常。但是,即使对于只读事务,也最好声明事务边界(在Spring中@Transactional,您可以标记只读事务,这具有很大的性能优势)。


1
“ ...那么每个语句将必须在单独的事务中执行”。容器不自动执行批处理吗?
阿鲁什

批处理用于修改,而不是用于读取数据。即使如此,使用JPA和Hibernate时默认情况下也不会启用JDBC批处理
弗拉德·米哈尔恰

1
谢谢弗拉德。我读了你的一些文章。它们真的很有用。
阿鲁什

这个stackoverflow.com/q/34797480/179850似乎与您肯定只读事务具有巨大性能优势的说法有些矛盾。至少,在休眠状态下似乎并非如此。
nimai

不,不是。那是关于设置只读的,而我的是关于避免自动提交的。
Vlad Mihalcea

17

事务确实在数据库上加了锁-好的数据库引擎以一种明智的方式处理并发锁-并且对于只读使用很有用,以确保没有其他事务添加任何会使您的视图不一致的数据。您总是想要一个事务(尽管有时调整隔离级别是合理的,但是最好不要这样做)。如果您在事务期间从未写入数据库,则提交和回滚事务都是一样的(而且非常便宜)。

现在,如果您很幸运,并且对数据库的查询使得ORM始终将它们映射到单个SQL查询,则可以依靠数据库的内置自动提交行为而无需显式事务就可以逃脱,但是ORM是相对复杂的系统因此,依靠这种行为是绝对不安全的,除非您去做更多的工作来检查实现的实际作用。在其中编写明确的事务边界要容易得多(特别是如果您可以使用AOP或类似的ORM驱动的技术来做到这一点;我想从Java 7开始,也可以使用try-with-resources)。


14

无论您是否只读取都没关系-数据库仍必须跟踪结果集,因为其他数据库客户端可能希望写入会更改结果集的数据。

我已经看到有错误的程序可以杀死庞大的数据库系统,因为它们只读取数据,但是从不提交,从而迫使事务日志增长,因为即使客户端不执行任何操作,DB也无法在COMMIT或ROLLBACK之前释放事务数据。用了几个小时。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.