hibernate的事务管理
在讲解hibernate的事物管理之前,我们必须首先明白一些基本的概念,以此帮助理解,为什么要事物管理这个东西,有它之后有什么用,它有哪些基本特性或者限制,
那么第一点,我们该知道的就是事物的4个基本特性,也就是我们常说的ACID【原子性,一致性,隔离性,持久性】;
第二点,应该知道的是,事物的隔离级别,即通过某种机制,在并发运行的多个事物之间进行分隔,使其保持执行时的独立性。一般分为Read Uncommitted, Read Committed , Repeatable Read, Serializable这四个隔离级别,这四种隔离级的严密程度是一次递增的,同事它的性能也是依次下降,在脏读取,不可重读,和虚读,这三个中数据库可能出现漏洞的情况下,这四个级别采取的应对措施是不同的,也就是说,这四个隔离级下,这三种情况发生的可能性是,1.可能脏读,可能重读,可能虚读;2.不可能脏读,可能重读,可能虚读; 3.不可能脏读,不可能重读,可能虚读 4.三种漏洞全都不可能发生。所以日常使用时,需根据实际情况取舍,以保持系统的最佳平衡。
hibernate是jdbc轻量级的封装,本身不具备事务管理的能力,在事物管理层面,一般是委托于底层的jdbc和jta来完成调度的。默认事物处理机制是基于jdbc transaction的,当然,我们也能通过配置jta来实现:
<hibernate-configuration> <session-factory> ........ <property name="hibernate.transaction.factory_class"> net.sf.hibernate.transaction.JTATransactionFactory //JTA <!--net.sf.hibernate.transaction.JDBCTransactionFactory--> //JDBC </property> ........ </sesssion-factoty> </hibernate-configuration>
将事物委托给jdbc处理,无疑是最简单的方式,hibernate支持它做默认方式,其事物的封装,也非常简单,比如当我们在处理这段代码时:
session = sessionFactory.openSession();
Transaction tx=session.beginTransaction();
......
tx.commit();
而从jdbc事物层面而言,实际上做的是:
Connection dbconn=getConnection();
dbconn.setAutoCommit(false);
......
dbconn.commit();
就这么简单,换了个说法,把hibernate的提交方式,在底层用原生的jdbc进行了提交,这里需要注意的是在openSession()的时候,底层的jdbc的AutoCommit属性已被关闭了,除非jdbc连接去commit,否则执行的操作将不会对数据库产生任何效果,这里那就需要我们去显示执行tx.commit()了[非事物性数据库除外,如Mysql ISam]
而JTA与jdbc的事物的最大差异,在于它提供了跨session的事物管理能力。jdbc事物由Connection管理,事物的周期限于Connection的生命周期。而JTA则由JTA容器实现,容器可对当前的众多Connection进行调度,于是它可横跨多个Connection生命周期,于是也提供了跨session的能力。
比如我们下面这段代码,在jdbc的事物下完全正确的,但是在jta下就会产生问题:
class ClassA{
public void save(User user){
Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();
session.save(user);
tx.commit();
session.close();
}
}
class ClassB{
public void save(Order order){
Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();
session.save(order);
tx.commit();
session.close();
}
}
class ClassC{
public void save(){
.....
UserTransaction tx=(UserTranscation)(new InitialContext().lookup("...."));
ClassA.save(user);
ClassB.save(oreder);
tx.commit();
......
}
}
这里的问题是,ClassA和B调用了hibernate的Transaction功能。而在hibernate的JTA分装中,Session.beginTransaction同样也执行了InitialContext.lookup去获得UserTransaction的实例,而在tx.commit的时候,ClassA和B都企图声明自己的事物,从而导致错误,因为我们知道,jdbc的事物,是不能够跨connection,跨session的。而假使使用jta,那么我们则可以修改上面代码,实现操作:
class ClassA{
public void save(User user){
Session session=sessionFactory.openSession();
session.save(user);
session.close();
}
}
class ClassB{
public void save(Order order){
Session session=sessionFactory.openSession();
session.save(order);
session.close();
}
}
class ClassC{
public void save(){
.....
Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();
ClassA.save(user);
ClassB.save(order);
tx.commit();
session.close();
}
}
在EJB中使用JTA Transaction无疑最为简单,我们只需要将save方法的属性声明为Required,Ejb容器就会自动的维护并执行过程中的事物。
到这里,hibernate的事务管理就jdbc和jta方面已经叙述过一遍了,假使大家最先使用的jdbc即默认方式,那么没关系,一直用下去就好了。假使希望用jta去跨session处理,那么一定要注意上面提出的差异和用法。希望对大家有所帮助。