1. 程式人生 > 實用技巧 >SpringBoot多資料來源與Atomikos分散式事務【SpringBoot+JTA+Atomikos】

SpringBoot多資料來源與Atomikos分散式事務【SpringBoot+JTA+Atomikos】

1:pom.xml引入相關依賴jar

<!--springboot整合mybatis的依賴 -->
        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</
artifactId> <version>2.1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId
> <version>8.0.22</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos --> <dependency> <groupId>org.springframework.boot</groupId> <
artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>

2、application.yml配置多資料來源

spring:
  datasource:
    test1:
      url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT&allowMultiQueries=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      sql-script-encoding: UTF-8
      type: com.zaxxer.hikari.HikariDataSource
      initialization-mode: always
      continue-on-error: true
      hikari:
        minimum-idle: 5
        connection-test-query: SELECT 1 FROM DUAL
        maximum-pool-size: 20
        auto-commit: true
        idle-timeout: 30000
        pool-name: SpringBootDemoHikariCP
        max-lifetime: 60000
        connection-timeout: 30000
      logging:
        level:
          com.qjc.dao.Test1: TARCE
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      maxLifetime: 20000
      maxPoolSize: 25
      minPoolSize: 3
      uniqueResourceName: test1
      testQuery: SELECT 1 FROM DUAL
    test2:
      url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT&allowMultiQueries=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      sql-script-encoding: UTF-8
      type: com.zaxxer.hikari.HikariDataSource
      initialization-mode: always
      continue-on-error: true
      hikari:
        minimum-idle: 5
        connection-test-query: SELECT 1 FROM DUAL
        maximum-pool-size: 20
        auto-commit: true
        idle-timeout: 30000
        pool-name: SpringBootDemoHikariCP
        max-lifetime: 60000
        connection-timeout: 30000
      logging:
        level:
         com.qjc.dao.Test2: TARCE
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      maxLifetime: 20000
      maxPoolSize: 25
      minPoolSize: 3
      uniqueResourceName: test2
      testQuery: SELECT 1 FROM DUAL

3、具體資料來源的config和application.yml配置的資料來源對應

package com.example.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

@Data
@ConfigurationProperties(prefix = "spring.datasource.test1")
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;
    private String uniqueResourceName;
}
package com.example.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

@Data
@ConfigurationProperties(prefix = "spring.datasource.test2")
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;
    private String uniqueResourceName;
}

4、啟動類上加具體資料來源的config

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

import com.example.config.DBConfig1;
import com.example.config.DBConfig2;

@SpringBootApplication
@EnableConfigurationProperties({DBConfig1.class, DBConfig2.class})
public class App {

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

5、配置資料來源加事務配置

package com.example.config;

import java.sql.SQLException;

import javax.sql.DataSource;

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;

@Configuration
//basePackages 最好分開配置,放在同一個資料夾可能會報錯
@MapperScan(basePackages = {"com.example.mapper.test1"},sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class DataSourceConfig1 {
    /**
     * 配置資料來源
     * @return
     */
    @Primary
    @Bean("test1DataSource")
    public DataSource e3shopDataSource(DBConfig1 eBConfig1) throws SQLException {
        //建立xa datasource
        MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
        mysqlXADataSource.setUrl(eBConfig1.getUrl());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setPassword(eBConfig1.getPassWord());
        mysqlXADataSource.setUser(eBConfig1.getUserName());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);

        //2.註冊到我們全域性事務上
        AtomikosDataSourceBean xaDataSource =new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName(eBConfig1.getUniqueResourceName());
        xaDataSource.setMinPoolSize(eBConfig1.getMinPoolSize());
        xaDataSource.setMaxPoolSize(eBConfig1.getMaxPoolSize());
        xaDataSource.setBorrowConnectionTimeout(eBConfig1.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(eBConfig1.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(eBConfig1.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(eBConfig1.getMaxIdleTime());
        xaDataSource.setTestQuery(eBConfig1.getTestQuery());
        return xaDataSource;

    }
    /**
     * sqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Primary
    @Bean("test1SqlSessionFactory")
    public SqlSessionFactory e3shopSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * sqlsessionTemplate
     * @param sqlSessionFactory
     * @return
     */
    @Primary
    @Bean(name = "test1SqlSessionTemplate")
    public SqlSessionTemplate e3shopSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
package com.example.config;

import java.sql.SQLException;

import javax.sql.DataSource;

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;

@Configuration
//basePackages 最好分開配置,放在同一個資料夾可能會報錯
@MapperScan(basePackages = {"com.example.mapper.test2"},sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class DataSourceConfig2 {
    /**
     * 配置資料來源
     * @return
     */
    @Bean("test2DataSource")
    public DataSource e3shopDataSource(DBConfig2 eBConfig1) throws SQLException {
        //建立xa datasource
        MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
        mysqlXADataSource.setUrl(eBConfig1.getUrl());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setPassword(eBConfig1.getPassWord());
        mysqlXADataSource.setUser(eBConfig1.getUserName());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);

        //2.註冊到我們全域性事務上
        AtomikosDataSourceBean xaDataSource =new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName(eBConfig1.getUniqueResourceName());
        xaDataSource.setMinPoolSize(eBConfig1.getMinPoolSize());
        xaDataSource.setMaxPoolSize(eBConfig1.getMaxPoolSize());
        xaDataSource.setBorrowConnectionTimeout(eBConfig1.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(eBConfig1.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(eBConfig1.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(eBConfig1.getMaxIdleTime());
        xaDataSource.setTestQuery(eBConfig1.getTestQuery());
        return xaDataSource;

    }
    /**
     * sqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean("test2SqlSessionFactory")
    public SqlSessionFactory e3shopSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * sqlsessionTemplate
     * @param sqlSessionFactory
     * @return
     */
    @Bean(name = "test2SqlSessionTemplate")
    public SqlSessionTemplate e3shopSqlSessionTemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

6、新增mapper層操作資料庫

package com.example.mapper.test1;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface User1Mapper {
    @Insert("INSERT INTO user (username, sex, password) VALUES ( #{username}, #{sex}, #{password})")
    int insertUser(@Param("username")String username,@Param("sex")String  sex,@Param("password")String password);
}
package com.example.mapper.test2;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface User2Mapper {
    @Insert("INSERT INTO user (username, sex, password) VALUES ( #{username}, #{sex}, #{password})")
    int insertUser(@Param("username")String username,@Param("sex")String  sex,@Param("password")String password);
}

7、service層新增事務@Transactional

package com.example.service.impl;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.mapper.test1.User1Mapper;
import com.example.mapper.test2.User2Mapper;
import com.example.service.UserService;
@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private User1Mapper user1Mapper;
    @Autowired
    private User2Mapper user2Mapper;
    @Transactional
    @Override
    public int addUser(String username, String sex, String password) {
        int rtu1=user1Mapper.insertUser(username, sex, password);
        int rtu2=user2Mapper.insertUser(username, sex, password);
        return rtu1+rtu2;
    }

}

完成,剩餘的就是Controller層呼叫了