Springboot整合RocketMQ解决分布式事务

直接上代码:

代码结构如下:

 

 依次贴出相关类:

DataSource1Config:
package com.example.demo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
//DataSource01
@Configuration // 注册到springboot容器中
@MapperScan(basePackages = "com.example.demo.test01", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config {

@Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test1")
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// bean.setMapperLocations(
// new
// PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml"));
return bean.getObject();
}

@Bean(name = "test1TransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

@Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}

}

DataSource2Config:
package com.example.demo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
//DataSource2
@Configuration // 注册到springboot容器中
@MapperScan(basePackages = "com.example.demo.test02", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSource2Config {
@Bean(name = "test2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test2")
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// bean.setMapperLocations(
// new
// PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test2/*.xml"));
return bean.getObject();
}

@Bean(name = "test2TransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("test2DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}

}
OrderConsumer:
package com.example.demo.consumer;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.DispatcherEntity;
import com.example.demo.entity.OrderEntity;
import com.example.demo.test02.DispatcherMapper;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @description: 消费者
* @author: dfz
* @create: 2020-02-24
**/
@Service
@RocketMQMessageListener(topic = "orderTopic", consumerGroup = "springboot-consumer-group")
public class OrderConsumer implements RocketMQListener<String>{
@Autowired
private DispatcherMapper dispatcherMapper;

@Override
public void onMessage(String msg) {
OrderEntity orderEntity = JSONObject.parseObject(msg, OrderEntity.class);
String orderId = orderEntity.getOrderId();
DispatcherEntity dispatcherEntity = new DispatcherEntity(orderId, "123456");
dispatcherMapper.insertDistribute(dispatcherEntity);

}
}
ProducerController:
package com.example.demo.producer;

import com.example.demo.service.ProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
@RestController
public class ProducerController {
@Autowired
private ProducerService producerService;

@RequestMapping(value = "sendMsg")
public String insertUser(){
producerService.saveOrder();
return "success";
}
}
ProducerService:
package com.example.demo.service;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.OrderEntity;
import com.example.demo.test01.OrderMapper;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
* @description: ProducerService
* @author: dfz
* @create: 2020-02-24
**/
@Service
public class ProducerService {

@Autowired
private OrderMapper orderMapper;

@Autowired
private RocketMQTemplate rocketMQTemplate;

public String saveOrder(){
//提前生成订单id
String orderId = System.currentTimeMillis()+"";
//提前生成半消息,再执行本地事务
OrderEntity orderEntity = creatOrder(orderId);
String msg = JSONObject.toJSONString(orderEntity);
MessageBuilder<String> tMessageBuilder = MessageBuilder.withPayload(msg);
tMessageBuilder.setHeader("msg", msg);
Message message = tMessageBuilder.build();
//该消息不会被消费
rocketMQTemplate.sendMessageInTransaction("springboot-producer-group", "orderTopic", message, null);
return orderId;
}


public OrderEntity creatOrder(String orderId){
OrderEntity orderEntity = new OrderEntity();
orderEntity.setOrderId(orderId);
orderEntity.setOrderName("订单Id1234");
orderEntity.setOrderCreateDate(new Date());
orderEntity.setPrice(300d);
return orderEntity;
}


}
package com.example.demo.listener;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.OrderEntity;
import com.example.demo.test01.OrderMapper;
import com.example.demo.utils.TransactionalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = "springboot-producer-group")
public class SyncProducerListener implements RocketMQLocalTransactionListener {

@Autowired
private TransactionalUtil transactionalUtil;

@Autowired
private OrderMapper orderMapper;

//执行本地事务
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
MessageHeaders headers = message.getHeaders();
Object object = headers.get("msg");
if (object == null) {
return RocketMQLocalTransactionState.ROLLBACK;
}
String orderMessage = (String) object;
OrderEntity orderEntity = JSONObject.parseObject(orderMessage, OrderEntity.class);
TransactionStatus status = null;
try {
status = transactionalUtil.begin();
int result = orderMapper.addOrder(orderEntity);
transactionalUtil.commit(status);
if (result < 1) {
return RocketMQLocalTransactionState.ROLLBACK;
}
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
if (status != null) {
transactionalUtil.rollback(status);
return RocketMQLocalTransactionState.ROLLBACK;
}
}
return null;
}

@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
MessageHeaders headers = message.getHeaders();
Object object = headers.get("msg");
if (object == null) {
return RocketMQLocalTransactionState.ROLLBACK;
}
String orderMessage = (String) object;
OrderEntity orderEntity = JSONObject.parseObject(orderMessage, OrderEntity.class);
String orderId = orderEntity.getOrderId();
//查询数据库
OrderEntity orderEntity1 = orderMapper.findOrder(orderId);
if (null == orderEntity1) {
return RocketMQLocalTransactionState.UNKNOWN;
}
return RocketMQLocalTransactionState.COMMIT;
}
}

package com.example.demo.test01;

import com.example.demo.entity.OrderEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
public interface OrderMapper {
@Insert("insert into order values(null,#{order.orderId},#{order.orderName}, #{order.price}, #{order.orderCreateDate});")
public int addOrder(OrderEntity order);
@Insert("select * from order where orderId = ${orderId};")
public OrderEntity findOrder(String orderId);

}

package com.example.demo.test02;

import com.example.demo.entity.DispatcherEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
public interface DispatcherMapper {
@Insert("insert into order_users values(null,#{entity.name},#{entity.age});")
public int insertDistribute(DispatcherEntity entity);
}

package com.example.demo.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
@Service
public class TransactionalUtil {
@Autowired
public DataSourceTransactionManager transactionManager;

public TransactionStatus begin(){
TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionAttribute());
return transactionStatus;
}

public void commit(TransactionStatus status){
transactionManager.commit(status);
}

public void rollback(TransactionStatus status){
transactionManager.rollback(status);
}
}

package com.example.demo.entity;

import lombok.Data;

import java.io.Serializable;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-24
**/
@Data
public class DispatcherEntity implements Serializable {
private String orderId;
private String userId;

public DispatcherEntity(String orderId, String userId) {
this.orderId = orderId;
this.userId = userId;
}
}
package com.example.demo.entity;

import java.io.Serializable;
import java.util.Date;

/**
* @description: ${description}
* @author: dfz
* @create: 2020-02-20
**/
public class OrderEntity implements Serializable{

private String orderId;
private String orderName;
private Date orderCreateDate;
private Double price;

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public String getOrderName() {
return orderName;
}

public void setOrderName(String orderName) {
this.orderName = orderName;
}

public Date getOrderCreateDate() {
return orderCreateDate;
}

public Double getPrice() {
return price;
}

public void setPrice(Double price) {
this.price = price;
}

public void setOrderCreateDate(Date orderCreateDate) {
this.orderCreateDate = orderCreateDate;
}

public OrderEntity(String orderId, String orderName) {
this.orderId = orderId;
this.orderName = orderName;
}

public OrderEntity() {
}
}

server.port=8088
###datasource1
spring.datasource.test1.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.test1.username = root
spring.datasource.test1.password = 123456
###datasource2
spring.datasource.test2.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test2.jdbc-url = jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.test2.username = root
spring.datasource.test2.password = 123456

rocketmq.name-server=192.168.2.3:9876
rocketmq.producer.group=boot_sync_user

Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.demo</groupId>
<artifactId>rocketmq-transactional</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rocketmq-transactional</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- springboot-web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot 整合 pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!--RocketMQ -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


posted @ 2020-02-24 20:53  ding9587  阅读(3859)  评论(0编辑  收藏  举报