Spring提供了一個JmsTransactionManager用於對JMS ConnectionFactory做事務管理。這將允許JMS應用利用Spring的事務管理特性。JmsTransactionManager在執行本地資源事務管理時將從指定的ConnectionFactory綁定一個ConnectionFactory/Session這樣的配對到線程中。JmsTemplate會自動檢測這樣的事務資源,並對它們進行相應操作。
在Spring整合JMS的應用中,如果我們要進行本地的事務管理的話非常簡單,只需要在定義對應的消息監聽容器時指定其sessionTransacted屬性為true。
xml: 增加<property name="sessionTransacted" value="true"/>
<!--配置 消息監聽容器--> <bean id="jmsContainer" class=" org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="queueDestination"/> <property name="messageListener" ref="consumerMessageListener"/> <!--應答模式是 INDIVIDUAL_ACKNOWLEDGE--> <!--AUTO_ACKNOWLEDGE = 1 自動確認 CLIENT_ACKNOWLEDGE = 2 客戶端手動確認 DUPS_OK_ACKNOWLEDGE = 3 自動批量確認 SESSION_TRANSACTED = 0 事務提交並確認 INDIVIDUAL_ACKNOWLEDGE = 4 單條消息確認--> <property name="sessionAcknowledgeMode" value="4"/> <!--對消息開啟事務模式--> <property name="sessionTransacted" value="true"/> </bean>
監聽器加入session.rollback();消息進行回滾重傳(上一篇中因為還沒有學習回滾,所以用的session.recover()實現的重發,現在就可以使用事務rollback()方法實現了)
回滾的過程是消息先出列,然后重發,默認6次,超過次數后進入到死亡隊列,(配置持久化數據庫的時候,並持久化到數據庫一條數據);
回滾肯定是開啟了事務的情況下,那么沒有開啟事務的情況呢?消息沒有確認的情況,消息會停在消息隊列中,等待着再次被監聽,除非調用session.recover()方法,效果和開啟事務並回滾一樣會進入死亡隊列。
session.rollback();//手動的調用此方法進行回滾,拋出異常時實際上事務開啟后會自動進行回滾的。
調用commit()方法進行提交:值得說的是,在事務模式下,在接收消息沒有確認的情況也會出列。完成消息。
session.commit();
也就是說,開啟事務后消息永遠不會出現停留在隊列的情況,消息會回滾重發,最后到死亡隊列中,而不開啟事務的情況,只要不使用session.recover();消息會停留在隊列中,不會重發,直至被確認出列。如果調用了recover就和回滾重發一樣了。
我們在實際業務中,接收消息操作和數據庫操作要在一起進行那么該怎么控制事務呢?
Spring里,如果同時存在JMS操作和DB操作,大概也就三種方式:
1.沒有使用JTA。JMS不在事務中,DB操作在事務中
a,消息處理
b,開始數據庫事務
c,數據庫操作
d,數據庫提交
成功:結束
失敗:回到b重試
這種方式事務沒有集成,靠的純粹是我們程序的控制,如果最終數據庫提交都沒成功的話,可以記下log,再人工去糾正數據。例子里把數據庫操作放在了更重要的位置,其實也可以倒過來,讓數據庫操作先完成,只好在做jms操作,看業務需求了:
a,開始數據庫事務
b,數據庫操作
c,數據庫提交
d,消息處理
成功:結束
失敗:回到d重試
我們只要把spring配置改成
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
...
<!-- This is important... -->
<property name="sessionTransacted" value="false" />
</bean>
再加上自己程序控制就好了。
2.如果沒有使用JTA,對於上面的配置如果把屬性 sessionTransacted 設成true的話,就產生了第二種方式,過程:
a,開始jms事務
b,開始數據庫事務
c,數據庫和jms操作
d,提交數據庫操作
e,提交jms操作
a,b的順序不肯定,不過對程序沒影響,但是d,e的順序是確定的。在Spring里,數據庫會在spring的commit方法里提交,而JMS會在afterCommit方法里提交。如果數據庫失敗,JMS當然也就回滾了,但是如果數據庫成功而JMS失敗,就產生了數據不一致,就要加上其它措施。而且這里還有一個致命缺點,就是某些情況下即使JMS失敗(比如JMS服務器down了),Spring也不會拋出異常,程序外部以為一切正常,而事實上以產生了不一致問題,而且很難發現。
3.如果使用了JTA那么把屬性 sessionTransacted 設成true的話,
JMS和數據庫操作就在同一個事務里了,沒什么好說,最安全的方式,但是效率很低。