Hibernate自己并不具有事务管理能力 。在事务管理层,
Hibernate将其委托给底层的JDBC或者JTA ,以实现事务管理和调度功能。
Hibernate的默认事务处理机制基于JDBC Transaction。java
JTA 提供了跨Session 的事务管理能力。这一点是与JDBC Transaction 最大的
差别。
JDBC事务由Connnection管理,也就是说,事务管理其实是在JDBC Connection
中实现。事务周期限于Connection的生命周期之类。一样,对于基于JDBC Transaction
的Hibernate 事务管理机制而言,事务管理在Session 所依托的JDBC Connection
中实现,事务周期限于Session的生命周期。
JTA 事务管理则由 JTA 容器实现,JTA 容器对当前加入事务的众多Connection 进
行调度,实现其事务性要求。JTA的事务周期可横跨多个JDBC Connection生命周期。
一样对于基于JTA事务的Hibernate而言,JTA事务横跨可横跨多个Session。数据库
9.3 Hibernate的事务管理 session
事务(Transaction)是工做中的基本逻辑单位,能够用于确保数据库可以被正确修改,避免数据只修改 了一部分而致使数据不完整,或者在修改时受到用户干扰。做为一名软件设计师,必须了解事务并合理利用,以确保数据库保存正确、完整的数据。数据库向用户提 供保存当前程序状态的方法,叫事务提交(commit);当事务执行过程当中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚 (rollback)。数据结构
事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)4个属性,简称ACID。下面对这4个特性分别进行说明。并发
l 原子性:将事务中所作的操做捆绑成一个原子单元,即对于事务所进行的数据修改等操做,要么所有执行,要么所有不执行。分布式
l 一致性:事务在完成时,必须使全部的数据都保持一致状态,并且在相关数据中,全部规则都必须应用于事务的修改,以保持全部数据的完整性。事务结束时,全部的内部数据结构都应该是正确的。性能
l 隔离性:由并发事务所作的修改必须与任何其余事务所作的修改相隔离。事务查看数据时数据所处的状态,要么是被另外一并发事务修改以前的状态,要么是被另外一并 发事务修改以后的状态,即事务不会查看由另外一个并发事务正在修改的数据。这种隔离方式也叫可串行性。spa
l 持久性:事务完成以后,它对系统的影响是永久的,即便出现系统故障也是如此。hibernate
事务隔离意味着对于某一个正在运行的事务来讲,好像系统中只有这一个事务,其余并发的事务都不存在同样。在大部分状况下,不多使用彻底隔离的事务。但不彻底隔离的事务会带来以下一些问题。设计
l 更新丢失(Lost Update):两个事务都企图去更新一行数据,致使事务抛出异常退出,两个事务的更新都白费了。
l 脏数据(Dirty Read):若是第二个应用程序使用了第一个应用程序修改过的数据,而这个数据处于未提交状态,这时就会发生脏读。第一个应用程序随后可能会请求回滚被修改的数据,从而致使第二个事务使用的数据被损坏,即所谓的“变脏”。
l 不可重读(Unrepeatable Read):一个事务两次读同一行数据,但是这两次读到的数据不同,就叫不可重读。若是一个事务在提交数据以前,另外一个事务能够修改和删除这些数据,就会发生不可重读。
l 幻读(Phantom Read):一个事务执行了两次查询,发现第二次查询结果比第一次查询多出了一行,这多是由于另外一个事务在这两次查询之间插入了新行。
针对由事务的不彻底隔离所引发的上述问题,提出了一些隔离级别,用来防范这些问题。
l 读操做未提交(Read Uncommitted):说明一个事务在提交前,其变化对于其余事务来讲是可见的。这样脏读、不可重读和幻读都是容许的。当一个事务已经写入一行数据但 未提交,其余事务都不能再写入此行数据;可是,任何事务均可以读任何数据。这个隔离级别使用排写锁实现。
l 读操做已提交(Read Committed):读取未提交的数据是不容许的,它使用临时的共读锁和排写锁实现。这种隔离级别不容许脏读,但不可重读和幻读是容许的。
l 可重读(Repeatable Read):说明事务保证可以再次读取相同的数据而不会失败。此隔离级别不容许脏读和不可重读,但幻读会出现。
l 可串行化(Serializable):提供最严格的事务隔离。这个隔离级别不容许事务并行执行,只容许串行执行。这样,脏读、不可重读或幻读均可发生。
事务隔离与隔离级别的关系如表9-2所示。
表9-2 事务隔离与隔离级别的关系
隔 离 级 别 |
脏读(Dirty Read) |
不可重读(Unrepeatable read) |
幻读(Phantom Read) |
读操做未提交(Read Uncommitted) |
可能 |
可能 |
可能 |
读操做已提交(Read Committed) |
不可能 |
可能 |
可能 |
可重读(Repeatable Read) |
不可能 |
不可能 |
可能 |
可串行化(Serializable) |
不可能 |
不可能 |
不可能 |
在一个实际应用中,开发者常常不能肯定使用什么样的隔离级别。太严厉的级别将下降并发事务的性能,可是不足够的隔离级别又会产生一些小的Bug,而这些Bug只会在系统重负荷(也就是并发严重时)的状况下才会出现。
通常来讲,读操做未提交(Read Uncommitted)是很危险的。一个事务的回滚或失败都会影响到另外一个并行的事务,或者说在内存中留下和数据库中不一致的数据。这些数据可能会被另外一个事务读取并提交到数据库中。这是彻底不容许的。
另外,大部分程序并不须要可串行化隔离(Serializable Isolation)。虽然,它不容许幻读,但通常来讲,幻读并非一个大问题。可串行化隔离须要很大的系统开支,不多有人在实际开发中使用这种事务隔离模式。
如今留下来的可选的隔离级别是读操做已提交(Read Committed)和可重读(Repeatable Read)。Hibernate能够很好地支持可重读(Repeatable Read)隔离级别。
JDBC链接数据库使用的是默认隔离级别,即读操做已提交(Read Committed)和可重读(Repeatable Read)。在Hibernate的配置文件hibernate.properties中,能够修改隔离级别:
#hibernate.connection.isolation 4
在上一行代码中,Hibernate事务的隔离级别是4,这是什么意思呢?级别的数字意义以下。
l 1:读操做未提交(Read Uncommitted)
l 2:读操做已提交(Read Committed)
l 4:可重读(Repeatable Read)
l 8:可串行化(Serializable)
所以,数字4表示“可重读”隔离级别。若是要使以上语句有效,应把此语句行前的注释符“#”去掉:
hibernate.connection.isolation 4
也能够在配置文件hibernate.cfg.xml中加入如下代码:
<session-factory>
…..
//把隔离级别设置为4
<property name=” hibernate.connection.isolation”>4</property>
……
</session-factory>
在开始一个事务以前,Hibernate从配置文件中得到隔离级别的值。
Hibernate对JDBC进行了轻量级的封装,它自己在设计时并不具有事务处理功能。Hibernate 将底层的JDBCTransaction或JTATransaction进行了封装,再在外面套上Transaction和Session的外壳,实际上是 经过委托底层的JDBC或JTA来实现事务的处理功能的。
要在Hibernate中使用事务,能够在它的配置文件中指定使用JDBCTransaction或者JTATransaction。在hibernate.properties中,查找“transaction.factory_class”关键字,获得如下配置:
# hibernate.transaction.factory_class org.hibernate.transaction.JTATransactionFactory
# hibernate.transaction.factory_class org.hibernate.transaction.JDBCTransactionFactory
Hibernate的事务工厂类能够设置成JDBCTransactionFactory或者JTATransactionFactory。若是不进行配置,Hibernate就会认为系统使用的事务是JDBC事务。
在JDBC的提交模式(commit mode)中,若是数据库链接是自动提交模式(auto commit mode),那么在每一条SQL语句执行后事务都将被提交,提交后若是还有任务,那么一个新的事务又开始了。
Hibernate在Session控制下,在取得数据库链接后,就马上取消自动提交模式,即 Hibernate在一个执行Session的beginTransaction()方法后,就自动调用JDBC层的 setAutoCommit(false)。若是想本身提供数据库链接并使用本身的SQL语句,为了实现事务,那么一开始就要把自动提交关掉 (setAutoCommit(false)),并在事务结束时提交事务。
使用JDBC事务是进行事务管理最简单的实现方式,Hibernate对于JDBC事务的封装也很简单。下面是一个在Hibernate中使用JDBC事务的例子:
try {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction(); //在默认状况下,开启一个JDBC事物
for(int i=0; i<10; i++) {
Student stu = new Student();
stu.setName("Student" + i);
session.save(stu);
}
tx.commit(); //提交事务
session.close();
} catch(Exception e) {…
tx.rollback(); //事务回滚
}
JTA(Java Transaction API)是事务服务的J2EE解决方案。本质上,它是描述事务接口的J2EE模型的一部分,开发人员直接使用该接口或者经过J2EE容器使用该接口来确保业务逻辑可以可靠地运行。
JTA有3个接口,它们分别是UserTransaction接口、TransactionManager接 口和Transaction接口。这些接口共享公共的事物操做,例如commit()和rollback(),但也包含特殊的事务操做,例如 suspend()、resume()和enlist(),它们只出如今特定的接口上,以便在实现中容许必定程度的访问控制。
在一个具备多个数据库的系统中,可能一个程序会调用几个数据库中的数据,须要一种分布式事务,或者准备用JTA来管理跨 Session的长事务,那么就须要使用JTA事务。下面介绍如何在Hibernate的配置文件中配置JTA事务。在 hibernate.properties文件中设置以下(把JTATransactionFactory所在的配置行的注释符“#”取消掉):
hibernate.transaction.factory_class org.hibernate.transaction.JTATransactionFactory
# hibernate.transaction.factory_class org.hibernate.transaction.JDBCTransactionFactory
或者在hibernate.cfg.xml文件中配置以下:
<session-factory>
…..
<property name=” hibernate.transaction.factory_class”>
org.hibernate.transaction.JTATransactionFactory
</property>
……
</session-factory>
下面是一个应用JTA事务的例子:
javax.transaction.UserTransaction tx = null;
tx = new initialContext().lookup(” javax.transaction.UserTransaction ”) ;
tx.begin();
Session s1 = sf.openSession();
……
s1.flush(); s1.close();
Session s2 = sf.openSession();
……
s2.flush(); s2.close();
tx.commit();