首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

10.3  非事务数据访问

许多数据库管理员在每个新的数据库连接上默认启用所谓的自动提交模式。自动提交模式对于SQL的特殊执行很有用。

想象你用SQL控制台连接到数据库,并且运行几个查询,甚至也可能更新和删除行。这种交互式的数据访问是特殊的;大多数时候,你并不具备考虑工作单元的计划或者一系列语句。数据库连接中默认的自动提交模式对于这种数据访问是至臻完美的——毕竟,你并不想给所编写和执行的每个SQL语句都输入begin a transaction和end a transaction。在自动提交模式中,一个(短)数据库事务给对于发送到数据库的每个SQL语句进行启动和终止。你正在有效地进行非事务地工作,因为对于使用SQL控制台的会话来说,并没有原子性或者隔离性保证。(唯一的保证是单个SQL语句具有原子性。)

按照定义,应用程序始终执行计划好的一系列语句。因而你始终创建事务范围,把语句组合到原子单元中去,这似乎很合理。因此,自动提交模式在应用程序中没有用武之地。

10.3.1  揭开自动提交的神秘面纱

许多开发人员经常出于一些含糊的、不确切的理由,仍然喜欢使用自动提交模式。如果你想(或者必须)了解其中的原因的话,就让我们在介绍如何非事务地访问数据之前先揭开这其中的一部分理由吧:

l    许多应用程序的开发人员认为,他们可以在事务外部与数据库对话。这显然是不可能的;没有任何SQL语句可以被发送到数据库事务外部的数据库。术语非事务的数据访问意味着没有显式的事务范围,没有系统事务,并且数据访问的行为处于自动提交模式。这并不意味着没有涉及实质性的数据库事务。

l    如果你的目标是利用自动提交模式改进应用程序的性能,就应该重新考虑许多小事务的含义。给每一个SQL语句启动和终止数据库事务涉及了重大的过载,它可能降低应用程序的性能。

l    如果你的目标是利用自动提交模式改进应用程序的可伸缩性,就重新考虑:对于每一个SQL语句来说,用更长运行的数据库事务代替许多小事务,可以保持更长时间的数据库锁,也可能不进行伸缩。但是,由于Hibernate持久化上下文和DML的迟写,数据库中所有写锁已经持续了很短的时间。根据你启用的隔离性级别,读锁的成本可能可以忽略。或者,你可以使用一个不需要读锁(Oracle、PostgreSQL、Informix、Firebird)的包含多版本并发的DBMS,因为默认情况下读取操作永远不会被阻塞。

l    因为你正在进行非事务地工作,不仅真的放弃了一组SQL语句的任何事务原子性,而且如果数据被同时修改,还减弱了隔离性保证。基于读锁不可能有自动提交模式的可重复读取。(持久化上下文高速缓存在此处自然很有帮助。)

在应用程序中引入非事务的数据访问时,要考虑更多的问题。我们已经注意到,引入一种新的事务类型,称作只读事务(read-only transaction),可以明显使应用程序的任何未来修改变得复杂起来。如果你引入非事务的操作也一样。

然后你在应用程序中就有3种不同的数据访问了:普通事务、只读事务以及现在又有不包含保证的非事务。想象你必须引入一项操作,把数据写到假定只读取数据的工作单元中。想象你必须把非事务的操作识别为事务操作。

我们的建议是不在应用程序中使用自动提交模式,并且只在有明显的性能好处,或者很可能要在未来改变代码时,才应用只读的事务。始终更喜欢一般的ACID事务,把数据访问操作组合起来,无论你是读还是写数据。

话虽这么说,Hibernate和Java Persistence还是允许非事务的数据访问。事实上,如果你想实现原子的长运行对话,EJB 3.0规范强制你非事务地访问数据。第11章将探讨这个主题。现在要深入到简单的Hibernate应用程序中自动提交模式所带来的结果。(注意,不要管我们负面的评论,自动提交模式还是有一些好的使用案例。依据我们的经验,自动提交经常由于错误的原因而被启用,我们本想先清除这些错误的观念。)

10.3.2  使用Hibernate非事务地工作

看看下列代码,它访问没有事务范围的数据库:

默认情况下,在包含JDBC配置的Java SE环境中,如果执行这个片段,将发生:

(1) 打开一个新Session。此时它没有获得数据库连接。

(2) 调用get()触发一个SQL SELECT。现在Session从连接池获得了JDBC Connection。Hibernate立即默认在这个连接中用setAutoCommit(false)关闭自动提交的模式。这样有效地启动了一个JDBC事务!

(3) SELECT在这个JDBC事务内部执行。Session关闭,且连接被返回到池,并由Hibernate释放——Hibernate在JDBC Connection中调用close()。对于没有被提交的事务,发生了什么事?

这个问题的答案是:不一定!JDBC规范没有提到在一个连接上调用close()时任何未处理的事务的内容。会发生什么取决于供应商如何实现规范。例如,利用Oracle JDBC驱动器,调用close()提交了事务!大多数其他的JDBC供应商走健全的路线,并在JDBC Connection对象被关闭,且资源返回到池中时,回滚任何未处理的事务。

显然,这对于你已经执行的SELECT来说不成问题,但是看看这个变形:

这段代码促成了INSERT语句,该语句在一个从不被提交或者回滚的事务内部执行。在Oracle中,这段代码永久地插入数据;在其他数据库中,则可能不是。(这种情形稍微更复杂一些:INSERT只有在标识符生成器需要它时才执行。例如,标识符值可以从一个没有INSERT的sequence中获得。然后,持久化实体被列队等待,直到清除时插入——这在这段代码中永远不会发生。identity策略对于要生成的值,需要一个立即的INSERT。)

我们甚至还没有接触到自动提交模式,但是已经强调了一个问题,它可能出现在你试图不设置显式的事务范围进行工作时。假设你仍然认为不用事务划分进行工作是个好主意,并且想要一般的自动提交行为。首先,必须在Hibernate配置中告诉Hibernate允许自动提交的JDBC连接:

利用这个设置,当从连接池获得JDBC连接时,Hibernate不再关闭自动提交——如果连接还没有处于该模式,就启用自动提交。现在前一个代码示例可以预期地生效了,并且JDBC驱动器把被发送到数据库的每一个SQL语句(包含我们前面列出过的那些含义)都包在一个简短的事务中。

在Hibernate中,你在什么情况下要启用自动提交模式,以便可以使用不用手工启动和终止事务的Session呢?受益于自动提交模式的系统是那些要求按需(延迟)加载数据的系统,在一个特定的Session和持久化上下文中,但是在这些系统中很难把所有可能触发按需数据获取的代码都包在事务范围中。这通常不是遵循我们在第16章中讨论的设计模式的Web应用程序中的案例。另一方面,通过Hibernate访问数据库层的桌面应用程序经常要求按需加载,没有显式的事务范围。例如,如果你在Java Swing树视图上双击一个节点,这个节点的所有子节点都必须从数据库中被加载。你最好必须把这个事件手工包在一个事务中;自动提交模式是一种更为方便的解决方案。(注意我们不是在建议按需打开和关闭Session!)

10.3.3  使用JTA的可选事务

前面着重讨论了自动提交模式,以及利用非托管的JDBC连接(在这里,Hibernate管理连接池)的应用程序中的非事务数据访问。现在想象你想要在Java EE环境中使用Hibernate,包含JTA,可能也有CMT。connection.autocommit配置选项在这个环境中不起作用。是否使用自动提交,取决于你的事务程序集。

想象你有一个EJB会话bean,它把特定的方法标记为非事务:

findItemById()方法生成一个返回Item实例的立即的SQL SELECT。因为该方法被标记为不支持事务上下文,所以没有为这项操作启动任何事务,并且任何现有的事务上下文在这个方法的持续时间内被暂停。SELECT在自动提交模式中被有效地执行。(自动提交的JDBC连接被内部分配用来服务于这个Session。)

最后,你需要知道当没有事务在处理时,Session的默认FlushMode改变了。默认的行为FlushMode.AUTO,导致在每个HQL、SQL或者Criteria查询之前同步。当然,这样不好,因为除了为查询执行SELECT外,DML UPDATE、INSERT和DELETE操作也执行。因为你正在自动提交模式下工作,这些修改是永久的。当你在事务范围外部使用Session时,Hibernate通过禁用自动清除防止了这一点。然后你不得不期待查询可以返回失效数据或者与当前Session中数据状态冲突的数据——当选择FlushMode.MANUAL时,必须有效地处理相同的问题。

我们将在第11章的对话讨论中,回到非事务数据访问的话题。应该把自动提交行为当作一项特性(你可能在利用Java Persistence或者EJB的对话中使用它),并且把所有数据访问事件都包在编程式的事务范围中会很困难(例如在一个桌面应用程序中)。在大多数的其他案例中,自动提交导致系统难以维护,并且没有任何性能或者可伸缩性方面的好处。(依我们之见,RDBMS供应商不应该默认启用自动提交。必要时,SQL查询控制台和工具应该在连接上启用自动提交模式。)

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论