📄 transactions.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Chapter 17. 事务和并行(Transactions And Concurrency)</title><link rel="stylesheet" href="../styles/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.60.1"><link rel="home" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="up" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="previous" href="inheritance.html" title="Chapter 16. 继承映射(Inheritance Mappings)"><link rel="next" href="examples.html" title="Chapter 18. 映射实例(Mapping Examples)"></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">Chapter 17. 事务和并行(Transactions And Concurrency)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="inheritance.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="examples.html">Next</a></td></tr></table><hr></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="transactions"></a>Chapter 17. 事务和并行(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="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-s1"></a>17.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>关闭它,最终丢弃它。 </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> 这里需要理解<tt class="literal">flush()</tt>的含义。 <tt class="literal">flush()</tt>将持久化存储与内存中的变化进行同步,但<span class="emphasis"><em>不是</em></span>将内存的变化与持久化存储进行同步。所以在调用<tt class="literal">flush()</tt>并接着调用<tt class="literal">commit()</tt>关闭连接时,会话将仍然含有过时的数据,在这种情况下,继续使用会话的<span class="emphasis"><em>唯一</em></span>的方法是将会话中的数据进行版本化。 </p><p> 接下来的几小节将讨论利用版本化的方法来确保事务原子性,这些“高级”方法需要小心使用。 </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-s2"></a>17.2. 线程和连接(Threads and connections)</h2></div></div><div></div></div><p> You should observe the following practices when creating Hibernate Sessions: </p><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>具有同步锁。 </p></li></ul></div></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-s3"></a>17.3. 乐观锁定/版本化(Optimistic Locking / Versioning)</h2></div></div><div></div></div><p> 许多商业过程需要一系列与用户进行交互的过程,数据库访问穿插在这些过程中。对于web和企业应用来说,跨一个用户交互过程的数据事务是不可接受的,因而维护各商业事务间的隔离(isolocation)就成为应用层的部分责任。唯一满足高并发性以及高可扩展性的方法是使用带有版本化的乐观锁定。Hibernate为使用乐观锁定的代码提供了三种可能的方法。 </p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-s3-1"></a>17.3.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><pre class="programlisting">// foo is an instance loaded earlier by the Sessionsession.reconnect();foo.setProperty("bar");session.flush();session.connection().commit();session.disconnect();</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-s3-2"></a>17.3.2. 使用带有自动版本化的多个会话</h3></div></div><div></div></div><p> 每个与持久化存储的交互出现在一个新的<tt class="literal">Session</tt>中,在每次与数据库的交互中,使用相同的持久化实例。应用操作那些从其它<tt class="literal">Session</tt>调入的不需要持久化实例的状态,通过使用<tt class="literal">Session.update()</tt>或者<tt class="literal">Session.saveOrUpdate()</tt>来重新建立与它们的关联。 </p><pre class="programlisting">// foo is an instance loaded by a previous Sessionfoo.setProperty("bar");session = factory.openSession();session.saveOrUpdate(foo);session.flush();session.connection().commit();session.close();</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-s3-3"></a>17.3.3. 应用程序自己进行版本检查</h3></div></div><div></div></div><p> 每当一个新的<tt class="literal">Session</tt>中与持久化存储层出现交互的时候,这个session会在操作持久化实例前重新把它们从数据存储中装载进来。我们现在所说的方式就是你的应用程序自己使用版本检查来确保商业过程的隔绝性。(当然,Hibernate仍会为你<span class="emphasis"><em>更新</em></span>版本号)。从数据库访问方面来看,这种方法是最没有效率的,与entity EJB方式类似。 </p><pre class="programlisting">// foo is an instance loaded by a previous Sessionsession = factory.openSession();int oldVersion = foo.getVersion();session.load( foo, foo.getKey() );if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();foo.setProperty("bar");session.flush();session.connection().commit();session.close();</pre><p> 当然,如果在低数据并行(low-data-concurrency)的环境中,并不需要版本检查,你仍可以使用这个方法,只需要忽略版本检查。 </p></div></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="transactions-s4"></a>17.4. 会话断开连接(Session disconnection)</h2></div></div><div></div></div><p> The first approach described above is to maintain a single <tt class="literal">Session</tt> for a whole business process thats spans user think time. (For example, a servlet might keep a <tt class="literal">Session</tt> in the user's <tt class="literal">HttpSession</tt>.) For performance reasons you should </p><p> 上面提到的第一种方法是对于对一个用户的一次登录产生的整个商业过程维护一个<tt class="literal">Session</tt>。(举例来说,servlet有可能会在用户的<tt class="literal">HttpSession</tt>中保留一个<tt class="literal">Session</tt>)。为性能考虑,你必须 </p><div class="orderedlist"><ol type="1" compact><li><p> 提交<tt class="literal">Transaction</tt>(或者JDBC连接),然后 </p></li><li><p> (在等待用户操作前,)断开<tt class="literal">Session</tt>与JDBC连接。 </p></li></ol></div><p> <tt class="literal">Session.disconnect()</tt>方法会断开会话与JDBC的连接,把连接返还给连接池(除非是你自己提供这个连接的)。 </p><p> <tt class="literal">Session.reconnect()</tt>方法会得到一个新的连接(你也可以自己提供一个),重新开始会话。在重新连接后,你可以通过对任何可能被其它事务更新的对象调用<tt class="literal">Session.lock()</tt>方法,来强迫对你没有更新的数据进行版本检查。你不需要对<span class="emphasis"><em>正在</em></span>更新的数据调用lock()。 </p><p> 这是一个例子: </p><pre class="programlisting">SessionFactory sessions;List fooList;Bar bar;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -