5分鐘學會springboot整合多數據源解決分佈式事務

                                        springboot整合多數據源解決分佈式事務

 

一、前言

       springboot整合多數據源解決分佈式事務。

            1.多數據源採用分包策略 

            2.全局分佈式事務管理:jta-atomikos。

       在此記錄下,分享給大家。

 

 二、springboot整合多數據源解決分佈式事務

                                         

1、pom文件 依賴引入

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath />
    </parent>

    <dependencies>
        <!-- SpringBoot 測試 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- SpringBoot web組件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mybatis 支持 SpringBoot -->
        <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>
            <version>5.1.38</version>
        </dependency>

        <!-- 全局事務集中管理 解決分佈式事務 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>

        <!-- SpringBoot 自動配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 註解式 插入/構建/優雅代碼 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
    </dependencies>

 

2、 application.yml 新增配置

spring:
  datasource:
    ## 用戶數據庫
    user:
      url: jdbc:mysql://127.0.0.1:3306/yys_user
      username: root
      password: 123456
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      maxLifetime: 20000
      maxPoolSize: 25
      minPoolSize: 3
      uniqueResourceName: userDataSource
      testQuery: select 1
    ## 訂單數據庫
    order:
      url: jdbc:mysql://127.0.0.1:3306/yys_order
      username: root
      password: 123456
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      maxLifeTime: 20000
      maxPoolSize: 25
      minPoolSize: 3
      uniqueResourceName: orderDataSource
      testQuery: select 1

 

3、userConfig.java

@ConfigurationProperties(prefix = "spring.datasource.user")
@Data
public class UserConfig {

    private String url;

    private String userName;

    private String password;

    private int minPoolSize;

    private int maxPoolSize;

    private int maxLifeTime;

    private int maxIdleTime;

    private int loginTimeout;

    private int maintenanceInterval;

    private int borrowConnectionTimeout;

    private String testQuery;

    private String uniqueResourceName;

}

 

4、userDataSourceConfig.java

/**
 * 用戶數據源
 *      Config
 * @author yys
 */
@Configuration
@MapperScan(basePackages = "com.yys.user.mapper", sqlSessionTemplateRef = "userSqlSessionTemplate")
public class UserDataSourceConfig {

    /**
     * 創建 XADataSource
     * @return
     */
    @Bean("userDataSource")
    public DataSource userDataSource(UserConfig userConfig) throws SQLException {
        // 1、創建Mysql XADataSource
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(userConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(userConfig.getPassword());
        mysqlXaDataSource.setUser(userConfig.getUserName());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        // 2、將本地事務註冊到 Atomikos 全局事務
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName(userConfig.getUniqueResourceName());
        xaDataSource.setMinPoolSize(userConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(userConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(userConfig.getMaxLifeTime());
        xaDataSource.setBorrowConnectionTimeout(userConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(userConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(userConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(userConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(userConfig.getTestQuery());

        return xaDataSource;
    }

    /**
     * 創建 SQL會話工廠
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean("userSqlSessionFactory")
    public SqlSessionFactory userSqlSessionFactory(@Qualifier("userDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 創建用戶 SqlSession模板
     * @param sqlSessionFactory
     * @return
     */
    @Bean("userSqlSessionTemplate")
    public SqlSessionTemplate userSqlSessionTemplate(@Qualifier("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

 

5、orderConfig.java

@ConfigurationProperties(prefix = "spring.datasource.order")
@Data
public class OrderConfig {

    private String url;

    private String userName;

    private String password;

    private int minPoolSize;

    private int maxPoolSize;

    private int maxLifeTime;

    private int maxIdleTime;

    private int loginTimeout;

    private int maintenanceInterval;

    private int borrowConnectionTimeout;

    private String testQuery;

    private String uniqueResourceName;

}

 

6、orderDataSourceConfig.java

/**
 * 訂單數據源
 *      Config
 * @author yys
 */
@Configuration
@MapperScan(basePackages = "com.yys.order.mapper", sqlSessionTemplateRef = "orderSqlSessionTemplate")
public class OrderDataSourceConfig {

    /**
     * 創建 XADataSource
     * @return
     */
    @Bean("orderDataSource")
    public DataSource orderDataSource(OrderConfig orderConfig) throws SQLException {

        // 1、創建Mysql XADataSource
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(orderConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(orderConfig.getPassword());
        mysqlXaDataSource.setUser(orderConfig.getUserName());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        // 2、將本地事務註冊到 Atomikos 全局事務
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName(orderConfig.getUniqueResourceName());
        xaDataSource.setMinPoolSize(orderConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(orderConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(orderConfig.getMaxLifeTime());
        xaDataSource.setBorrowConnectionTimeout(orderConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(orderConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(orderConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(orderConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(orderConfig.getTestQuery());

        return xaDataSource;
    }

    /**
     * 創建 SQL會話工廠
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean("orderSqlSessionFactory")
    public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 創建訂單 SqlSession模板
     * @param sqlSessionFactory
     * @return
     */
    @Bean("orderSqlSessionTemplate")
    public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

 

7、MybatisController.java

/**
 * 多數據源解決分佈式事務測試
 *      Controller
 * @author yys
 */
@RestController
@RequestMapping("/add")
public class MybatisController {

    @Autowired
    private UserService userService;

    @Autowired
    private OrderService orderService;

    /**
     * 新增用戶並生成訂單(解決分佈式事務問題)
     * @return
     */
    @RequestMapping("/user")
    public String addUser(String name, Integer age, Double amount, String address) {
        return userService.addUser(name, age, amount, address) ? "success" : "fail";
    }

}

 

8、UserService.java

/**
 * 用戶管理
 *      Service
 * @author yys
 */
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    // 全局事務處理器
    // 事務底層原理採用aop技術做增強
    // 無需再指定某個事務管理器,全交給 Atomikos 全局事務
    @Transactional
    public Boolean addUser(String name, Integer age, Double amount, String address) {

        // 操作用戶庫
        int i = userMapper.addUser(name, age);
        // 操作訂單庫
        int j = orderMapper.addOrder(amount, address);

        // 測試事務回滾(age = 0:回滾;age > 0:事務提交)
        int flag = 1 / age;

        return i > 0 && j > 0;
    }

}

 

9、UserMapper.java

/**
 * 用戶管理
 *      Mapper
 * @author yys
 */
public interface UserMapper {

    @Insert("INSERT INTO user VALUES (NULL, #{name}, #{age}, 1, NOW(), NOW())")
    int addUser(@Param("name") String name, @Param("age") Integer age);

}

 

10、OrderMapper.java

/**
 * 訂單管理
 *      Mapper
 * @author yys
 */
public interface OrderMapper {

    // order爲數據庫關鍵字,記得使用``
    @Insert("INSERT INTO `order` VALUES (NULL, #{amount}, #{address}, 1, NOW(), NOW())")
    int addOrder(@Param("amount") Double amount, @Param("address") String address);

}

 

11、啓動類

@SpringBootApplication
@MapperScan("com.yys.mapper")
public class YysApp {

    public static void main(String[] args) {
        SpringApplication.run(YysApp.class, args);
    }

}

 

12、初始化sql文件


-- Database:yys_user

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID,自增列',
  `name` varchar(32) NOT NULL COMMENT '用戶名',
  `age` int(11) NOT NULL COMMENT '用戶年齡',
  `status` tinyint(2) NOT NULL DEFAULT '1' COMMENT '狀態:-1-刪除;1-正常;',
  `create_time` datetime NOT NULL COMMENT '創建時間',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;


-- Database:yys_order

DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID,自增列',
  `amount` double(11,2) NOT NULL COMMENT '訂單金額',
  `address` varchar(32) NOT NULL COMMENT '地址',
  `status` tinyint(2) NOT NULL DEFAULT '1' COMMENT '狀態:-1-刪除;1-正常;',
  `create_time` datetime NOT NULL COMMENT '創建時間',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

 

13、測試

http://localhost:8080/add/user?name=古猿&age=0&amount=12.02&address=南方

  a、頁面結果 - 如下圖所示 :

                    

 

  b、數據庫結果 - 如下圖所示 : 

                      

                     

 

http://localhost:8080/add/user?name=古猿&age=18&amount=12.02&address=南方

  a、頁面結果 - 如下圖所示 :

 

                     

 

  b、數據庫結果 - 如下圖所示 : 

                      

                     

 

 

 

                       Now ~ ~ ~寫到這裏,就寫完了,如果有幸幫助到你,請記得關注我,共同一起見證我們的成長

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章