欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Hibernate_9 博客分类: Hibenate hibernate事务 

程序员文章站 2024-03-22 08:45:40
...
数据库事务必须具备ACID特征,ACID是Atomic(原子性)、Consistency(一致性)、Isoation(隔离性)、Durability(持久性)的缩写。
原子性:指整个数据库事务是不可分割的工作单元。只有事务中所有的操作执行成功,才算整个事务成功
一致性:数据库事务不能破坏关系数据的完整性及业务逻辑上的一致性
隔离性:在并发环境下,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间
持久性:只要事务成功结束,他对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统之后,数据库还能恢复到事务成功结束时的状态
事物的ACID特性是由关系数据库管理系统来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,若某个事务在执行过程中发生错误,就可以根据日志撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。
数据库管理系统采用所机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其它事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。

数据库系统的客户程序只要向数据库系统声明了一个事务,数据库系统就会自动保证事务的ACID特性。声明事务包含以下几方面:
事务的开始边界(BEGIN)
事务的正常结束边界(COMMIT):提交事务,永久保存被事务更新后的数据库状态
事务的异常结束边界(ROLLBACK):撤销事务,使数据库退回到执行事务前的初始状态
数据库支持以下两种事务模式:
自动提交模式:每个SQL语句都是一个独立的事务,当数据库系统执行完一个SQL语句后,会自动提交事务。
手工提交模式:必须由数据库的客户程序显式指定事务开始边界和结束边界

在MySQL中,数据库表分为3种类型:INNODB、BDB、MyISAM类型。其中INNODB和BDB类型的表是支持数据库事务的,而MyISAM类型的表是不支持事务的。

对于java应用,声明事务有以下方式:直接通过JDBC API类声明JDBC事务;直接通过Hibernate API来声明JDBC事务;直接通过Hibernate API来声明JTA事务;直接通过JTA API来声明JTA事务。

在mysql.exe程序中声明事务
每启动一个mysql.exe程序,都会得到一个单独的数据库连接。每个数据库连接都有一个全局变量@@autocommit,表示当前的事0务模式,它有两个可选值:0:表示手工提交模式;1默认值,表示自动提交模式
mysql> select @@autocommit
mysql> set autocommit=0;
在自动提交模式下运行事务:
在自动提交模式下,每个SQL语句都是一个独立的事务。
在手工提交模式下运行事务:
在手工提交模式下,必须显式指定事务开始边界和结束边界:事务的开始边界:begin、提交事务:commit、撤销事务:rollback
mysql > begin
mysql > select * from ACCOUNTS
mysql > commit

Java应用通过JDBC API声明JDBC事务
在JDBC API中,java.sql.Connection类代表一个数据库连接。Connection类提供了以下用于控制事务的方法:
setAutoCommit(boolean autoCommit):设置是否自动提交事务
commit():提交事务
rollback():撤销事务
可以通过setAtuoCommit(false)方法来设置手工提交事务模式,然后就可以把多条更新数据库的SQL语句作为一个事务,在所有操纵完成后调用commit()方法来整体提交事务,若一项SQL操作失败,则会抛出SQLException,此时,应在捕获异常的代码块中调用rollback()方法撤销事务。

Java应用通过Hibernate API声明JDBC事务
Hibernate冯各庄了JDBC API和JTA API,Java应用直接通过Hibernate API来声明事务,有两个有点:1.有利于跨平台开发 2.当应用程序通过Hibernate API声明事务时,事务与Session更加紧密地集成在一起。
在Hibernate API中,Session和Transaction接口提供了一下声明事务边界的方法:
声明事务的开始边界:
Transaction tx = session.beginTransaction(); //为Session对象分配数据库连接,并且自动把这个连接设为手工提交事务模式。Hibernate底层实现会自动调用代表数据库连接的java.sql.Connection对象的setAutoCommit(false)方法。开始一个新的JDBC事务。Session的beginTransaction()方法返回的Transaction实例的类型是由Hibernate配置文件中的hibernate.transaction.factory_class事务工厂属性决定的,该属性的默认值为org.hibernate.transaction.JDBCTransactionFactory。
JDBCTransactionFactory表示JDBC事务工厂,它负责创建JDBCTransaction类的实例。
提交事务:
tx.commit(); //在默认的情况下,Session采用自动清理缓存模式,在这种模式下,commit()方法会先自动调用Session的flush()方法清理缓存,即按照Session缓存中对象的变化去同步更新数据库;向底层数据库提交事务;释放Session占用的数据库连接
撤销事务:
tx.rollback(); //立即撤销事务,并且释放Session占用的数据库连接

在Hibernate3以前的版本中,Hibernate抛出的异常都属于受检查异常(Checked Excpetion)
,因此程序必须捕获并处理这种异常(JDBC API抛出异常都属于受检查异常)。但在多数情况下,Hibernate抛出的异常都是致命的。因此,从Hibernate3版本开始,Hibernate抛出的异常都是运行时异常。
public void doWork(){
   Session session = factory.openSession();
   Transaction tx;
   try{
      tx = session.beginTransaction();
      tx.setTimeout(5);  //设置事务超时时间
      ...
      tx.commit();
   }catch(RuntimeException e){
      if(tx==null){
         try{
             tx.rollback();
         }catch(RuntimeException ex){
             log.error("无法撤销事务",ex);
         }
      }
     throw e;
   }finally{
     try{
        session.close();
     }catch(RuntimeException ex){
        log.error("无法关闭Session",ex);
     }
   }
}

一个Session可以对应多个事务,,但一个Session只允许有一个未提交的事务。

Java应用通过Hibernate API声明JTA事务
JTA事务主要运行在受管理环境中。与JDBC事务相比,JTA具有以下特点:由低层JTA实现来提供和管理数据库连接池。而对于JDBC事务,必须由Hibernate来管理数据库连接池;数据库连接池与JNDI绑定,应用程序可通过JNDI API来访问数据库连接池;在处理复杂的大事务方面,由JTA实现提供的数据库连接池更加健壮可靠,性能优越;通过JTA API来声明JTA事务时,支持分布式事务 ;JTA事务在运行时并不会产生额外的系统开销。对于非分布式的JTA事务,它和JDBC具有同样的运行效率。
通过Hibernate API来声明JTA事务的步骤:
在Hibernate配置文件中配置hibernate.connection.datasource属性、hibernate.transaction.factory_class属性和hibernate.transaction.manager_lookup_class属性
#指定数据库连接池的JNDI
hibernate.connection.datasource=java:comp/env/jdbc/SAMPLEDB

#制定事务工厂
hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JbossTransactionManagerLookup

JTA和JDBC在Hibernate内部实现的运行时行为有以下区别:
对于JDBC事务,当事务开始时,Session被分配一个数据库连接,当事务结束时,Session就释放数据库连接;对于JTA事务,仅仅当执行完一个SQL语句时,Session才获得数据库连接,而SQL语句执行完毕后,Session就立刻释放数据库连接。JTA实现会保证同一个JTA事务中的所有SQL操作都是用同一个数据库连接。用org.hibernate.Transaction接口的setTimeout()方法来设定超时时间时,对于JDBC事务,实际上设定的是JDBC层的PreparedSatement执行SQL语句的超时时间;而对于JTA事务,JTA实现能够监控整个JTA事务是否超时,Transaction接口的setTimeout()方法与javax.transaction.UserTransaction接口的setTransactionTimeout()方法是等价的。

Java应用通过JTA API声明JTA事务
通过JTA API声明JTA事务的步骤如下:
在Hibernate配置文件中配置hibernate.transaction.factory_class属性和hibernate.transaction.manager_lookup_class属性
hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JbossTransactionManagerLookup
在程序中,通过JTA的javax.transaction.UserTransaction接口来声明JTA事务。
UserTransaction utx = (UserTransaction)new InitialContext().lookup("UserTransaction");
Session session1 = null;
Session session2 = null;

try{
   utx.begin();

   session1 = sessionFactory.openSession();
   session2 = sessionFactory.openSession();
   ....
   //默认情况下,UserTransaction的commit()方法提交事务时,不会自动清理缓存
   session1.flush();
   session2.flush();

   uxt.commit();  

} }catch(RuntimeException e){
      if(tx==null){
         try{
             utx.rollback();
         }catch(RuntimeException ex){
             log.error("无法撤销事务",ex);
         }
      }
     throw e;
   }finally{
     try{
        session1.close();
        session2.close();
     }catch(RuntimeException ex){
        log.error("无法关闭Session",ex);
     }
   }
}
若在Hibernate配置文件中设置以下两个属性时,则可不必手动清理缓存和关闭Session
hibernate.transaction.flush_before_completion:若为true,在提交事务前自动清理缓存
hibernate.transaction.auto_close_session:若为true,在提交或撤销事务后自动关闭Session


相关标签: hibernate 事务