📄 transactions.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>第 10 章 事务和并行(Transactions And Concurrency)</title><link rel="stylesheet" href="../shared/css/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="up" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="previous" href="manipulatingdata.html" title="第 9 章 操作持久化数据(Manipulating Persistent Data)"><link rel="next" href="queryhql.html" title="第 11 章 Hibernate查询语言(Query Language), 即HQL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">第 10 章 事务和并行(Transactions And Concurrency)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="manipulatingdata.html">上一页</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="queryhql.html">下一页</a></td></tr></table><hr></div><div class="chapter" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title"><a name="transactions"></a>第 10 章 事务和并行(Transactions And Concurrency)</h2></div></div><div></div></div><p> Hibernate本身并不是数据库,它只是一个轻量级的对象-关系数据库映射(object-relational)工具。它的事务交由底层的数据库连接管理,如果数据库连接有JTA的支持,那么在<tt class="literal">Session</tt>中进行的操作将是整个原子性JTA事务的一部分。Hibernate可以看作是添加了面向对象语义的JDBC瘦适配器(thin adapter)。 </p><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="ttransactions-basics"></a>10.1. 配置,会话和工厂(Configurations, Sessions and Factories)</h2></div></div><div></div></div><p> <tt class="literal">SessionFactory</tt>的创建需要耗费大量资源,它是线程安全(threadsafe)的对象,在应用中它被所有线程共享。而<tt class="literal">Session</tt>的创建耗费资源很少,它不是线程安全的对象,对于一个简单商业过程(business process),它应该只被使用一次,然后被丢弃。举例来说,当Hibernate在基于servlet的应用中,servlet能够以下面的方式得到<tt class="literal">SessionFactory</tt>。 </p><pre class="programlisting">SessionFactory sf = (SessionFactory)getServletContext().getAttribute("my.session.factory");</pre><p> 每次调用SessionFactory的service方法能够生成一个新的<tt class="literal">Session</tt>对象,然后调用Session的<tt class="literal">flush()</tt>,调用<tt class="literal">commit()</tt>提交它的连接,调用<tt class="literal">close()</tt>关闭它,最终丢弃它。(<tt class="literal">SessionFactory</tt>可能被保存在JNDI或者一个静态的<span class="emphasis"><em>单例(Singleton)</em></span>辅助变量中。) </p><p> 在无状态的session bean中,可以同样使用类似的方法。bean在<tt class="literal">setSessionContext()</tt>中得到<tt class="literal">SessionFactory</tt>的实例,每个商业方法会生成一个<tt class="literal">Session</tt>对象,调用它的<tt class="literal">flush()</tt>和<tt class="literal">close()</tt>,当然,应用不应该<tt class="literal">commit()</tt>connection. (把它留给JTA.在容器管理的事务中,数据库连接会自动完成事务。) </p><p> 我们用上述方法使用Hibernate 的<tt class="literal">Transaction</tt> API,对<tt class="literal">Transaction</tt>执行一次<tt class="literal">commit()</tt>会把所有状态同步,把底层的数据库连接提交(对JTA 事务会特殊处理。) </p><p> 这里需要理解<tt class="literal">flush()</tt>的含义。 <tt class="literal">flush()</tt>将持久化存储与内存中的变化进行同步,但<span class="emphasis"><em>不是</em></span>将内存的变化与持久化存储进行同步。注意对所有的Hibernate JDBD 连接/事务来说,其隔离级别将施加于所有的Hibernate执行的操作之上! </p><p> 接下来的几小节将讨论利用版本化的方法来确保事务原子性,这些“高级”方法需要小心使用。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-threads"></a>10.2. 线程和连接(Threads and connections)</h2></div></div><div></div></div><p> 在创建Hibernate会话(Session)时,你应该留意以下的实践(practices): </p><div class="itemizedlist"><ul type="disc" compact><li><p> 对于一个数据库连接,不要创建一个以上的<tt class="literal">Session</tt>或<tt class="literal">Transaction</tt>。 </p></li><li><p> 在对于一个数据库连接、一个事务使用多个<tt class="literal">Session</tt>时,你尤其需要格外地小心。<tt class="literal">Session</tt>对象会记录下调入数据更新的情况,所以另一个<tt class="literal">Session</tt>对象可能会遇到过时的数据。 </p></li><li><p> <tt class="literal">Session</tt><span class="emphasis"><em>不是</em></span>线程安全的。决不要在两个并发的线程中访问同一个<tt class="literal">Session</tt>。一个<tt class="literal">Session</tt>一般只对应一批需要一次性完成的单元操作! </p></li></ul></div></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-identity"></a>10.3. 考虑对象辨别</h2></div></div><div></div></div><p> 程序可能在两批单元操作中并发访问同一个对象的持久化状态。不管怎样,持久化类的一个实例不可能在两个<tt class="literal">Session</tt>中共享。所以有两种不同的辨别方式: </p><div class="variablelist"><dl><dt><span class="term">数据库辨别</span></dt><dd><p> <tt class="literal">foo.getId().equals( bar.getId() )</tt> </p></dd><dt><span class="term">JVM 辨别</span></dt><dd><p> <tt class="literal">foo==bar</tt> </p></dd></dl></div><p> 对于依附于某个<span class="emphasis"><em>特定</em></span><tt class="literal">Session</tt>的对象,两种辨别方式是等价的。然而,当程序可能在两个不同的session中并发访问“同一个”(持久化辨别)商业对象时,两个实例(对于JVM辨别来说)却可能是“不同”的。 </p><p> 这种方式把关于并发的头疼问题留给了Hibernate和数据库。程序不需要对任何商业对象进行同步,只要程序坚持每个<tt class="literal">Session</tt>一个线程,或者对象辨别的策略(在一个<tt class="literal">Session</tt>重,程序可以安全的使用<tt class="literal">==</tt>来比较对象)。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-optimistic"></a>10.4. 乐观并发控制(Optimistic concurrency control)</h2></div></div><div></div></div><p> 许多商业过程需要一系列与用户进行交互的过程,数据库访问穿插在这些过程中。对于web和企业应用来说,跨一个用户交互过程的数据事务是不可接受的。 </p><p> 维护各商业事务间的隔离(isolocation)就成为应用层的部分责任,我们把这种过程称为长时间运行的<span class="emphasis"><em>应用事务(application transaction)</em></span>。单一的应用事务可能跨越多个数据库事务。如果这些数据库事务中只有一个(最后一个)保存了被修改的数据,其他事务只是简单地读数据,则这个应用事务就是原子性的。 </p><p> 唯一满足高并发性以及高可扩展性的方法是使用带有版本化的乐观并发控制。Hibernate为使用乐观并发控制的代码提供了三种可能的方法。 </p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-optimistic-longsession"></a>10.4.1. 使用长生命周期带有自动版本化的会话</h3></div></div><div></div></div><p> 在整个商业过程中使用一个单独的<tt class="literal">Session</tt>实例以及它的持久化实例,这个<tt class="literal">Session</tt>使用带有版本化的乐观锁定机制,来确保多个数据库事务对于应用来说只是一个逻辑上的事务。在等待用户交互时,<tt class="literal">Session</tt>断开与数据库的连接。这个方法从数据库访问方面来看是最有效的,应用不需要关心对自己的版本检查或是重新与不需要序列化(transient)的实例进行关联。 </p><p> 在整个应用事务中,使用单一的<tt class="literal">Session</tt> 实例和它的持久化实例。 </p><p> <tt class="literal">Session</tt> 使用带有版本化的乐观锁定来保证多个数据库事务对程序来说就如同是单一的逻辑应用事务。在等待用户交互的时候,<tt class="literal">Session</tt> 脱离所有的底层JDBC连接。对于数据库访问来说,这种方法是最高效的。程序自己不需要关心版本检查或者把已经脱离session的实例重新关联到session。 </p><pre class="programlisting">// foo is an instance loaded earlier by the Sessionsession.reconnect();foo.setProperty("bar");session.flush();session.connection().commit();session.disconnect();</pre><p> <tt class="literal">foo</tt>对象仍然知道是哪个<tt class="literal">Session</tt>把自己装载的。 只要<tt class="literal">Session</tt> 拥有一个JDBC连接,我们可以把对象的更改提交。 </p><p> 如果我们的 <tt class="literal">Session</tt> 太大,以至于在用户思考的时间内无法保存住,这种模式就会出现问题。比如,<tt class="literal">HttpSession</tt>应该保持尽量小。因为<tt class="literal">Session</tt>也持有(必须的)第一级缓存,包含所有被装载的对象,我们只能在很少的request/response周期中使用这一策略。这种少用是被鼓励的,因为<tt class="literal">Session</tt> 很快就会出现过时的数据。 </p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-optimistic-detached"></a>10.4.2. 使用带有自动版本化的多个会话</h3></div></div><div></div></div><p> 每个与持久化存储的交互出现在一个新的<tt class="literal">Session</tt>中,在每次与数据库的交互中,使用相同的持久化实例。应用操作那些从其它<tt class="literal">Session</tt>调入的已经脱离session的实例的状态,通过使用<tt class="literal">Session.update()</tt>或者<tt class="literal">Session.saveOrUpdate()</tt>来重新建立与它们的关联。 </p><pre class="programlisting">// foo is an instance loaded by a previous Session
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -