2019独角兽企业重金招聘Python工程师标准>>>

Spring整合JMS(四)——事务管理

博客分类:

  • Spring

  • Jms

    SpringJMS事务sessionTransactedJtaTransactionManager

 

spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理。这将允许JMS应用利用Spring的事务管理特性。JmsTransactionManager在执行本地资源事务管理时将从指定的ConnectionFactory绑定一个ConnectionFactory/Session这样的配对到线程中。JmsTemplate会自动检测这样的事务资源,并对它们进行相应操作。

Java EE环境中,ConnectionFactory会池化Connection和Session,这样这些资源将会在整个事务中被有效地重复利用。在一个独立的环境中,使用Spring的SingleConnectionFactory时所有的事务将公用一个Connection,但是每个事务将保留自己独立的Session。

JmsTemplate可以利用JtaTransactionManager和能够进行分布式的 JMS ConnectionFactory处理分布式事务。

在Spring整合JMS的应用中,如果我们要进行本地的事务管理的话非常简单,只需要在定义对应的消息监听容器时指定其sessionTransacted属性为true,如:

Xml代码   

1.  <bean id="jmsContainer"

2.      class="org.springframework.jms.listener.DefaultMessageListenerContainer">

3.      <property name="connectionFactory" ref="connectionFactory" />

4.      <property name="destination" ref="queueDestination" />

5.      <property name="messageListener" ref="consumerMessageListener" />

6.      <property name="sessionTransacted" value="true"/>

7.  </bean>

该属性值默认为false,这样JMS在进行消息监听的时候就会进行事务控制,当在接收消息时监听器执行失败时JMS就会对接收到的消息进行回滚,对于SessionAwareMessageListener在接收到消息后发送一个返回消息时也处于同一事务下,但是对于其他操作如数据库访问等将不属于该事务控制。

这里我们可以来做一个这样的测试:我们如上配置监听在queueDestination的消息监听容器的sessionTransacted属性为true,然后把我们前面提到的消息监听器ConsumerMessageListener改成这样:

Java代码   

1.  public class ConsumerMessageListener implements MessageListener {

2.

3.      public void onMessage(Message message) {

4.              //这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换,或者直接把onMessage方法的参数改成Message的子类TextMessage

5.              TextMessage textMsg = (TextMessage) message;

6.              System.out.println("接收到一个纯文本消息。");

7.              try {

8.                  System.out.println("消息内容是:" + textMsg.getText());

9.                  if (1 == 1) {

10.                     throw new RuntimeException("Error");

11.                 }

12.             } catch (JMSException e) {

13.                 e.printStackTrace();

14.             }

15.     }

16.

17. }

我们可以看到在上述代码中我们的ConsumerMessageListener在进行消息接收的时候抛出了一个RuntimeException,根据我们上面说的,因为我们已经在对应的监听容器上定义了其sessionTransacted属性为true,所以当这里抛出异常的时候JMS将对接收到的消息进行回滚,即下次进行消息接收的时候该消息仍然能够被接收到。为了验证这一点,我们先执行一遍测试代码,往queueDestination发送一个文本消息,这个时候ConsumerMessageListener在进行接收的时候将会抛出一个RuntimeException,已经接收到的纯文本消息将进行回滚;接着我们去掉上面代码中抛出异常的语句,即ConsumerMessageListener能够正常的进行消息接收,这个时候我们再运行一次测试代码,往ConsumerMessageListener监听的queueDestination发送一条消息。如果之前在接手时抛出了异常的那条消息已经回滚了的话,那么这个时候将能够接收到两条消息,控制台将输出接收到的两条消息的内容。具体结果有兴趣的朋友可以自己验证一下。

如果想接收消息和数据库访问处于同一事务中,那么我们就可以配置一个外部的事务管理同时配置一个支持外部事务管理的消息监听容器(如DefaultMessageListenerContainer)。要配置这样一个参与分布式事务管理的消息监听容器,我们可以配置一个JtaTransactionManager,当然底层的JMS ConnectionFactory需要能够支持分布式事务管理,并正确地注册我们的JtaTransactionManager。这样消息监听器进行消息接收和对应的数据库访问就会处于同一数据库控制下,当消息接收失败或数据库访问失败都会进行事务回滚操作。

Xml代码   

1.  <bean id="jmsContainer"

2.      class="org.springframework.jms.listener.DefaultMessageListenerContainer">

3.      <property name="connectionFactory" ref="connectionFactory" />

4.      <property name="destination" ref="queueDestination" />

5.      <property name="messageListener" ref="consumerMessageListener" />

6.      <property name="transactionManager" ref="jtaTransactionManager"/>

7.  </bean>

8.

9.  <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

当给消息监听容器指定了transactionManager时,消息监听容器将忽略sessionTransacted的值。

关于使用JtaTransactionManager来管理上述分布式事务,我们这里也可以来做一个试验。

首先:往Spring配置文件applicationContext.xml中添加如下配置:

Xml代码   

1.  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

2.      <property name="dataSource" ref="dataSource"/>

3.  </bean>

4.

5.  <jee:jndi-lookup jndi-name="jdbc/mysql" id="dataSource"/>

6.  <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

7.

8.  <tx:annotation-driven transaction-manager="jtaTransactionManager"/>

我们可以看到,在这里我们引入了一个jndi数据源,定义了一个JtaTransactionManager,定义了Spring基于注解的声明式事务管理,定义了一个Spring提供的进行Jdbc操作的工具类jdbcTemplate。

接下来把我们的ConsumerMessageListener改为如下形式:

Java代码   

1.  public class ConsumerMessageListener implements MessageListener {

2.

3.      @Autowired

4.      private TestDao testDao;

5.

6.      private int count = 0;

7.

8.      public void onMessage(Message message) {

9.              //这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换,或者直接把onMessage方法的参数改成Message的子类TextMessage

10.             TextMessage textMsg = (TextMessage) message;

11.             System.out.println(new Date().toLocaleString() + "接收到一个纯文本消息。");

12.             try {

13.                 String text = textMsg.getText();

14.                 System.out.println("消息内容是:" + text);

15.                 System.out.println("当前count的值是:" + count);

16.                 testDao.insert(text + count);

17.                 if (count == 0) {

18.                     count ++;

19.                     throw new RuntimeException("Error! 出错啦!");

20.                 }

21.             } catch (JMSException e) {

22.                 e.printStackTrace();

23.             }

24.     }

25.

26. }

我们可以看到,在ConsumerMessageListener中我们定义了一个实例变量count,其初始值为0;在onMessage里面,我们可以看到我们把接收到的消息内容作为参数调用了testDao的insert方法;当count值为0,也就是进行第一次消息接收的时候会将count的值加1,同时抛出一个运行时异常。那么我们这里要测试的就是进行第一次接收的时候testDao已经把相关内容插入数据库了,接着在onMessage里面抛出了一个异常同时count加1,我们预期的结果应该是此时数据库进行回滚,同时JMS也回滚,这样JMS将继续尝试接收该消息,此时同样会调用testDao的insert方法将内容插入数据库,再接着count已经不为0了,所以此时将不再抛出异常,JMS成功进行消息的接收,testDao也成功的将消息内容插入到了数据库。要证明这个预期我们除了看数据库中插入的数据外,还可以看控制台的输出,正常情况控制台将输出两次消息接收的内容,且第一次时count为0,第二次count为1。

TestDao是一个接口,其TestDaoImpl对insert的方法实现如下:

Java代码   

1.  @Transactional(readOnly=false)

2.  public void insert(final String name) {

3.

4.      jdbcTemplate.update("insert into test(name) values(?)", name);

5.

6.  }

这里我们使用支持JtaTransactionManager的Weblogic来进行测试,因为是Web容器,所以我们这里定义了一个Controller来进行消息的发送,具体代码如下:

Java代码   

1.  @Controller

2.  @RequestMapping("test")

3.  public class TestController {

4.

5.      @Autowired

6.      @Qualifier("queueDestination")

7.      private Destination destination;

8.

9.      @Autowired

10.     private ProducerService producerService;

11.

12.     @RequestMapping("first")

13.     public String first() {

14.         producerService.sendMessage(destination, "你好,现在是:" + new Date().toLocaleString());

15.         return "/test/first";

16.     }

17.

18. }

接下来就是启用Weblogic服务器,进入其控制台,定义一个名叫“jdbc/MySQL”的JNDI数据源,然后把该项目部署到Weblogic服务器上并进行启动。接下来我们就可以访问/test/first.do访问到上述first方法。之后控制台会输出如下信息:


        我们可以看到当count为0时接收了一次,并随后抛出了异常,之后count为1又接收了一次,这说明在count为0时抛出异常后我们的JMS进行回滚了,那么我们的数据库是否有进行回滚呢?接着我们来看数据库中的内容:


        我们可以看到数据库表中只有一条记录,而且最后一位表示count的值的为1,这说明在JMS进行消息接收抛出异常时我们的数据库也回滚了。关于使用JtaTransactionManager进行分布式事务管理的问题就说到这里了,有兴趣的朋友可以自己试验一下。

转载于:https://my.oschina.net/lsl1991/blog/674639

Spring整合JMS(四)——事务管理相关推荐

  1. 全面分析 Spring 的编程式事务管理及声明式事务管理(转)

    摘要 Spring 的事务管理是 Spring 框架中一个比较重要的知识点,该知识点本身并不复杂,只是由于其比较灵活,导致初学者很难把握.本教程从基础知识开始,详细分析了 Spring 事务管理的使用 ...

  2. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  3. Spring整合JMS——基于ActiveMQ实现(一)

    Spring整合JMS--基于ActiveMQ实现(一) 1.1     JMS简介 JMS的全称是Java Message Service,即Java消息服务.它主要用于在生产者和消费者之间进行消息 ...

  4. spring配置c3p0连接池、spring的声明式事务管理

    一.spring配置c3p0连接池: 1.导入maven依赖: <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> & ...

  5. spring 的4种事务管理(1种编程式+3种声明式)

    见:http://blog.csdn.net/sinat_25926481/article/details/48208619 Spring的4种事务管理(1种编程式事务+三种声明事务) 一.Sprin ...

  6. Spring的4种事务管理(1种编程式事务+三种声明事务)

    2019独角兽企业重金招聘Python工程师标准>>> Spring的4种事务管理(1种编程式事务+三种声明事务) 一.Spring事务的介绍 二.编程式事务xml的配置 注入后直接 ...

  7. Spring整合JMS(二)——消息监听器

    消息监听器 在Spring整合JMS的应用中我们在定义消息监听器的时候一共能够定义三种类型的消息监听器,各自是MessageListener.SessionAwareMessageListener和M ...

  8. 在Spring中使用JTA事务管理

    在Spring中使用JTA事务管理 Spring 通过AOP技术可以让我们在脱离EJB的情况下享受声明式事务的丰盛大餐,脱离Java EE应用服务器使用声明式事务的道路已经畅通无阻.但是很大部分人都还 ...

  9. spring的annotation-driven配置事务管理器详解

    来源:http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html 这篇文章是我从ITeye上复制来的,看了一遍,觉得很深刻,决定把他复制来,对原作者表示感 ...

最新文章

  1. 架构师之路 — API 经济 — RESTful API 设计规范原则
  2. php如何防止超发,PHP+redis实现的限制抢购防止商品超发功能详解
  3. latex subfigure_latex-图片插入
  4. (软件工程复习核心重点)第三章需求分析-第一节:需求分析相关概念
  5. 浅谈spring--AOP与IOC / DI
  6. datatables中的Options总结(2)
  7. JavaScript_Object.assign(目标对象,源对象1,源对象2,...)
  8. 【Clickhouse】Clickhouse 表引擎之 Log系列
  9. Linux升级php
  10. STP/RSTP/MSTP经典分析与对比
  11. RAID一个硬盘FAIL。
  12. 蓄电池内阻测试仪分析软件,蓄电池内阻测试仪(GCBT-8610)
  13. Golang import 包时可以使用相对路径吗
  14. (四)孪生神经网络介绍及pytorch实现
  15. Javasript中Date日期常用用法(正则、比较)
  16. 软件测试的艺术读书笔记
  17. 全国各地区域码 --- (当地身份证号前六位)
  18. 调试器如何工作(2)
  19. 2022年最新京东滑块验证码破解思路(算法过验)
  20. 阿里云RDS的内存一直增加

热门文章

  1. MapReduce的序列化案例
  2. 机器学习基础专题:逻辑回归
  3. [转]Linux 进程间通信:共享内存
  4. Vue 系列之 组件
  5. Linux下搭建DNS服务器
  6. 【洛谷 1991】 无线通讯网
  7. Android自动化测试之路——Provider(一)
  8. 自制精排 ePub 集、不定期更新(UPDATA-2015-8-2)
  9. JQuery EasyUI datagrid 复杂表头处理
  10. Android SDK版本和ADT版本