在數(shù)據(jù)庫操作中,一項事務(wù)是由一條或者多條表達式所組成的一個不可分割的工作單元。
事務(wù)必須遵循ACID原則,即:
原子性(Atomicity):事務(wù)必須完全執(zhí)行或者完全不執(zhí)行,任何任務(wù)失敗都將導(dǎo)致整個事務(wù)回滾。
一致性(Consistency):指原始數(shù)據(jù)庫的完整性,事務(wù)系統(tǒng)通過確保事務(wù)是原子性,隔離性和持續(xù)性來實現(xiàn)一致性。
隔離性(Isolation):事物的執(zhí)行必須不受其它進程或者事務(wù)的干擾。
持續(xù)性(Durabilty):意味著所有事務(wù)過程中的數(shù)據(jù)更改在事務(wù)成功之前必須寫入某些物理介質(zhì),確保系統(tǒng)崩潰時數(shù)據(jù)更改不會丟失。
JDBC事務(wù)管理
JDBC中引入一批語句對事務(wù)進行支持,它們是:
Connection.setAutoCommit(True or fasle);設(shè)置事務(wù)是否自動提交,它的參數(shù)缺省是true.要進行事務(wù)處理的話,參數(shù)應(yīng)該設(shè)置為false.
Connection.commit():從setAutoCommit語句開始,到commit語句之間算作一個數(shù)據(jù)庫事務(wù),其中的數(shù)據(jù)庫操作要么全成功要么全失敗.所有在調(diào)用commit方法之前的SQL語句都可以被回滾,然而,一旦commit()方法被調(diào)用,執(zhí)行過的SQL語句就不能再回滾.
Rollback():事務(wù)回滾,如果事務(wù)中有一個步驟出現(xiàn)錯誤,那么調(diào)用這個方法將使數(shù)據(jù)庫恢復(fù)到執(zhí)行事務(wù)之前的狀態(tài),這樣,事務(wù)的原子性就得到了保證.
下面是使用事務(wù)處理的例程:
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事務(wù)管理的問題
以上事務(wù)處理代碼是以數(shù)據(jù)庫的記錄為核心來考慮的,即使用Java代碼通過JDBC和SQL語句直接操作數(shù)據(jù)庫中的記錄,這種傳統(tǒng)方式在現(xiàn)代程序中已經(jīng)不多見,現(xiàn)代程序多把記錄歸納成領(lǐng)域?qū)ο螅缓笫褂妙I(lǐng)域?qū)ο髮?yīng)的DAO來完成領(lǐng)域?qū)ο笈c數(shù)據(jù)庫之間的交互。除了DAO層外,其它層次不與數(shù)據(jù)庫發(fā)生聯(lián)系。因此,上面的直接使用JDBC進行事務(wù)處理就不適用了,試想AccountService中有一個方法執(zhí)行兩個帳戶之間的轉(zhuǎn)賬,這個方法要具備事務(wù)功能必須繞過DAO層書寫底層的JDBC代碼,這是層次明晰紀律嚴明的系統(tǒng)所不能接受的,好在我們有Spring的事務(wù)處理可以幫我們擺脫兩難境地。
使用Spring進行事務(wù)處理
Spring對程序控制事務(wù)管理的支持和EJB有很大不同,EJB的事務(wù)管理和JTA密不可分,而Spring使用了一種回調(diào)機制,把真實的食物實現(xiàn)從事務(wù)代碼中抽象出來。實際上Spring的事務(wù)管理甚至不需要JTA,它也可以使用持久化機制本身多提供的事務(wù)管理支持。下面就是使用JDBC作為應(yīng)用的持久化機制的事務(wù)管理示例代碼:
首先,在上下文定義文件中寫入JDBC事務(wù)管理器的bean定義。
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
它需要一個數(shù)據(jù)源的支持,數(shù)據(jù)源定義示例如下:
<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事務(wù)管理器會作為事務(wù)管理模板的一個屬性注入進入。
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
</bean>
而事務(wù)管理模板又作為一個屬性注入到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就具備了事務(wù)處理功能,下面轉(zhuǎn)賬函數(shù)的具體代碼:

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("轉(zhuǎn)出的帳戶"+from+"余額不足");
}

if(count>5000)
{
throw new Exception("轉(zhuǎn)出的金額超過了中紀委限定的額度5000");
}
// 轉(zhuǎn)出帳戶減去轉(zhuǎn)賬金額
from.setCount(from.getCount()-count);
update(from);
// 轉(zhuǎn)入帳戶加上轉(zhuǎn)賬金額
to.setCount(to.getCount()+count);
update(to);
}

catch(Exception ex)
{
ex.printStackTrace();
// 出現(xiàn)任何異常即回滾
ts.setRollbackOnly();
}
// 如果成功,事務(wù)被提交
return null;
}
}
);
}

以上代碼中,執(zhí)行轉(zhuǎn)賬的業(yè)務(wù)代碼放在try塊中,如果全部代碼執(zhí)行成功,事務(wù)將會被提交;如果如下任何異常,事務(wù)會被回滾,出現(xiàn)異常前的任何修改點將被恢復(fù)到初始狀態(tài)。
下面是模擬轉(zhuǎn)賬的具體代碼:
ApplicationContext context=new ClassPathXmlApplicationContext("appCtx.xml");

AccountService service=(AccountService)context.getBean("accountService");

// 準備測試數(shù)據(jù)

/**//* 建表語句如下
* 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);

// 從數(shù)據(jù)庫取得帳戶Andy,11是其ID
Account andy=service.getAccount("11");
Account bill=service.getAccount("12");

// 從Andy帳戶轉(zhuǎn)出4000給Bill,如果Andy帳戶余額低于4000將不會通過事務(wù)處理
service.transfer(andy, bill, 4000);

// 從Andy帳戶轉(zhuǎn)出1000給Bill,如果Andy帳戶余額低于1000將不會通過事務(wù)處理
service.transfer(andy, bill, 1000);

// 從Bill帳戶轉(zhuǎn)出5500給Andy,因為轉(zhuǎn)賬額度大于中紀委規(guī)定的5000因此不會通過事務(wù)處理
service.transfer(bill, andy, 5500);
以上就是使用Spring進行事務(wù)管理的主要流程,根據(jù)應(yīng)用的持久化機制的不同,Spring還提供了HibernateTransactionManger,JdoTransactionManger,OjbTransactionManger,JtaTransactionManger等事務(wù)管理器供用戶選擇,修改一下上面的transactionManager配置部分即可。
本例中使用到的代碼下載:
http://www.tkk7.com/Files/heyang/SpringTransaction.rar
如果使用Hibernate作為程序持久介質(zhì)請下載:
http://www.tkk7.com/Files/heyang/SpringHibernateTransaction.rar