歡迎您光臨本站 註冊首頁

Spring事務管理高級應用難點剖析(3)

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  事務方法嵌套調用的迷茫

  Spring事務一個被訛傳很廣說法是:一個事務方法不應該調用另一個事務方法,否則將產生兩個事務.結果造成開發人員在設計事務方法時束手束腳,生怕一不小心就踩到地雷.其實這種是不認識Spring事務傳播機制而造成的誤解,Spring對事務控制的支持統一在TransactionDefinition類中描述,該類有以下幾個重要的介面方法:

  ◆intgetPropagationBehavior():事務的傳播行為;

  ◆intgetIsolationLevel():事務的隔離級別;

  ◆intgetTimeout():事務的過期時間;

  ◆booleanisReadOnly():事務的讀寫特性.

  很明顯,除了事務的傳播行為外,事務的其它特性Spring是藉助底層資源的功能來完成的,Spring無非只充當個代理的角色.但是事務的傳播行為卻是Spring憑藉自身的框架提供的功能,是Spring提供給開發者最珍貴的禮物,訛傳的說法玷污了Spring事務框架最美麗的光環.所謂事務傳播行為就是多個事務方法相互調用時,事務如何在這些方法間傳播.Spring支持7種事務傳播行為:

  ◆PROPAGATION_REQUIRED如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中.這是最常見的選擇.

  ◆PROPAGATION_SUPPORTS支持當前事務,如果當前沒有事務,就以非事務方式執行.

  ◆PROPAGATION_MANDATORY使用當前的事務,如果當前沒有事務,就拋出異常.

  ◆PROPAGATION_REQUIRES_NEW新建事務,如果當前存在事務,把當前事務掛起.

  ◆PROPAGATION_NOT_SUPPORTED以非事務方式執行操作,如果當前存在事務,就把當前事務掛起.

  ◆PROPAGATION_NEVER以非事務方式執行,如果當前存在事務,則拋出異常.

  ◆PROPAGATION_NESTED如果當前存在事務,則在嵌套事務內執行.如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作.

  Spring默認的事務傳播行為是PROPAGATION_REQUIRED,它適合於絕大多數的情況.假設ServiveX#methodX()都工作在事務環境下(即都被Spring事務增強了),假設程序中存在如下的調用鏈:Service1#method1()->Service2#method2()->Service3#method3(),那麼這3個服務類的3個方法通過Spring的事務傳播機制都工作在同一個事務中.

  下面,我們來看一下實例,UserService#logon()方法內部調用了UserService#updateLastLogonTime()和ScoreService#addScore()方法,這兩個類都繼承於BaseService.它們之間的類結構說明如下:


圖1.UserService和ScoreService

  具體的代碼如下所示:

  清單9UserService.java

  69.@Service("userService")

  70.publicclassUserServiceextendsBaseService{

  71.@Autowired

  72.privateJdbcTemplatejdbcTemplate;

  73.@Autowired

  74.privateScoreServicescoreService;

  75.

  76.publicvoidlogon(StringuserName){

  77.updateLastLogonTime(userName);

  78.scoreService.addScore(userName,20);

  79.}

  80.

  81.publicvoidupdateLastLogonTime(StringuserName){

  82.Stringsql="UPDATEt_useruSETu.last_logon_time=?WHEREuser_name=?";

  83.jdbcTemplate.update(sql,System.currentTimeMillis(),userName);

  84.}

  85.}

  UserService中注入了ScoreService的Bean,ScoreService的代碼如下所示:

  清單10ScoreService.java

  86.@Service("scoreUserService")

  87.publicclassScoreServiceextendsBaseService{

  88.@Autowired

  89.privateJdbcTemplatejdbcTemplate;

  90.publicvoidaddScore(StringuserName,inttoAdd){

  91.Stringsql="UPDATEt_useruSETu.score=u.score ?WHEREuser_name=?";

  92.jdbcTemplate.update(sql,toAdd,userName);

  93.}

  94.}

  通過Spring的事務配置為ScoreService及UserService中所有公有方法都添加事務增強,讓這些方法都工作於事務環境下.下面是關鍵的配置代碼:

  清單11事務增強配置

  1.

  2.<aop:configproxy-target-classaop:configproxy-target-class="true">

  3.<aop:pointcutidaop:pointcutid="serviceJdbcMethod"

  4.

  5.expression="within(user.nestcall.BaseService )"/>

  6.<aop:advisorpointcut-refaop:advisorpointcut-ref="serviceJdbcMethod"

  7.advice-ref="jdbcAdvice"order="0"/>

  8.</< span>aop:config>

  9.<tx:adviceidtx:adviceid="jdbcAdvice"transaction-manager="jdbcManager">

  10.<tx:attributes>

  11.<tx:methodnametx:methodname="*"/>

  12.</< span>tx:attributes>

  13.</< span>tx:advice>

  將日誌級別設置為DEBUG,啟動Spring容器並執行UserService#logon()的方法,仔細觀察如下的輸出日誌:

  清單12執行日誌

  14.16:25:04,765DEBUG(AbstractPlatformTransactionManager.java:365)-

  15.Creatingnewtransactionwithname[user.nestcall.UserService.logon]:

  16.PROPAGATION_REQUIRED,ISOLATION_DEFAULT①為UserService#logon方法啟動一個事務

  17.16:25:04,765DEBUG(DataSourceTransactionManager.java:205)-

  18.AcquiredConnection[org.apache.commons.dbcp.PoolableConnection@32bd65]

  19.forJDBCtransaction

  20.logonmethod...

  21.updateLastLogonTime...②直接執行updateLastLogonTime方法

  22.16:25:04,781DEBUG(JdbcTemplate.java:785)-ExecutingpreparedSQLupdate

  23.16:25:04,781DEBUG(JdbcTemplate.java:569)-ExecutingpreparedSQLstatement

  24.[UPDATEt_useruSETu.last_logon_time=?WHEREuser_name=?]

  25.16:25:04,828DEBUG(JdbcTemplate.java:794)-SQLupdateaffected0rows

  26.16:25:04,828DEBUG(AbstractPlatformTransactionManager.java:470)-Participating

  27.inexistingtransaction③ScoreService#addScore方法加入到UserService#logon的事務中

  28.addScore...

  29.16:25:04,828DEBUG(JdbcTemplate.java:785)-ExecutingpreparedSQLupdate

  30.16:25:04,828DEBUG(JdbcTemplate.java:569)-ExecutingpreparedSQLstatement

  31.[UPDATEt_useruSETu.score=u.score ?WHEREuser_name=?]

  32.16:25:04,828DEBUG(JdbcTemplate.java:794)-SQLupdateaffected0rows

  33.16:25:04,828DEBUG(AbstractPlatformTransactionManager.java:752)-

  34.Initiatingtransactioncommit

  35.16:25:04,828DEBUG(DataSourceTransactionManager.java:265)-CommittingJDBCtransaction

  36.onConnection[org.apache.commons.dbcp.PoolableConnection@32bd65]

  37.16:25:04,828DEBUG(DataSourceTransactionManager.java:323)-ReleasingJDBCConnection

  38.[org.apache.commons.dbcp.PoolableConnection@32bd65]aftertransaction

  39.16:25:04,828DEBUG(DataSourceUtils.java:312)-ReturningJDBCConnectiontoDataSource

  從上面的輸入日誌中,可以清楚地看到Spring為UserService#logon()方法啟動了一個新的事務,而UserSerive#updateLastLogonTime()和UserService#logon()是在相同的類中,沒有觀察到有事務傳播行為的發生,其代碼塊好像「直接合併」到UserService#logon()中.接著,當執行到ScoreService#addScore()方法時,我們就觀察到了發生了事務傳播的行為:Participatinginexistingtransaction,這說明ScoreService#addScore()添加到UserService#logon()的事務上下文中,兩者共享同一個事務.最終的結果是UserService的logon(),updateLastLogonTime()以及ScoreService的addScore都工作於同一事務中.


[火星人 ] Spring事務管理高級應用難點剖析(3)已經有570次圍觀

http://coctec.com/docs/java/show-post-60405.html