1、定义事务
可以引用数据库的物理事务
可以引用和一个持久上下文相关的逻辑事务
可以引用应用程序的工作单元,当做由原型模式定义
2、物理事务
Hibernate使用JDBC API的持久性,在java中有两个定义良好的机制处理事务的JDBC:JDBC和JTA。
Hibernate支持结合事务机制,允许应用程序来管理物理事务。
org.hibernate.engine.transaction.spi.TransactionFactory中2个主要的功能:
它允许hibernate理解环境的事务语义,是否JTA?是否当前活动的物理事务?等等
作为一个为org.hibernate.Transaction实例工厂。用来允许应用程序管理和检查事务的状态,
org.hibernate.Transaction是hibernate逻辑事务概念。JPA中javax.persistence.EntityTransaction接口也类似
物理事务-JDBC
基于JDBC事务管理利用java.sql.Connection.commit()和java.sql.Connection.rollback(),
JDBC没有定义一个显式的方法开始一个事务。
在hibernate中,该方法由org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory类表现
物理事务-JTA
基于JTA事务方法是从org.hibernate.service.jta.platform.spi.JtaPlatform API获得javax.transaction.UserTransaction接口
由org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory类表现
物理事务-CMT
另一种基于JTA事务方法是从org.hibernate.service.jta.platform.spi.JtaPlatform API获得javax.transaction.TransactionManager接口
由org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory类表现
在真实的JEE CMT环境中,访问javax.transaction.UserTransaction是限制的
物理事务-Custom
可以实现org.hibernate.engine.transaction.spi.TransactionFactory来插入自定义的事务方式。
默认服务启动有内置支持由hibernate.transaction.factory_class指定名称的自定义事务:
使用org.hibernate.engine.transaction.spi.TransactionFactory实例
使用实现org.hibernate.engine.transaction.spi.TransactionFactory类的名字,实现类提供无参构造器
物理事务-Legacy
在开发4.0过程中,此部分大多数类名被移动到新的包中,帮助促进升级,Hibernate也遗留名字一段时间
org.hibernate.transaction.JDBCTransactionFactory org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
org.hibernate.transaction.JTATransactionFactory org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
org.hibernate.transaction.CMTTransactionFactory org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
3、hibernate事务的使用
Hibernate使用直接JDBC连接和JTA资源,在不增加任何额外的锁定行为。
对你熟悉JDBC,ANSI SQL,事务隔离细节你的数据库管理系统很重要
Hibernate不会在内存中锁定对象,当你使用Hibernate时,由你数据库事务隔离级别定义的行为不会改变。
org.hibernate.Session当做一个事务作用范围缓存提供通过标识符可重复读和加载实体的结果集
为了减少数据库中锁挣用,物理数据库的事务必需尽可能的短。长数据库事务阻止你的应用扩展到高并发负载,
不持有一个数据库的事务在打开最终用户级别的工作期间,但在最终用户级别的工作完成后打开它。称为:事务的write-behind
4、事务的模式(反模式)
Session-per-operation 反模式
在单线程中为每个数据库的调用打开和关闭一个Session的反模式,它也是一种反模式的数据库事务。
把你的数据库调用分组到一个计划序列。同样的,应用中每一个sql语句之后不自动提交。
Hibernate禁用,或者应用服务器禁用自动提交模式。
数据库的事务从来都不是可选的,所有使用数据库的通讯必需通过一个事务进行封装。
避免读取数据时自动提交,因为许多小事务不太可能比一个定义良好的工作单元执行更好,并且难以维护和扩展。
注:使用自动提交不会避免数据库事务。当在自动提交模式中时,JDBC在一个隐式事务中简单的执行每次调用。
如同每一个JDBC调用之后由应用程序调用提交一样。
Session-per-request 模式
这是最常见的事务模式,request有关一系列从客户端/用户的请求。Web应用就是这种类型的一个典型例子。
在处理这一请求的开始,应用打开一个Hibernate的Session,开始一个事务,执行所有数据相关的工作,结束该事务并关闭Session。
该模式的关键是在事务和Session之间为1对1的关系。
在此模式中,为了简化传递Session到所有可能需要访问它的应用程序组件而定义一个Current Session的通用技术。
Hibernate通过SessionFactory的getCurrentSession来支持该技术。
Current Session有一个范围,定义了边界的“当前”的概念是有效,这是org.hibernate.context.spi.CurrentSessionContext的目的,有2个主要的范围:
首先是一个JTA事务,因为它允许一个回调当它结束时使Hibernate关闭会话和清理的选择,
这是由org.hibernate.context.spi.CurrentSessionContext接口的实现org.hibernate.context.internal.JTASessionContext表示
使用该实现,在这个事务中第一次调用getCurrentSession时将打开一个Session
其次是这个应用程序请求周期本身,这是使用org.hibernate.context.spi.CurrentSessionContext接口的实现org.hibernate.context.internal.ManagedSessionContext最好的代表。
这里的外部组件负责管理“当前”会话的生命周期和范围。在此范围的开始,当传递Session时ManagedSessionContext的bind方法被调用,最后,unbind被调用
外部组件通常包括如下:
javax.servlet.Filter的实现
在服务方法上的一个pointcut的AOP拦截器
一个proxy/interception container
Conversations
Session-per-request不是唯一有效设计工作单元的方式,许多业务操作要求一系列的用户交互(数据库访问是交织一起)
在Web和企业应用中,一个数据库事务跨越用户的交互是不可接受的。考虑下面:
一个长期运行的对话的例子
1、首先打开对话框一个屏幕,在一个特定的Session和数据库事务中加载用户所看到的数据。用户可以自由的修改这些对象。
2、用户编辑5分钟后使用一个UI元素保存他们的工作。这些修改进行持久化。用户也希望在编辑会话期间,可以独占的访问这些数据。
尽管上例中有多个数据库的访问,从用户视图入口点,这一系列的步骤代表一个工作单元,有多种实现方式。
第一个天真的实现可能保持会话和数据库事务打开在用户编辑,使用数据库级锁,以防止其他用户修改相同的数据和保证隔离和原子性。因为锁争用的瓶颈将防止未来的可扩展性
有几个数据库事务用来实现Conversation,在这种情况下,应用程序保持业务处理的隔离性。
一个Conversation通常跨越多个数据库事务,多个数据库访问只能原子作为一个整体,只要其中一个数据库事务(通常是最后一个)存储更新的数据。其他所有只读数据。
hibernate包括一些特性使得这些更容易实现(Automatic Versioning,Detached Objects,Extended Session)
5、对象标识
一个应用程序可以同时访问相同的持久状态(数据库行)在两个不同的会话,然而,持久化类的实例是从来没有两个会话实例之间共享。
2个不同标识的概念存在:数据库标识和JVM标识
数据库标识:foo.getId().equals( bar.getId() )
JVM标识:foo==bar
应用程序可能并发访问业务对象在两个不同的会话以相同的对象标识,在JVM标识上面,两个实例实际上是不同的。
在flush/commit时,使用乐观锁和自动版本来解决冲突,这种方法用于在Hibernate和数据库并发性,它还提供了最好的可伸缩性。
因为不需要昂贵的锁保证标识在单线程的工作单元。应用程序不需要同步的任何业务对象。只要保持一个线程一个反模式
不建议在应用的一个Session中使用“==”操作符比较对象。而在应用的Session之外使用“==”操作可能会引入问题,
若将2个detached实例放入相同的Set中,他们可能使用相同的数据库标识(这意味着它们代表数据库中的同一行)。
如果他们处于detached state时,它们不会保证有相同的JVM标识。在持久化类覆盖的equals和hashCode方法,保证它们有自己相等性
不使用数据库标识符来实现平等。代替使用一个业务key(唯一的组合,通常是不可变的,属性)。
如果一个transient对象变成persistent,则它的数据库标识会改变。
若是transient 实例和detached 实例被放入Set中,违背了Set中hashcode的规则。
业务key稳定性低于数据库的主键,若对象是在相同的Set中,只需保证稳定性。这不是一个Hibernate问题,但涉及到Java对象标识平等的实现
6、常见的问题
Session 不是线程安全的,像HTTP requests, session beans一样,若Session实例共享会引起争用情况。
若在javax.servlet.http.HttpSession中保持Hibernate的Session,你应该考虑同步访问你的HttpSession,否则,
用户以足够快点击重新加载,出现在2个并发线程中使用相同的Session
Hibernate抛出异常,意味着你必须立即回滚数据库事务并关闭会话。如果你的会话绑定到应用程序,你必须停止应用程序。
回滚数据库事务没有把你的业务对象回到事务开始的状态。这意味着数据库状态和业务对象将不同步。
通常这不是一个问题,因为异常是不可恢复的并且回滚后,你将不得不重新开始。
Session缓存每个在persistent state的对象(由Hibernate检查更改),如果你保持打开很长一段时间或简单地加载太多数据,
它将不断增长,直到抛出OutOfMemoryException。一个解决办法是调用clear()和evict()来管理会话缓存,
但是你应该考虑另一种方法处理大量数据,如一个存储过程,Java是不合适的工具对这些类型的操作,一些批量处理方案。
保持一个会话打开一个用户会话期间也意味着更高概率的失效数据。
转载于:https://my.oschina.net/rksi5/blog/325461