Hibernate事务控制.docx
《Hibernate事务控制.docx》由会员分享,可在线阅读,更多相关《Hibernate事务控制.docx(6页珍藏版)》请在冰豆网上搜索。
Hibernate事务控制
4.7事务控制
每个业务逻辑方法都是由一系列的数据库访问完成,这一系列的数据访问可能会修改多条数据记录,这系列的修改应该是一个整体,绝不能仅修改其中的几条。
也就是说,多个数据库原子访问应该绑定成一个整体——这就是事务。
事务是一个最小的逻辑执行单元,整个事务不能分开执行,要么同时执行,要么同时放弃执行。
4.7.1事务的概念
事务是一步或几步基本操作组成的逻辑执行单元,这些基本操作作为一个整体执行单元,它们要么全部执行,要么全部取消,绝不能仅仅执行部分。
一般而言,每次用户请求,对应一个业务逻辑方法,一个业务逻辑方法往往具有逻辑上的原子性,应该使用事务。
例如,一个转账操作,对应修改两个账户的余额,这两个账户的修改要么同时生效,要么同时取消——同时生效是转账成功,同时取消是转账失败;但不可只修改其中一个账户,那将破坏数据库的完整性。
通常来讲,事务具备如下4个特性:
原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持续性(durability)。
这4个特性也简称为ACID性。
●原子性:
事务是应用中最小执行单位,就如原子是自然界最小颗粒,具有不可再分的特征一样。
事务是应用中不可再分的最小逻辑执行体。
●一致性:
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。
当数据库只包含事务成功提交的结果时,数据库处于一致性状态。
如果系统运行发生中断,某个事务尚未完成而被迫中断,而该未完成的事务对数据库所做的修改已被写入数据库,此时,数据库就处于一种不正确的状态。
比如银行在两个账户之间转账,从A账户向B账户转入1000元。
系统先减少A账户的1000元,然后再为B账户增加1000元。
如果全部执行成功,数据库处于一致性状态。
如果仅执行完A账户金额的修改,而没有增加B账户的金额,则数据库就处于不一致性状态。
因此,一致性是通过原子性来保证的。
●隔离性:
各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都具有隔离性。
也即并发执行的事务之间不能互相影响。
●持续性:
持续性也称为持久性(persistence),指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常保存进物理数据库。
4.7.2Hibernate的事务
Hibernate直接使用JDBC连接和JTA资源,不添加任何附加锁定行为。
Hibernate只添加自动版本管理,而不会锁定内存中的对象,也不会改变数据库事务的隔离级别。
基本上,使用Hibernate就好像直接使用JDBC(或者JTA/CMT)进行数据库访问。
Hibernate中SessionFactory对象的创建代价很高,它是线程安全的对象,被设计成可以为所有的应用程序线程所共享。
通常,SessionFactory会在应用程序启动时创建,一旦创建了SessionFactory将不会轻易关闭,只有当应用关闭时,SessionFactory才会关闭。
而Session的对象是轻量级的,它也是线程不安全的。
对于单个业务进程单个工作单元而言,Session只被使用一次。
创建Session时,并不会立即打开与数据库之间的连接,Session只在需要进行数据库操作时,才会获取JDBC连接。
因此,打开和关闭Session,并不会对性能造成很大的影响。
甚至即使无法确定一个请求是否需要数据访问,也可以打开Session对象,因为如果不进行数据库访问,Session不会获取JDBC连接。
相反,数据库事务应该尽可能的短。
从而,降低数据库锁定造成的资源争用。
数据库长事务会导致应用程序无法承载高并发的负荷。
由上面的介绍可知,Hiberante的Session和事务是紧密相关的,因为事务是通过Session来打开的。
那么事务的范围是多大?
单个Session可以跨越多个数据库事务吗?
事务和Session的对应关系又如何呢?
下面将介绍HibernateSession和事务的关系。
4.7.3事务和Session
数据库操作必须在Hibernate的Session管理下进行,但不推荐因为一次简单的数据库原子调用,就打开和关闭一次Session,数据库事务也是如此。
因为,对于一次原子操作打开的事务没有任何意义——事务应该是将多个操作步骤组合成一个逻辑整体。
事务是按顺序发送并组成一个逻辑整体的原子操作单元。
注意:
也就是说单个的SQL语句发送之后,自动事务提交模式失效了。
这种自动提交模式仅为SQL控制台设计,在实际项目没有太大的实用价值。
Hibernate禁止事务立即自动提交模式,或者让应用服务器禁止事务自动提交。
通常,建议每个请求对应一个Session。
在这种模式下,来自客户端的请求被发送到服务器端,此处可能对应一个业务逻辑方法。
在这个业务逻辑方法内,一个新的HibernateSession被打开,然后开始事务,在事务内执行这个操作单元中所有的数据库操作。
一旦操作完成,需要发送给客户端的响应也准备就绪。
此时,提交事务,然后关闭Session。
在这种模式下,Session和用户请求是一对一的关系,这是一种理想的Session管理模式。
为了达到这种效果,推荐使用一个ThreadLocal变量,把Session绑定到处理客户端请求的线程上去。
这种方式可以让运行在该线程上的所有程序代码轻松地访问Session。
也可以在一个ThreadLocal变量中保持事务上下文环境,不过这依赖于所选择的数据库事务划分机制。
这种实现模式被称之为ThreadLocalSession和OpenSessioninView。
下面是一个HibernateUtil类,该类将HibernateSession存放在一个ThreadLocal变量中,对于同一个线程的请求,将可以轻松访问该Session。
publicclassHibernateUtil
{
publicstaticfinalSessionFactorysessionFactory;
//静态初始化块,使用该类时使用该代码块
static
{
try
{
//采用默认的hibernate.cfg.xml来启动一个Configuration的实例
Configurationconfiguration=newConfiguration().configure();
//由Configuration的实例来创建一个SessionFactory实例
sessionFactory=configuration.buildSessionFactory();
}
catch(Throwableex)
{
System.err.println("初始化sessionFactory失败."+ex);
thrownewExceptionInInitializerError(ex);
}
}
//ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要
对线程同步
publicstaticfinalThreadLocalsession=newThreadLocal();
//该方法用于获取当前线程的Session对象
publicstaticSessioncurrentSession()throwsHibernateException
{
Sessions=(Session)session.get();
//如果该线程还没有Session,则创建一个新的Session
if(s==null)
{
s=sessionFactory.openSession();
//将获得的Session变量存储在ThreadLocal变量的Session里
session.set(s);
}
returns;
}
//该方法用于关闭当前线程里的Session
publicstaticvoidcloseSession()throwsHibernateException
{
Sessions=(Session)session.get();
if(s!
=null)
s.close();
session.set(null);
}
}
在上面的代码中,HibernateSession被绑定到当前线程。
当调用currentSession方法时,如果当前线程中的Session已经创建出来,那么将返回这个已经存在的Session实例。
每次请求对应一个Session的模式不仅可以用于设计操作单元,甚至很多业务处理流程都需要组合一系列的用户操作,即用户对数据库的交叉访问。
但是,对于企业应用,跨用户交互的数据库事务是无法接受的。
例如,在第一个页面,用户打开对话框,打开一个特定Session装入的数据,可以随意修改对话框中的数据,修改完成后,将修改结果存入数据库。
从用户的角度来看,这个操作单元被称为应用程序长事务。
在一个J2EE应用实现中,可以有很多方法来实现这种应用程序长事务。
一个比较差的做法是,当用户思考时,应用程序保持Session和数据库事务是打开的,并保持数据库锁定,以阻止并发修改,从而保证数据库事务隔离级别和原子操作。
这种数据库锁定会导致应用程序无法扩展并发用户的数目。
因此,不要使用每个应用对应一次HibernateSession的模式,也不要使用每次HttpSession对应一次HibernateSession的模式。
注意:
几乎所有情况下,都不要使用每个应用对应一次HibernateSession的模式,也不要使用每次HttpSession对应一次HibernateSession的模式。
对于这种情况,Hibernate主要有如下两种模式来解决这个问题:
●脱管对象,如果采用每次用户请求对应一次Session的模式。
那么,前面载入的实例在用户思考的过程中,始终与Session脱离,处于脱管状态。
都处于与Session脱离的状态。
Hibernate允许把脱管对象重新关联到Session上,并且对修改进行持久化。
在这种模式下,自动版本化被用来隔离并发修改。
这种模式也被称为使用脱管对象的每个请求对应一个HibernateSession。
●长生命周期Session,Session可以在数据库事务提交之后,断开和底层的JDBC连接。
当新的客户端请求到来时,它又重新连接上底层的JDBC连接。
这种模式被称为每个应用程序事务对应一个Session,因为应用程序事务相当长(跨越多个用户请求),所以也被称为每次应用事务对应一个HibernateSession。