1.Jdbc訪問(wèn)數(shù)據(jù)庫(kù)
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public int insertUser(User user){
String sqlStr = "insert into t_user (user_name,password,credits)values(?,?,?)";
return jdbcTemplate.update(sqlStr,new Object[]{user.getUserName()
,user.getPassword(),user.getCredits()});
}
}
在默認(rèn)情況下,dataSource數(shù)據(jù)源的autoCommit被設(shè)置為true,這也意味著所有通過(guò)jdbcTemplate執(zhí)行的語(yǔ)句馬上提交,沒(méi)有事務(wù)。所以不使用Spring事務(wù)也可以進(jìn)行數(shù)據(jù)庫(kù)操作。
配置Spring事務(wù)的時(shí)候首先要確定數(shù)據(jù)庫(kù)支持Spring事務(wù),如果使用MyISAM引擎的MySQL數(shù)據(jù)庫(kù),這時(shí)即使配置了,也沒(méi)有實(shí)際價(jià)值。
2.Hibernate訪問(wèn)數(shù)據(jù)庫(kù)
Hibernate的事務(wù)管理有其自身的意義,它和Hibernate一級(jí)緩存存在密切的關(guān)系,當(dāng)調(diào)用Session的save、update等方法時(shí),Hibernate并不直接向數(shù)據(jù)庫(kù)發(fā)送SQL語(yǔ)句,只在提交事務(wù)或flush一級(jí)緩存時(shí)才真正向數(shù)據(jù)庫(kù)發(fā)送SQL。
因此,數(shù)據(jù)庫(kù)不支持事務(wù),Hibernate的事務(wù)管理也有一定的好處,不會(huì)對(duì)數(shù)據(jù)庫(kù)的操作產(chǎn)生負(fù)面的影響。如果使用Hibernate的數(shù)據(jù)訪問(wèn)技術(shù),沒(méi)有理由不配置HibernateTransactionManager.
3.應(yīng)用分層
Spring框架所提供的各種好處(如AOP、注解增強(qiáng)、注解MVC)的唯一前提就是讓POJO的類變?yōu)橐粋€(gè)受Spring容器管理的Bean,除此之外沒(méi)有其他任何要求。因此一個(gè)類可以同時(shí)是Contorller,也可以注解為Service,也可以注解為Dao.
4.事務(wù)方法嵌套調(diào)用
- Spring事務(wù)傳播機(jī)制
-
Spring對(duì)事務(wù)控制的支持統(tǒng)一在TransactionDefinition類中描述。
事務(wù)的傳播行為 事務(wù)的隔離級(jí)別 事務(wù)的過(guò)期時(shí)間 事務(wù)的讀寫特性 -
事務(wù)的7種傳播行為
事務(wù)默認(rèn)的PROPAGATION_REQUIRED適合絕代大多數(shù)的情況,如果存在事務(wù)就加入 如果不存在事務(wù)就創(chuàng)建
-
- 相互嵌套的方法
- 多個(gè)方法嵌套,事務(wù)直接合并。
5.多線程事務(wù)問(wèn)題
- 由于Spring管理器是通過(guò)線程相關(guān)的ThreadLocal來(lái)保存數(shù)據(jù)訪問(wèn)的基礎(chǔ)設(shè)施(Connection實(shí)例),在結(jié)合IOC和AOP實(shí)現(xiàn)高級(jí)聲明式事務(wù)的的功能,所以Spring的事務(wù)天然的和事務(wù)有千絲萬(wàn)縷的聯(lián)系。
- 在Web程序本身就是多線程的,Web容器為一個(gè)HTTP請(qǐng)求創(chuàng)建一個(gè)獨(dú)立的線程(實(shí)際上使用的是共享的線程池)。所以有此請(qǐng)求鎖牽涉到的Spring容器中的Bean也運(yùn)行與多線程的環(huán)境下。
- 在多數(shù)情況下Spring的Bean都是單實(shí)例的(singleton),單實(shí)例Bean的最大好處就是線程無(wú)關(guān)性,不存在多線程并發(fā)的問(wèn)題,也就是線程安全的。
- 一個(gè)類能夠以單實(shí)例的方式運(yùn)行的前提是“無(wú)狀態(tài)”;即一個(gè)類不能擁有狀態(tài)話的成員變量。我們知道,在傳統(tǒng)的編程中。Dao必須持有一個(gè)Connection,而Connection即是狀態(tài)化的對(duì)象。所以傳統(tǒng)的DAO不能做成單實(shí)例的,每次要用時(shí)都必須創(chuàng)建一個(gè)新的實(shí)例。傳統(tǒng)的Service由于內(nèi)部包含了若干個(gè)有狀態(tài)的DAO成員變量,所以基本也是有狀態(tài)的。
- 但在Spring中,Dao和Service都以單實(shí)例的方式存在。Spring是通過(guò)ThreadLocal將有狀態(tài)的變量(Connection)本地線程化,達(dá)到另一個(gè)層面上的“線程無(wú)關(guān)”,從而實(shí)現(xiàn)線程安全。Spring不予余力將有狀態(tài)的對(duì)象無(wú)狀態(tài)化,就是達(dá)到單實(shí)例化Bean的目的。
- Spring中事務(wù)時(shí)線程獨(dú)立的。在相同的線程進(jìn)行相互嵌套調(diào)用的事務(wù)方法工作于相同的事務(wù)中。如果這些相互前臺(tái)的方法工作在不同的線程中,則不同線程下的事務(wù)方法工作在獨(dú)立的事務(wù)中。
6.Spring應(yīng)對(duì)混合框架的事務(wù)管理
- Spring中底層的ORM框架同底層JDBC技術(shù)的整合使用。Spring事務(wù)對(duì)每種數(shù)據(jù)訪問(wèn)技術(shù)提供了響應(yīng)的事務(wù)管理器,但是Spring當(dāng)遇到使用高端的ORM(Hibernate,JPA,JDO)框架,同時(shí)采用一個(gè)JDBC技術(shù)(SpringJDBC,Ibatis)時(shí),由于前者的會(huì)話Session是對(duì)后者連接(Connection)的封裝,Spring會(huì)足夠只能的在同一個(gè)事物線程讓前者的會(huì)話封裝后者的連接。我們只要直接采用前者的事務(wù)管理器就可以。
- 但在使用Hibernate技術(shù)時(shí),Session的提交不是真正的提交。
7.不能實(shí)施Spring事務(wù)的方法
- Spring事務(wù)管理是基于接口代理或動(dòng)態(tài)字節(jié)碼技術(shù),通過(guò)AOP試試事務(wù)增強(qiáng)的。
- 對(duì)基于接口動(dòng)態(tài)代理的AOP事務(wù)增強(qiáng)來(lái)說(shuō),由于接口的方法必然是public的,這就要求實(shí)現(xiàn)類的實(shí)現(xiàn)方法也必須是public的,不能是protected或者private等,同時(shí)不能使用static的修飾符。所以實(shí)施接口代理的方法只能使用public或public final修飾符的方法,其他方法不可能被動(dòng)態(tài)代理,響應(yīng)的也就不能實(shí)施AOP增強(qiáng),換句話話說(shuō),即不能進(jìn)行Spring事務(wù)增強(qiáng)。
- 基于CGLib字節(jié)碼動(dòng)態(tài)代理的方案是通過(guò)擴(kuò)展被增強(qiáng)的類,動(dòng)態(tài)創(chuàng)建子類的方式進(jìn)行AOP增強(qiáng)織入的。由于使用final,static ,private 修飾符的方法都不能被子類覆蓋,響應(yīng)的,這些方法將無(wú)法實(shí)施AOP增強(qiáng)。所以方法名必須特別注意這些修飾符的使用,以免方法不小心稱為事務(wù)管理的漏網(wǎng)之魚(yú)。
- 這里不能被Spring事務(wù)增強(qiáng)的方法和可被Spring事務(wù)增強(qiáng)的方法唯一的區(qū)別在于使否可主動(dòng)啟動(dòng)一個(gè)新事物;不被Spring事務(wù)增強(qiáng)的方法是不能啟動(dòng)一個(gè)新事務(wù)的,只有被Spring事務(wù)增強(qiáng)的方法才能主動(dòng)啟動(dòng)一個(gè)新事務(wù)。對(duì)于事務(wù)傳播來(lái)說(shuō),二者是相同的,兩者都不會(huì)造成數(shù)據(jù)連接的泄露問(wèn)題。如果這些“特殊方法”被無(wú)事務(wù)上下文的方法調(diào)用,則他們就工作者無(wú)事務(wù)上下文中;反之,如果被具有事務(wù)事務(wù)上下文的方法調(diào)用,則他們就工作在事務(wù)的上下文中。
- 對(duì)于private方法,由于最終都會(huì)被Public方法封裝后再開(kāi)始被外部調(diào)用。而public方法都是被事務(wù)增強(qiáng)的,所以基本上沒(méi)有什么問(wèn)題。在實(shí)際的開(kāi)發(fā)中,最容易造成隱患的是基于CGLib的動(dòng)態(tài)代理是的“public static”和public final這兩種特殊方法。原因是他們本身是public的。因此可以直接被外部類調(diào)用。只要調(diào)用者沒(méi)有事務(wù)上下文,這些特殊的方法就運(yùn)行在沒(méi)有事務(wù)的。
8.數(shù)據(jù)連接泄露問(wèn)題
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
- Spring提供一個(gè)能從當(dāng)前事務(wù)上線文中獲取綁定數(shù)據(jù)連接的工具,以上為DataSourceUtils。Spring強(qiáng)調(diào)使用DataSourceUtils工具獲取連接,Spring的JdbcTemplate內(nèi)部也是通過(guò)DataSourceUtils來(lái)獲得連接的。DataSourceUtils提供了釋放連接的實(shí)現(xiàn)。
- 但如果DataSourceUtils沒(méi)有在事務(wù)的上下文的方法中使用getConnection獲取連接,依然會(huì)造成數(shù)據(jù)連接泄露。