程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 淺談如何結合JDBC事務與Spring+Hibernate

淺談如何結合JDBC事務與Spring+Hibernate

編輯:關於JAVA

問題:客戶在其數據庫操作過程中需要調用我們的工作流接口,這樣就需要將我們的工作流操作與他 們的業  務操作置於同一個事務中。我們的服務采用的都是spring的聲明式事務,而客戶采用的是對 connection進行事務處理。

如何保證JDBC事務的一致性?

想到的解決方案一:使用jta事務,用tomcat+jotm提供事務管理器。為什麼一開始就想到要使用jta事 務??實際上我們和客戶都是使用的同一個數據庫,為了方便,各自使用了不同的數據庫連接方式,使用 jta的話確實有bt的意思在裡面。但是事實上是我們的第一反應都是jta。最後沒有采用該方法的原因也很 簡單:我沒有將jotm配置成功!汗一個。

想到的解決方案二:將客戶的這些特定代碼用spring管理起來。因為要修改客戶部分代碼,這個方案 遭到了客戶的強烈反對。於是放棄。

想到的解決方案三:客戶數據庫操作與我們的服務使用同一個數據庫連接。然後編程處理事務。存在 兩種方式:一種是把客戶的連接傳給我們,另一種則是把我們的連接傳給客戶。第一種方式對我們的影響 太大,所以最後決定采用後一種方式:從hibernate session中獲取connection然後傳遞給客戶。接下來 查看一下HibernateTemplate的execute()方法,思路就很簡單了:獲取定義的sessionFactory-->創建 一個新的session並打開-->將session與當前線程綁定-->給客戶代碼返回connection-->打開事 務-->客戶使用我們傳遞的connection進行數據庫操作-->我們不帶聲明事務的服務操作-->提交 事務-->解除綁定。

JDBC事務實際要注意的地方是:

1、將session與當前線程綁定使用的TransactionSynchronizationManager.bindResource()方法,這 樣在HibernateTemplate裡才能找到session;

2、我們的服務一定要把聲明式事務徹底干掉,否則會有commit;

3、我們服務調用完畢後一定要flush session,否則客戶代碼不會感知數據庫裡的數據變化。

最終解決:使用了spring裡常用的模板和回調。JDBC事務代碼如下:

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");  
        //創建一個新的session並打開  
        logger.debug("Opening single Hibernate Session in 

TransactionTemplate");  
        Session session = getSession(sessionFactory);  
        //將session與當前線程綁定  
        TransactionSynchronizationManager.bindResource(sessionFactory, new 

SessionHolder(session));  
        //獲取數據庫連接  
        Connection conn = session.connection();  
        Object result = null;  
        Transaction transaction = null;  
        try {  
            //開始處理JDBC事務  
            transaction = session.beginTransaction();  
            try {  
                result = callback.doInTransaction(conn);  
            }  
            catch (RuntimeException ex) {  
                doRollback(session, transaction);  
                throw ex;  
            }  
            catch (Error err) {  
                doRollback(session, transaction);  
                throw err;  
            }  
            //如果數據庫操作過程中沒有發生異常則提交事務  
            transaction.commit();  
        } catch (WorkflowException e) {  
            logger.error("數據庫操作失敗,事務回滾也失敗!");  
            throw e;  
        } catch (RuntimeException ex) {  
            logger.error("數據庫操作失敗,事務被回滾!");  
            throw ex;  
        } catch (Error err) {  
            logger.error("數據庫操作失敗,事務被回滾!");  
            throw err;  
        } finally {  
            // 將session與當前線程解除綁定  
            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("數據庫操作異常,開始回滾事務");  
        try {  
            transaction.rollback();  
            logger.debug("回滾事務成功!");  
        }  
        catch (Exception e) {  
            logger.error("回滾事務失敗!");  
            throw new WorkflowException("回滾事務失敗!");  
        } finally {  
            session.clear();  
        }  
    }  
 
    private void doClose(Session session) {  
        logger.debug("開始關閉連接");  
        try {  
            session.close();  
        }  
        catch (Exception e) {  
            logger.error("關閉連接失敗!");  
            throw new WorkflowException("關閉連接失敗!");  
        }  
    }  
    public FlushMode getFlushMode() {  
        return flushMode;  
    }  
 
    public void setFlushMode(FlushMode flushMode) {  
        this.flushMode = flushMode;  
    }  
}  
public interface TransactionCallback {  
 
    Object doInTransaction(Connection conn);  
}

調用偽代碼:  

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;    
            }  
        });  
    }
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved