步驟一、在spring配置文件中引入命名空間
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
步驟二、配置事務管理和啟用事務注解驅動
1、只有一個事務
xml配置:
<!– mysql事務管理器 –>
< bean id=”transactionManager”
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>
< property name=”dataSource” ref=”dataSource”></property>
</bean>
<!– 對標注transactional的bean或方法進行事務處理 –>
< tx:annotation-driven transaction-manager=”transactionManager” />
dao事務注解:
//@Transactional(value=transactionManager)
@Transactional
public class UserScoreRepositoryImpl implements UserScoreRepository {
private JdbcTemplate jdbcTemplate;
@Override
public UserScore getUserSocore(String userNo) {
final UserScore us = new UserScore();
...
return us;
}
...
}
2、多事務
xml:
<bean id="transactionManagerX"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource">
<qualifier value="tran_1"/>
</bean>
<bean id="transactionManagerY"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource">
<qualifier value="tran_2"/>
</bean>
<tx:annotation-driven transaction-manager=”transactionManagerX” />
< tx:annotation-driven transaction-manager=”transactionManagerY” />
dao注解:
public class MultiTxService {
@Transactional(“transactionManagerX”)
//@Transactional(“tran_1″)
public void addTest(int id){
}
@Transactional(“transactionManagerY”)
//@Transactional(“tran_2″)
public void deleteTest(int id){
}
}
注:
1、一共有四個屬性如下,
mode:指定Spring事務管理框架創建通知bean的方式。可用的值有proxy和aspectj。前者是默認值,表示通知對象是個JDK代理;後者表示Spring AOP會使用AspectJ創建代理
proxy-target-class:如果為true,Spring將創建子類來代理業務類;如果為false,則使用基於接口的代理。(如果使用子類代理,需要在類路徑中添加CGLib.jar類庫)
order:如果業務類除事務切面外,還需要織入其他的切面,通過該屬性可以控制事務切面在目標連接點的織入順序。
transaction-manager:指定到現有的PlatformTransaction Manager bean的引用,通知會使用該引用
2、@Transactional標注的位置
@Transactional注解可以標注在類和方法上,也可以標注在定義的接口和接口方法上。
如果我們在接口上標注@Transactional注解,會留下這樣的隱患:因為注解不能被繼承,所以業務接口中標注的@Transactional注解不會被業務實現類繼承。所以可能會出現不啟動事務的情況。所以,Spring建議我們將@Transaction注解在實現類上。
在方法上的@Transactional注解會覆蓋掉類上的@Transactional。
3、
1.當在@Transactional(“xxx”)中正確指定了需要使用的事務管理器時,事務控制正常。
2.如果@Transactional指定了未定義過的事務管理器,spring以缺省默認的事務管理器來處理。(如果程序正好使用的是缺省事務管理器同一個數據源,事務控制將生效)。
3.如果@Transactional不指定事務管理器,使用缺省。
4.如果@Transactional指定了不匹配的事務管理器(實際用到的數據源和指定的事務管理器控制的數據源不一致),事務控制將失效.
注:spring容器缺省事務管理器:以加載順序,首先加載的作為缺省。例如
如果
定義在同一個文件中,則第一個transactionManagerX作為缺省。
定義在不同文件,則按文件的加載順序,首先加載的作為缺省。
建議:實際代碼中需要用到@Transactional時,即使默認只有一個transactionManager,@Transactional也將其標明。以提高新增數據源後代碼可讀性,另外防止定義多個數據源後,以前缺省的不被spring默認為缺省了(比如哪天上線新定義了一個數據源,剛好新定義的transactionManager被先加載了,那就悲劇了。)
4、方法的可見度和 @Transactional
@Transactional 注解應該只被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯, 但是這個被注解的方法將不會展示已配置的事務設置。
@Transactional 注解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 注解的出現不足於開啟事務行為,它僅僅 是一種元數據,能夠被可以識別 @Transactional 注解和上述的配置適當的具有事務行為的beans所使用。上面的例子中,其實正是 元素的出現 開啟 了事務行為。
Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional 注解,但是這將只能當你設置了基於接口的代理時它才生效。因為注解是 不能繼承 的。
5、特別注意:
實際開發中,多半喜歡將持久化操作的代碼集中抽出為另一個方法(因為不想事務被無關的業務代碼托的持續太長),然後在抽取出來的方法上加上@Transactional,這樣的結果是被抽離出的代碼即使加了事務標記,也根本起不到事務控制的效果(不管是private和public)。
例如:
public class TestEntityServiceImpl implements TestEntityService {
@Resource
private TestEntityDao testEntityDao;//實際操作的是datasourceX.
@Transactional
public void methodX() {
testEntityDao.xxx();
testEntityDao.zzz();
}
public void methodY() {
methodX()
}
}
如果執行TestEntityService.methodY();事務是不生效的。只有TestEntityService.methodY();才生效。
從spring實現這些的原理(動態代理和aop)上來看,只攔截外部調用,方法的內部調用通常是不被aop支持的。