springboot+jta+atomikos 分布式事物管理

首先新建两个数据库,test1和test2 

项目结构目录

springboot+jta+atomikos 分布式事物管理_第1张图片

在application.properties进行配置

#分布式事务进行管理
#MySql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root

mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1


# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root

mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1


pom.xml配置



    4.0.0

    cn.mywork
    springboot
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.8.RELEASE
    
    
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-tomcat
    

        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            mysql
            mysql-connector-java
            5.1.21
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
        
        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.mybatis
            mybatis
            3.4.5
        
        
        
            org.springframework.boot
            spring-boot-starter-jta-atomikos
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        
    


分布式事务用分包管理的方式,即新建两个包user1和user2,包的路径下分别创建dao和service,用来存放数据库操作的类和事务管理的类,以对不同数据源进行区分操作

user1包下dao层 

package cn.mywork.user1.dao;

import cn.mywork.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 14:02
 */
public interface UserDAOTest1  extends JpaRepository {
}

service层

package cn.mywork.user1.service;

import cn.mywork.dao.UserDAO;
import cn.mywork.entity.User;
import cn.mywork.user1.dao.UserDAOTest1;
import cn.mywork.user2.dao.UserDAOTest2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 11:09
 */
@Service
public class UserService {
   @Autowired
    private UserDAOTest1 userDAOTest1;
    @Autowired
    private UserDAOTest2 userDAOTest2;
  @Transactional
    public String insert(User user){
        userDAOTest1.save(user);
        userDAOTest2.save(user);
        int i=1/0;
        return "success";
    }
}

user2的dao层

package cn.mywork.user2.dao;

import cn.mywork.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 14:04
 */
public interface UserDAOTest2  extends JpaRepository {
}

datasource包下,对两个数据源进行配置管理

package cn.mywork.datasource;


import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import javax.sql.DataSource;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.sql.SQLException;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 15:15
 */
@ConfigurationProperties(prefix = "mysql.datasource.test1")
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "cn.mywork.user1", sqlSessionTemplateRef = "user1SqlSessionTemplate")
public class DBConfig1 {
    private String url;
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;


  @Primary
  @Bean(name ="user1DataSource")
  public DataSource user1DataSource()throws SQLException {
      MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
      mysqlXADataSource.setUrl(getUrl());
      mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
      mysqlXADataSource.setPassword(getPassword());
      mysqlXADataSource.setUser(getUsername());
      mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);


      AtomikosDataSourceBean xaDataSource=new AtomikosDataSourceBean();
      xaDataSource.setXaDataSource(mysqlXADataSource);
      xaDataSource.setUniqueResourceName("user1DataSource");

      xaDataSource.setMinPoolSize(getMinPoolSize());
      xaDataSource.setMaxPoolSize(getMaxPoolSize());
      xaDataSource.setMaxLifetime(getMaxLifetime());
      xaDataSource.setBorrowConnectionTimeout(getBorrowConnectionTimeout());
      xaDataSource.setLoginTimeout(getLoginTimeout());
      xaDataSource.setMaintenanceInterval(getMaintenanceInterval());
      xaDataSource.setMaxIdleTime(getMaxIdleTime());
      xaDataSource.setTestQuery(getTestQuery());

      return   xaDataSource;
  }

  @Primary
  @Bean (name="user1SqlSessionFactory")
  public SqlSessionFactory user1SqlSessionTemplate(@Qualifier("user1DataSource") DataSource dataSource) throws Exception {
      SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
      bean.setDataSource(dataSource);
      return bean.getObject();
  }
    @Primary
    @Bean(name = "user1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("user1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }


    @Override
    public String toString() {
        return "DBConfig1{" +
                "url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", minPoolSize=" + minPoolSize +
                ", maxPoolSize=" + maxPoolSize +
                ", maxLifetime=" + maxLifetime +
                ", borrowConnectionTimeout=" + borrowConnectionTimeout +
                ", loginTimeout=" + loginTimeout +
                ", maintenanceInterval=" + maintenanceInterval +
                ", maxIdleTime=" + maxIdleTime +
                ", testQuery='" + testQuery + '\'' +
                '}';
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getMinPoolSize() {
        return minPoolSize;
    }

    public void setMinPoolSize(int minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getMaxLifetime() {
        return maxLifetime;
    }

    public void setMaxLifetime(int maxLifetime) {
        this.maxLifetime = maxLifetime;
    }

    public int getBorrowConnectionTimeout() {
        return borrowConnectionTimeout;
    }

    public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
        this.borrowConnectionTimeout = borrowConnectionTimeout;
    }

    public int getLoginTimeout() {
        return loginTimeout;
    }

    public void setLoginTimeout(int loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

    public int getMaintenanceInterval() {
        return maintenanceInterval;
    }

    public void setMaintenanceInterval(int maintenanceInterval) {
        this.maintenanceInterval = maintenanceInterval;
    }

    public int getMaxIdleTime() {
        return maxIdleTime;
    }

    public void setMaxIdleTime(int maxIdleTime) {
        this.maxIdleTime = maxIdleTime;
    }

    public String getTestQuery() {
        return testQuery;
    }

    public void setTestQuery(String testQuery) {
        this.testQuery = testQuery;
    }
}
package cn.mywork.datasource;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 20:50
 */

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import javax.sql.DataSource;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;

@ConfigurationProperties(prefix = "mysql.datasource.test2")
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "cn.mywork.user2", sqlSessionTemplateRef = "user2SqlSessionTemplate")
public class DBConfig2 {
    private String url;
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;



    @Bean(name ="user2DataSource")
    public DataSource user1DataSource()throws SQLException {
        MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
        mysqlXADataSource.setUrl(getUrl());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setPassword(getPassword());
        mysqlXADataSource.setUser(getUsername());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);


        AtomikosDataSourceBean xaDataSource=new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName("user2DataSource");

        xaDataSource.setMinPoolSize(getMinPoolSize());
        xaDataSource.setMaxPoolSize(getMaxPoolSize());
        xaDataSource.setMaxLifetime(getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(getLoginTimeout());
        xaDataSource.setMaintenanceInterval(getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(getMaxIdleTime());
        xaDataSource.setTestQuery(getTestQuery());

        return   xaDataSource;
    }

    @Bean (name="user2SqlSessionFactory")
    public SqlSessionFactory user1SqlSessionTemplate(@Qualifier("user2DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

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





    @Override
    public String toString() {
        return "DBConfig1{" +
                "url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", minPoolSize=" + minPoolSize +
                ", maxPoolSize=" + maxPoolSize +
                ", maxLifetime=" + maxLifetime +
                ", borrowConnectionTimeout=" + borrowConnectionTimeout +
                ", loginTimeout=" + loginTimeout +
                ", maintenanceInterval=" + maintenanceInterval +
                ", maxIdleTime=" + maxIdleTime +
                ", testQuery='" + testQuery + '\'' +
                '}';
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getMinPoolSize() {
        return minPoolSize;
    }

    public void setMinPoolSize(int minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getMaxLifetime() {
        return maxLifetime;
    }

    public void setMaxLifetime(int maxLifetime) {
        this.maxLifetime = maxLifetime;
    }

    public int getBorrowConnectionTimeout() {
        return borrowConnectionTimeout;
    }

    public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
        this.borrowConnectionTimeout = borrowConnectionTimeout;
    }

    public int getLoginTimeout() {
        return loginTimeout;
    }

    public void setLoginTimeout(int loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

    public int getMaintenanceInterval() {
        return maintenanceInterval;
    }

    public void setMaintenanceInterval(int maintenanceInterval) {
        this.maintenanceInterval = maintenanceInterval;
    }

    public int getMaxIdleTime() {
        return maxIdleTime;
    }

    public void setMaxIdleTime(int maxIdleTime) {
        this.maxIdleTime = maxIdleTime;
    }

    public String getTestQuery() {
        return testQuery;
    }

    public void setTestQuery(String testQuery) {
        this.testQuery = testQuery;
    }
}

根据实体类分别在数据库里面创建两个user表
package cn.mywork.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 12:55
 */
@Entity(name="users")
public class User {
    @Id
    @GeneratedValue
    private Integer id;
    @Column
    private  String name;
    @Column
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
控制层
package cn.mywork.controller;

import cn.mywork.entity.User;

import cn.mywork.user1.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 10:43
 */
@RestController
public class IndexController {

    @Autowired
    private UserService userService;
    @RequestMapping("test")
    @ResponseBody
    public String  insert (){
        String message = userService.insert(new User("zhangsan", 15));

        return  message;
    }


}

package cn.mywork.Application;

import cn.mywork.datasource.DBConfig1;
import cn.mywork.datasource.DBConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

/**
 * @author Terry
 * @version 1.0
 * @date 2018-04-22 10:46
 */
@ComponentScan(basePackages = {"cn.mywork.controller","cn.mywork.service","cn.mywork.user1.service"})
@EnableAutoConfiguration
@EnableJpaRepositories(basePackages ={"cn.mywork.dao","cn.mywork.user1.dao","cn.mywork.user2.dao"})
@EntityScan("cn.mywork.entity")
@EnableConfigurationProperties(value = {DBConfig1.class, DBConfig2.class })
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

运行程序,访问 localhost:8080/test

调用test接口,由于在service类中的insert方法,int i=1/0;程序肯定会报错的

然后就可以看到报错的页面,返回数据库里面看看,分别查看test1 和test2数据库里面的user表,

你会发现插入数据失败,可以肯定,由于调用了事务管理,两个数据源的事务回滚了,这就是想要的效果。


注意:进行事务操作要在datasource指定的包里面操作,否则达不到效果



你可能感兴趣的:(springboot)