java簡單項目案例,java中使用事務案例_Java事務之四——成功的案例

 2023-10-20 阅读 31 评论 0

摘要:在本系列的上一篇文章中我們講到,要實現在同一個事務中使用相同的Connection對象,我們可以通過傳遞Connection對象的方式達到共享的目的,但是這種做法是丑陋的。在本篇文章中,我們將引入另外一種機制(ConnectionHolder)來完成事務管理。這是一個關

在本系列的上一篇文章中我們講到,要實現在同一個事務中使用相同的Connection對象,我們可以通過傳遞Connection對象的方式達到共享的目的,但是這種做法是丑陋的。在本篇文章中,我們將引入另外一種機制(ConnectionHolder)來完成事務管理。

這是一個關于Java事務處理的系列文章,請通過以下方式下載github源代碼:

java簡單項目案例,git clone https://github.com/davenkin/java_transaction_workshop.git

ConnectionHolder的工作機制是:我們將Connection對象放在一個全局公用的地方,然后在不同的操作中都從這個地方取得Connection,從而完成Connection共享的目的,這也是一種ServiceLocator模式,有點像JNDI。定義一個ConnectionHolder類如下:

packagedavenkin.step3_connection_holder;importjavax.sql.DataSource;importjava.sql.Connection;importjava.sql.SQLException;importjava.util.HashMap;importjava.util.Map;public classConnectionHolder

泛型java,{private Map connectionMap = new HashMap();public Connection getConnection(DataSource dataSource) throwsSQLException

{

Connection connection=connectionMap.get(dataSource);if (connection == null ||connection.isClosed())

java如何控制事務、{

connection=dataSource.getConnection();

connectionMap.put(dataSource, connection);

java后端、}returnconnection;

}public voidremoveConnection(DataSource dataSource)

{

java事務組成的五部分、connectionMap.remove(dataSource);

}

}

從ConnectionHolder類中可以看出,我們維護了一個鍵為DataSource、值為Connection的Map,這主要用于使ConnectionHolder可以服務多個DataSource。在調用getConnection方法時傳入了一個DataSource對象,如果Map里面已經存在該DataSource對應的Connection,則直接返回該Connection,否則,調用DataSource的getConnection方法獲得一個新的Connection,再將其加入到Map中,最后返回該Connection。這樣在同一個事務過程中,我們先后從ConnectionHolder中取得的Connection是相同的,除非在中途我們調用了ConnectionHolder的removeConnection方法將當前Connection移除掉或者調用了Connection.close()將Connection關閉,然后在后續的操作中再次調用ConnectionHolder的getConnection方法,此時返回的則是一個新的Connection對象,從而導致事務處理失敗,你應該不會做出這種中途移除或關閉Connection的事情。

然而,雖然我們不會自己手動地在中途移除或者關閉Conncetion對象(當然,在事務處理末尾我們應該關閉Conncetion),我們卻無法阻止其他線程這么做。比如,ConnectionHolder類是可以在多個線程中同時使用的,并且這些線程使用了同一個DataSource,其中一個線程使用完Connection后便將其關閉,而此時另外一個線程正試圖使用這個Connection,問題就出來了。因此,上面的ConnectionHolder不是線程安全的。

為了獲得線程安全的ConnectionHolder類,我們可以引入Java提供的ThreadLocal類,該類保證一個類的實例變量在各個線程中都有一份單獨的拷貝,從而不會影響其他線程中的實例變量。定義一個SingleThreadConnectionHolder類如下:

packagedavenkin.step3_connection_holder;importjavax.sql.DataSource;importjava.sql.Connection;importjava.sql.SQLException;public classSingleThreadConnectionHolder

{private static ThreadLocal localConnectionHolder = new ThreadLocal();public static Connection getConnection(DataSource dataSource) throwsSQLException

{returngetConnectionHolder().getConnection(dataSource);

}public static voidremoveConnection(DataSource dataSource)

{

getConnectionHolder().removeConnection(dataSource);

}private staticConnectionHolder getConnectionHolder()

{

ConnectionHolder connectionHolder=localConnectionHolder.get();if (connectionHolder == null)

{

connectionHolder= newConnectionHolder();

localConnectionHolder.set(connectionHolder);

}returnconnectionHolder;

}

}

有了一個線程安全的SingleThreadConnectionHolder類,我們便可以在service層和各個DAO中使用該類來獲取Connection對象:

Connection connection = SingleThreadConnectionHolder.getConnection(dataSource);

當然,此時我們需要傳入一個DataSource,這個DataSource可以作為DAO類的實例變量存在,所以我們不用像上一篇文章那樣將Connection對象直接傳給DAO的方法。這里你可能要問,既然可以將DataSource作為實例變量,那么在上一篇文章中,為什么不可以將Connection也作為實例變量呢,這樣不就不會造成丑陋的API了嗎?原因在于:將Connection對象作為實例變量同樣會帶來線程安全問題,當多個線程同時使用同一個DAO類時,一個線程關閉了Connection而另一個正在使用,這樣的問題和上面講到的ConnectionHolder的線程安全問題一樣。

關于Bank DAO和Insurance DAO類的源代碼這里就不列出了,他們和上篇文章只是獲得Connection對象的方法不一樣而已,你可以參考github源代碼。

接下來,我們再來看看TransactionManager類,在上幾篇文章中,我們都是在service類中直接寫和事務處理相關的代碼,而更好的方式是聲明一個TransactionManger類將事務處理相關工作集中管理:

packagedavenkin.step3_connection_holder;importjavax.sql.DataSource;importjava.sql.Connection;importjava.sql.SQLException;public classTransactionManager

{privateDataSource dataSource;publicTransactionManager(DataSource dataSource)

{this.dataSource =dataSource;

}public final void start() throwsSQLException

{

Connection connection=getConnection();

connection.setAutoCommit(false);

}public final void commit() throwsSQLException

{

Connection connection=getConnection();

connection.commit();

}public final voidrollback()

{

Connection connection= null;try{

connection=getConnection();

connection.rollback();

}catch(SQLException e)

{throw new RuntimeException("Couldn't rollback on connection[" + connection + "].", e);

}

}public final voidclose()

{

Connection connection= null;try{

connection=getConnection();

connection.setAutoCommit(true);

connection.setReadOnly(false);

connection.close();

SingleThreadConnectionHolder.removeConnection(dataSource);

}catch(SQLException e)

{throw new RuntimeException("Couldn't close connection[" + connection + "].", e);

}

}private Connection getConnection() throwsSQLException

{returnSingleThreadConnectionHolder.getConnection(dataSource);

}

}

可以看出,TransactionManager對象也維護了一個DataSource實例變量,并且也是通過SingleThreadConnectionHolder來獲取Connection對象的。然后我們在service類中使用該TransactionManager:

packagedavenkin.step3_connection_holder;importdavenkin.BankService;importjavax.sql.DataSource;public class ConnectionHolderBankService implementsBankService

{privateTransactionManager transactionManager;privateConnectionHolderBankDao connectionHolderBankDao;privateConnectionHolderInsuranceDao connectionHolderInsuranceDao;publicConnectionHolderBankService(DataSource dataSource)

{

transactionManager= newTransactionManager(dataSource);

connectionHolderBankDao= newConnectionHolderBankDao(dataSource);

connectionHolderInsuranceDao= newConnectionHolderInsuranceDao(dataSource);

}public void transfer(int fromId, int toId, intamount)

{try{

transactionManager.start();

connectionHolderBankDao.withdraw(fromId, amount);

connectionHolderInsuranceDao.deposit(toId, amount);

transactionManager.commit();

}catch(Exception e)

{

transactionManager.rollback();

}finally{

transactionManager.close();

}

}

}

在ConnectionHolderBankService中,我們使用TransactionManager來管理事務,由于TransactionManger和兩個DAO類都是使用SingleThreadConnectionHolder來獲取Connection,故他們在整個事務處理過程中使用了相同的Connection對象,事務處理成功。我們也可以看到,在兩個DAO的withdraw和deposit方法沒有接受和業務無關的對象,消除了API污染;另外,使用TransactionManager來管理事務,使Service層代碼也變簡潔了。

在下一篇文章中,我們將講到使用Template模式來完成事務處理。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/150984.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息