假定spring 容器中定義了兩個事務管理器:
transactionManagerX,
transactionManagerY,分管兩個數據源datasourceX和datasourceY.
<tx:annotation-driven transaction-manager="transactionManagerX" />
<tx:annotation-driven transaction-manager="transactionManagerY" />
(spring容器中的定義順序如上)
有以下應用代碼:
public interface TestEntityService {
public void methodX();
public void methodY();
}
接口實現類1
public class
TestEntityServiceImpl implements TestEntityService {
private TestEntityDao testEntityDao;//實際操做的是
datasourceX.
@Transactional
public void methodX() {
testEntityDao.xxx();
testEntityDao.zzz();
}
public void methodY() {
}
}
public class
AnotherTestEntityServiceImpl implements TestEntityService {
private TestEntityDao anOtherTestEntityDao;//實際操做的是
datasourceY.
@Transactional
public void methodX() {
testEntityDao.mmm();
testEntityDao.nnn();
}
public void methodY() {
}
}
假設方法methodX須要事務控制的,一般咱們是直接在方法上添加@Transactional標註,
可是好像spring3.0(具體版本沒弄清)以前的Transactional標註不支持區分使用哪一個事務管理器。3.0以後的版本Transactional增長了個string類型的value屬性來特殊指定加以區分。
例如@Transactional("
aaaaa"),即顯示的要求spring用id="
aaaaa"的事務管理器來管理事務。該屬性亦可省略(省略的話用容器中缺省的transactionManager)
對於該屬性的用法作了以下測試來
methodX()事務生效測試結果
|
@Transactional
|
@Transactional
|
@Transactional
("transactionManagerZ")
transactionManagerZ爲未定義過的
|
@Transactional
|
TestEntityServiceImpl(實際使用datasourceX)
|
Y
|
N
|
Y
|
Y
|
AnotherTestEntityServiceImpl (實際使用datasourceY)
|
N
|
Y
|
N
|
N
|
若是調換兩個事務管理器在容器中的定義順序,如
<tx:annotation-driven transaction-manager="transactionManagerY" />
<tx:annotation-driven transaction-manager="transactionManagerX" />
獲得的結果
methodX()事務生效測試結果
|
@Transactional
|
@Transactional
|
@Transactional
("transactionManagerZ")
transactionManagerZ爲未定義過的
|
@Transactional
|
TestEntityServiceImpl(實際使用datasourceX)
|
Y
|
N
|
N
|
N
|
AnotherTestEntityServiceImpl (實際使用datasourceY)
|
N
|
Y
|
Y
|
Y
|
分析結果(其實源碼就能夠反應出):容器指定一個默認的事務管理器
1.當在@Transactional("xxx")中正確指定了須要使用的事務管理器時,事務控制正常。
2.若是@Transactional指定了未定義過的事務管理器,spring以缺省默認的事務管理器來處理。(若是程序正好使用的是缺省事務管理器同一個數據源,事務控制將生效)。
3.若是@Transactional不指定事務管理器,使用缺省。
4.若是@Transactional指定了不匹配的事務管理器(實際用到的數據源和指定的事務管理器控制的數據源不一致),事務控制將失效.
注:spring容器缺省事務管理器:以加載順序,首先加載的做爲缺省。例如
若是
<tx:annotation-driven transaction-manager="transactionManagerX" />
<tx:annotation-driven transaction-manager="transactionManagerY" />
定義在同一個文件中,則第一個transactionManagerX做爲缺省。
定義在不一樣文件,則按文件的加載順序,首先加載的做爲缺省。
建 議:實際代碼中須要用到@Transactional時,即便默認只有一個transactionManager,@Transactional也將其標 明。以提升新增數據源後代碼可讀性,另外防止定義多個數據源後,之前缺省的不被spring默認爲缺省了(好比哪天上線新定義了一個數據源,恰好新定義的 transactionManager被先加載了,那就悲劇了。)
二.bean的配置使用
容器中加了<tx:annotation-driven >(須要增長一些xsd)以後,須要事務控制的的service,不須要再具體的bean上作其餘的配置,例如用代理包裝。直接配置便可
<bean id="testEntityService" class="com.xxx.impl.TestEntityServiceImpl"/>
spring將由JdkDynamicAopProxy 生成代理過的類提供使用。
這種用法的效果和下面配置使用效果同樣。都是由JdkDynamicAopProxy 生成代理對象提供使用。
我以爲區別是下面的方法在事務控制的代碼可讀性上很差,由於哪一個方法須要事務控制和控制粒度都在配置文件中,和代碼分開了。
<bean id="testEntityService3" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManagerX" />
<property name="target">
<bean class="com.xxxx.impl.TestEntityServiceImpl" />
</property>
<property name="proxyInterfaces" value="com.xxxx.TestEntityService"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
方法的可見度和 @Transactional
@Transactional 註解應該只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。
@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據,可以被能夠識別 @Transactional 註解和上述的配置適當的具備事務行爲的beans所使用。上面的例子中,其實正是 <tx:annotation-driven/>元素的出現 開啓 了事務行爲。
Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。你固然能夠在接口上使用 @Transactional 註解,可是這將只能當你設置了基於接口的代理時它才生效。由於註解是 不能繼承 的。
實際開發中,多半喜歡將持久化操做的代碼集中抽出爲另外一個方法(由於不想事務被無關的業務代碼託的持續太長),而後在抽取出來的方法上加上@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支持的。