淺談如何結(jié)合JDBC事務(wù)與Spring+Hibernate
問題:客戶在其數(shù)據(jù)庫操作過程中需要調(diào)用我們的工作流接口,這樣就需要將我們的工作流操作與他們的業(yè) 務(wù)操作置于同一個(gè)事務(wù)中。我們的服務(wù)采用的都是spring的聲明式事務(wù),而客戶采用的是對connection進(jìn)行事務(wù)處理。
如何保證JDBC事務(wù)的一致性?
想到的解決方案一:使用jta事務(wù),用tomcat+jotm提供事務(wù)管理器。為什么一開始就想到要使用jta事務(wù)??實(shí)際上我們和客戶都是使用的同一個(gè)數(shù)據(jù)庫,為了方便,各自使用了不同的數(shù)據(jù)庫連接方式,使用jta的話確實(shí)有bt的意思在里面。但是事實(shí)上是我們的第一反應(yīng)都是jta。最后沒有采用該方法的原因也很簡單:我沒有將jotm配置成功!汗一個(gè)。
想到的解決方案二:將客戶的這些特定代碼用spring管理起來。因?yàn)橐薷目蛻舨糠执a,這個(gè)方案遭到了客戶的強(qiáng)烈反對。于是放棄。
想到的解決方案三:客戶數(shù)據(jù)庫操作與我們的服務(wù)使用同一個(gè)數(shù)據(jù)庫連接。然后編程處理事務(wù)。存在兩種方式:一種是把客戶的連接傳給我們,另一種則是把我們的連接傳給客戶。第一種方式對我們的影響太大,所以最后決定采用后一種方式:從hibernate session中獲取connection然后傳遞給客戶。接下來查看一下HibernateTemplate的execute()方法,思路就很簡單了:獲取定義的sessionFactory-->創(chuàng)建一個(gè)新的session并打開-->將session與當(dāng)前線程綁定-->給客戶代碼返回connection-->打開事務(wù)-->客戶使用我們傳遞的connection進(jìn)行數(shù)據(jù)庫操作-->我們不帶聲明事務(wù)的服務(wù)操作-->提交事務(wù)-->解除綁定。
JDBC事務(wù)實(shí)際要注意的地方是:
1、將session與當(dāng)前線程綁定使用的TransactionSynchronizationManager.bindResource()方法,這樣在HibernateTemplate里才能找到session;
2、我們的服務(wù)一定要把聲明式事務(wù)徹底干掉,否則會(huì)有commit;
3、我們服務(wù)調(diào)用完畢后一定要flush session,否則客戶代碼不會(huì)感知數(shù)據(jù)庫里的數(shù)據(jù)變化。
最終解決:使用了spring里常用的模板和回調(diào)。JDBC事務(wù)代碼如下:
- 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并打開
- logger.debug("Opening single Hibernate Session in TransactionTemplate");
- Session session = getSession(sessionFactory);
- //將session與當(dāng)前線程綁定
- TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
- //獲取數(shù)據(jù)庫連接
- Connection conn = session.connection();
- Object result = null;
- Transaction transaction = null;
- try {
- //開始處理JDBC事務(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ù)庫操作過程中沒有發(fā)生異常則提交事務(wù)
- transaction.commit();
- } catch (WorkflowException e) {
- logger.error("數(shù)據(jù)庫操作失敗,事務(wù)回滾也失?。?);
- throw e;
- } catch (RuntimeException ex) {
- logger.error("數(shù)據(jù)庫操作失敗,事務(wù)被回滾!");
- throw ex;
- } catch (Error err) {
- logger.error("數(shù)據(jù)庫操作失敗,事務(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ù)庫操作異常,開始回滾事務(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("開始關(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) {
- //客戶代碼
- client.method1("1");
- //我們代碼 直接使用
- our.method2();
- //客戶代碼
- client.method3("l");
- return null;
- }
- });
- }
【編輯推薦】