問(wèn)題背景:我們是一家工作流公司,客戶(hù)采購(gòu)我們的產(chǎn)品后,將其嵌入其項(xiàng)目中。我們的工作流采用的是 spring+hibernate的方式,客戶(hù)項(xiàng)目則是jdbc直接進(jìn)行數(shù)據(jù)庫(kù)操作。
問(wèn)題:客戶(hù)在其數(shù)據(jù)庫(kù)操作過(guò)程中需要調(diào)用我們的工作流接口,這樣就需要將我們的工作流操作與他們的業(yè) 務(wù)操作置于同一個(gè)事務(wù)中。我們的服務(wù)采用的都是spring的聲明式事務(wù),而客戶(hù)采用的是對(duì) connection進(jìn)行事務(wù)處理。如何保證事務(wù)的一致性?
想到的解決方案一:使用jta事務(wù),用tomcat+jotm提供事務(wù)管理器。為什么一開(kāi)始就想到要使用jta事務(wù)??實(shí)際上我們和客戶(hù)都是使用的同一個(gè)數(shù)據(jù)庫(kù),為了方便,各自使用了不同的數(shù)據(jù)庫(kù)連接方式,使用jta的話確實(shí)有bt的意思在里面。但是事實(shí)上是我們的第一反應(yīng)都是jta。最后沒(méi)有采用該方法的原因也很簡(jiǎn)單:我沒(méi)有將jotm配置成功!汗一個(gè)。
想到的解決方案二:將客戶(hù)的這些特定代碼用spring管理起來(lái)。因?yàn)橐薷目蛻?hù)部分代碼,這個(gè)方案遭到了客戶(hù)的強(qiáng)烈反對(duì)。于是放棄。
想到的解決方案三:客戶(hù)數(shù)據(jù)庫(kù)操作與我們的服務(wù)使用同一個(gè)數(shù)據(jù)庫(kù)連接。然后編程處理事務(wù)。存在兩種方式:一種是把客戶(hù)的連接傳給我們,另一種則是把我們的連接傳給客戶(hù)。第一種方式對(duì)我們的影響太大,所以最后決定采用后一種方式:從hibernate session中獲取connection然后傳遞給客戶(hù)。接下來(lái)查看一下HibernateTemplate的execute()方法,思路就很簡(jiǎn)單了:獲取定義的sessionFactory-->創(chuàng)建一個(gè)新的session并打開(kāi)-->將session與當(dāng)前線程綁定-->給客戶(hù)代碼返回connection-->打開(kāi)事務(wù)-->客戶(hù)使用我們傳遞的connection進(jìn)行數(shù)據(jù)庫(kù)操作-->我們不帶聲明事務(wù)的服務(wù)操作-->提交事務(wù)-->解除綁定。
實(shí)際要注意的地方是:1、將session與當(dāng)前線程綁定使用的TransactionSynchronizationManager.bindResource()方法,這樣在HibernateTemplate里才能找到session;
2、我們的服務(wù)一定要把聲明式事務(wù)徹底干掉,否則會(huì)有commit;
3、我們服務(wù)調(diào)用完畢后一定要flush session,否則客戶(hù)代碼不會(huì)感知數(shù)據(jù)庫(kù)里的數(shù)據(jù)變化。
最終解決:使用了spring里常用的模板和回調(diào)。代碼如下:
public class TransactionTemplate {
protected final Log logger = LogFactory.getLog(TransactionTemplate.class);
private FlushMode flushMode = FlushMode.ALWAYS;
public Object execute(TransactionCallback callback) {
//首先獲取sessionFactory
SessionFactory sessionFactory = (SessionFactory) Framework.getEngine()
.getContainer().getComponent("sessionFactory");
//創(chuàng)建一個(gè)新的session并打開(kāi)
logger.debug("Opening single Hibernate Session in TransactionTemplate");
Session session = getSession(sessionFactory);
//將session與當(dāng)前線程綁定
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
//獲取數(shù)據(jù)庫(kù)連接
Connection conn = session.connection();
Object result = null;
Transaction transaction = null;
try {
//開(kāi)始處理事務(wù)
transaction = session.beginTransaction();
try {
result = callback.doInTransaction(conn);
}
catch (RuntimeException ex) {
doRollback(session, transaction);
throw ex;
}
catch (Error err) {
doRollback(session, transaction);
throw err;
}
//如果數(shù)據(jù)庫(kù)操作過(guò)程中沒(méi)有發(fā)生異常則提交事務(wù)
transaction.commit();
} catch (WorkflowException e) {
logger.error("數(shù)據(jù)庫(kù)操作失敗,事務(wù)回滾也失敗!");
throw e;
} catch (RuntimeException ex) {
logger.error("數(shù)據(jù)庫(kù)操作失敗,事務(wù)被回滾!");
throw ex;
} catch (Error err) {
logger.error("數(shù)據(jù)庫(kù)操作失敗,事務(wù)被回滾!");
throw err;
} finally {
// 將session與當(dāng)前線程解除綁定
TransactionSynchronizationManager.unbindResource(sessionFactory);
doClose(session);
}
return result;
}
protected Session getSession(SessionFactory sessionFactory) {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = getFlushMode();
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
private void doRollback(Session session, Transaction transaction) {
logger.debug("數(shù)據(jù)庫(kù)操作異常,開(kāi)始回滾事務(wù)");
try {
transaction.rollback();
logger.debug("回滾事務(wù)成功!");
}
catch (Exception e) {
logger.error("回滾事務(wù)失敗!");
throw new WorkflowException("回滾事務(wù)失敗!");
} finally {
session.clear();
}
}
private void doClose(Session session) {
logger.debug("開(kāi)始關(guān)閉連接");
try {
session.close();
}
catch (Exception e) {
logger.error("關(guān)閉連接失敗!");
throw new WorkflowException("關(guān)閉連接失敗!");
}
}
public FlushMode getFlushMode() {
return flushMode;
}
public void setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
}
}
public interface TransactionCallback {
Object doInTransaction(Connection conn);
}
調(diào)用偽代碼:
public void methodA(){
TransactionTemplate transactionTemplate=new TransactionTemplate();
transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(Connection conn) {
//客戶(hù)代碼
client.method1("1");
//我們代碼 直接使用
our.method2();
//客戶(hù)代碼
client.method3("l");
return null;
}
});
}
http://www.tkk7.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處:)
posted on 2007-10-09 15:11
ronghao 閱讀(7741)
評(píng)論(5) 編輯 收藏 所屬分類(lèi):
工作日志