在数据库操作中,一项事务是由一条或者多条表达式所组成的一个不可分割的工作单元。
事务必须遵循ACID原则,即:
原子性(Atomicity):事务必须完全执行或者完全不执行,任何任务失败都将导致整个事务回滚。
一致性(Consistency):指原始数据库的完整性,事务系统通过确保事务是原子性,隔离性和持续性来实现一致性。
隔离性(Isolation):事物的执行必须不受其它进程或者事务的干扰。
持续性(Durabilty):意味着所有事务过程中的数据更改在事务成功之前必须写入某些物理介质,确保系统崩溃时数据更改不会丢失。
JDBC事务管理
JDBC中引入一批语句对事务进行支持,它们是:
Connection.setAutoCommit(True or fasle);设置事务是否自动提交,它的参数缺省是true.要进行事务处理的话,参数应该设置为false.
Connection.commit():从setAutoCommit语句开始,到commit语句之间算作一个数据库事务,其中的数据库操作要么全成功要么全失败.所有在调用commit方法之前的SQL语句都可以被回滚,然而,一旦commit()方法被调用,执行过的SQL语句就不能再回滚.
Rollback():事务回滚,如果事务中有一个步骤出现错误,那么调用这个方法将使数据库恢复到执行事务之前的状态,这样,事务的原子性就得到了保证.
下面是使用事务处理的例程:
Connection conn=null;
Statement statement=null;
try{
Class.forName("org.gjt.mm.mysql.Driver");
conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1/test", "root", "hy");
statement=conn.createStatement();
conn.setAutoCommit(false);
int changeCount1=statement.executeUpdate(" update employee set salary=salary-10000 where NAME='郭德纲' ");
int changeCount2=statement.executeUpdate(" update employee set salary=salary+10000 where NAME='于谦' ");
if(changeCount1==1 && changeCount2==1){
conn.commit();
}
else{
conn.rollback();
}
}
catch(SQLException se){
try{
conn.rollback();
}
catch(Exception ex){
ex.printStackTrace();
}
}
JDBC事务管理的问题
以上事务处理代码是以数据库的记录为核心来考虑的,即使用Java代码通过JDBC和SQL语句直接操作数据库中的记录,这种传统方式在现代程序中已经不多见,现代程序多把记录归纳成领域对象,然后使用领域对象对应的DAO来完成领域对象与数据库之间的交互。除了DAO层外,其它层次不与数据库发生联系。因此,上面的直接使用JDBC进行事务处理就不适用了,试想AccountService中有一个方法执行两个帐户之间的转账,这个方法要具备事务功能必须绕过DAO层书写底层的JDBC代码,这是层次明晰纪律严明的系统所不能接受的,好在我们有Spring的事务处理可以帮我们摆脱两难境地。
使用Spring进行事务处理
Spring对程序控制事务管理的支持和EJB有很大不同,EJB的事务管理和JTA密不可分,而Spring使用了一种回调机制,把真实的食物实现从事务代码中抽象出来。实际上Spring的事务管理甚至不需要JTA,它也可以使用持久化机制本身多提供的事务管理支持。下面就是使用JDBC作为应用的持久化机制的事务管理示例代码:
首先,在上下文定义文件中写入JDBC事务管理器的bean定义。
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
它需要一个数据源的支持,数据源定义示例如下:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="org.gjt.mm.mysql.Driver">
</property>
<property name="url" value="jdbc:mysql://127.0.0.1/test">
</property>
<property name="username" value="root"></property>
<property name="password" value="hy"></property>
</bean>
JDBC事务管理器会作为事务管理模板的一个属性注入进入。
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
</bean>
而事务管理模板又作为一个属性注入到AccountService中。
<bean id="accountService"
class="com.heyang.service.AccountService">
<property name="dao" ref="accountDao"/>
<property name="table" value="SpringTransaction_Account"/>
<property name="transactionTemplate">
<ref bean="transactionTemplate" />
</property>
</bean>
到这里,在transactionTemplate的帮助下,AccountService就具备了事务处理功能,下面转账函数的具体代码:
public void transfer(final Account from,final Account to,final int count){
transactionTemplate.execute(
new TransactionCallback(){
public Object doInTransaction(TransactionStatus ts){
try{
if(count>from.getCount()){
throw new Exception("转出的帐户"+from+"余额不足");
}
if(count>5000){
throw new Exception("转出的金额超过了中纪委限定的额度5000");
}
// 转出帐户减去转账金额
from.setCount(from.getCount()-count);
update(from);
// 转入帐户加上转账金额
to.setCount(to.getCount()+count);
update(to);
}
catch(Exception ex){
ex.printStackTrace();
// 出现任何异常即回滚
ts.setRollbackOnly();
}
// 如果成功,事务被提交
return null;
}
}
);
}
以上代码中,执行转账的业务代码放在try块中,如果全部代码执行成功,事务将会被提交;如果如下任何异常,事务会被回滚,出现异常前的任何修改点将被恢复到初始状态。
下面是模拟转账的具体代码:
ApplicationContext context=new ClassPathXmlApplicationContext("appCtx.xml");
AccountService service=(AccountService)context.getBean("accountService");
// 准备测试数据
/**//* 建表语句如下
* create table SpringTransaction_Account(
id INTEGER(10) primary key not null ,
name VARCHAR(255),
count INTEGER(255)
)
*/
//Account andy=new Account("andy",3000);
//service.create(andy);
//Account bill=new Account("bill",5000);
//service.create(bill);
// 从数据库取得帐户Andy,11是其ID
Account andy=service.getAccount("11");
Account bill=service.getAccount("12");
// 从Andy帐户转出4000给Bill,如果Andy帐户余额低于4000将不会通过事务处理
service.transfer(andy, bill, 4000);
// 从Andy帐户转出1000给Bill,如果Andy帐户余额低于1000将不会通过事务处理
service.transfer(andy, bill, 1000);
// 从Bill帐户转出5500给Andy,因为转账额度大于中纪委规定的5000因此不会通过事务处理
service.transfer(bill, andy, 5500);
以上就是使用Spring进行事务管理的主要流程,根据应用的持久化机制的不同,Spring还提供了HibernateTransactionManger,JdoTransactionManger,OjbTransactionManger,JtaTransactionManger等事务管理器供用户选择,修改一下上面的transactionManager配置部分即可。
本例中使用到的代码下载:
http://www.blogjava.net/Files/heyang/SpringTransaction.rar
如果使用Hibernate作为程序持久介质请下载:
http://www.blogjava.net/Files/heyang/SpringHibernateTransaction.rar