📄 transactions.html
字号:
</p><p> 头一个幼稚的做法是,在用户思考的过程中,保持<tt class="literal">Session</tt>和数据库事务是打开的, 保持数据库锁定,以阻止并发修改,从而保证数据库事务隔离级别和原子操作。这种方式当然是一个反模式, 因为数据库锁定的维持会导致应用程序无法扩展并发用户的数目。 </p><p> 很明显,我们必须使用多个数据库事务来实现一个应用程序事务。在这个例子中,维护业务处理流程的 事务隔离变成了应用程序层的部分责任。单个应用程序事务通常跨越多个数据库事务。如果仅仅只有一 个数据库事务(最后的那个事务)保存更新过的数据,而所有其他事务只是单纯的读取数据(例如在一 个跨越多个请求/响应周期的向导风格的对话框中),那么应用程序事务将保证其原子性。这种方式比听 起来还要容易实现,特别是当你使用了Hibernate的下述特性的时候: </p><div class="itemizedlist"><ul type="disc"><li><p> <span class="emphasis"><em>自动版本化</em></span> - Hibernate能够自动进行乐观并发控制 ,如果在用户思考 的过程中发生并发修改冲突,Hibernate能够自动检测到。 </p></li><li><p> <span class="emphasis"><em>脱管对象</em></span>(Detached Objects)- 如果你决定采用前面已经讨论过的 <span class="emphasis"><em>session-per-request</em></span>模式,所有载入的实例在用户思考的过程 中都处于与Session脱离的状态。Hibernate允许你把与Session脱离的对象重新关联到Session 上,并且对修改进行持久化,这种模式被称为 <span class="emphasis"><em>session-per-request-with-detached-objects</em></span>。自动版本化被用来隔离并发修改。 </p></li><li><p> <span class="emphasis"><em>长生命周期的Session</em></span> (Long Session)- Hibernate 的<tt class="literal">Session</tt> 可以在数据库事务提交之后和底层的JDBC连接断开,当一个新的客户端请求到来的时候,它又重新连接上底层的 JDBC连接。这种模式被称之为<span class="emphasis"><em>session-per-application-transaction</em></span>,这种情况可 能会造成不必要的Session和JDBC连接的重新关联。自动版本化被用来隔离并发修改。 </p></li></ul></div><p> <span class="emphasis"><em>session-per-request-with-detached-objects</em></span> 和 <span class="emphasis"><em>session-per-application-transaction</em></span> 各有优缺点,我们在本章后面乐观并发 控制那部分再进行讨论。 </p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-basics-identity"></a>12.1.3. 关注对象标识(Considering object identity)</h3></div></div><div></div></div><p> 应用程序可能在两个不同的<tt class="literal">Session</tt>中并发访问同一持久化状态,但是, 一个持久化类的实例无法在两个 <tt class="literal">Session</tt>中共享。因此有两种不同的标识语义: </p><p> </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> </p><p> 对于那些关联到 <span class="emphasis"><em>特定</em></span><tt class="literal">Session</tt> (也就是在单个<tt class="literal">Session</tt>的范围内)上的对象来说,这 两种标识的语义是等价的,与数据库标识对应的JVM标识是由Hibernate来保 证的。不过,当应用程序在两个不同的session中并发访问具有同一持久化标 识的业务对象实例的时候,这个业务对象的两个实例事实上是不相同的(从 JVM识别来看)。这种冲突可以通过在同步和提交的时候使用自动版本化和乐 观锁定方法来解决。 </p><p> </p><p> 这种方式把关于并发的头疼问题留给了Hibernate和数据库;由于在单个线程内,操作单元中的对象识别不 需要代价昂贵的锁定或其他意义上的同步,因此它同时可以提供最好的可伸缩性。只要在单个线程只持有一个 <tt class="literal">Session</tt>,应用程序就不需要同步任何业务对象。在<tt class="literal">Session</tt> 的范围内,应用程序可以放心的使用<tt class="literal">==</tt>进行对象比较。 </p><p> </p><p> 不过,应用程序在<tt class="literal">Session</tt>的外面使用<tt class="literal">==</tt>进行对象比较可能会 导致无法预期的结果。在一些无法预料的场合,例如,如果你把两个脱管对象实例放进同一个 <tt class="literal">Set</tt>的时候,就可能发生。这两个对象实例可能有同一个数据库标识(也就是说, 他们代表了表的同一行数据),从JVM标识的定义上来说,对脱管的对象而言,Hibernate无法保证他们 的的JVM标识一致。开发人员必须覆盖持久化类的<tt class="literal">equals()</tt>方法和 <tt class="literal">hashCode()</tt> 方法,从而实现自定义的对象相等语义。警告:不要使用数据库标识 来实现对象相等,应该使用业务键值,由唯一的,通常不变的属性组成。当一个瞬时对象被持久化的时 候,它的数据库标识会发生改变。如果一个瞬时对象(通常也包括脱管对象实例)被放入一 个<tt class="literal">Set</tt>,改变它的hashcode会导致与这个<tt class="literal">Set</tt>的关系中断。虽 然业务键值的属性不象数据库主键那样稳定不变,但是你只需要保证在同一个<tt class="literal">Set</tt> 中的对象属性的稳定性就足够了。请到Hibernate网站去寻求这个问题更多的详细的讨论。请注意,这不是一 个有关Hibernate的问题,而仅仅是一个关于Java对象标识和判等行为如何实现的问题。 </p><p> </p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="transactions-basics-issues"></a>12.1.4. 常见问题</h3></div></div><div></div></div><p> 决不要使用反模式<span class="emphasis"><em>session-per-user-session</em></span>或者<span class="emphasis"><em> session-per-application</em></span>(当然,这个规定几乎没有例外)。请注意, 下述一些问题可能也会出现在我们推荐的模式中,在你作出某个设计决定之前,请务必理解该模式的应用前提。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -