Springboot+mybatisPlusのマルチデータソースのトランザクション

14010 ワード

Springbootは箱を開けてすぐに使うのは多くありませんが、mybatisplusの強力な持続層プラグインは、キーでコードを生成します.両者の結合は開発効率を大幅に向上させることができる.しかし、最初に学んだマルチデータ・ソースとマルチデータ・ソースのトランザクションの処理には、頭が痛くなる可能性があります.本稿ではmybatis-plusのマルチデータソースのトランザクション問題に焦点を当てて、いくつかのリファレンスソリューションを提供します.
datasource.ymlファイル構成

spring:
    user_center:
      url: jdbc:mysql://192.168.1.2:3306/a?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
    center_user:
      url: jdbc:mysql://192.168.1.2:3306/b?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20

application.yml構成

spring:
  application:
    name: auth-service
  profiles.active: dev
  aop:
   proxy-target-class: true
   auto: true

server:
  port: 18601

mybatis:
  type-aliases-package: com.br.auth.entity.*
  mapper-locations: classpath:mapper/*/*.xml
#logging
logging:
  level: warn



DataSourceConfiguration.java
package com.br.auth.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by jack-cooper on 2017/1/18.
 */
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration implements TransactionManagementConfigurer {

    private static final Logger log = LoggerFactory.getLogger(DataSourceConfiguration.class);

    @Value("${spring.center_user.type}")
    private Class extends DataSource> dataSourceType;

    @Bean(name = "centerUserDataSource", destroyMethod = "close", initMethod = "init")
    @Primary
    @ConfigurationProperties(prefix = "spring.center_user")
    public DataSource centerUserDataSourceOne() {
        log.info("--------------------     -     init ---------------------");
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean(name = "userCenterDataSource", destroyMethod = "close", initMethod="init")
    @ConfigurationProperties(prefix = "spring.user_center")
    public DataSource userCenterDataSourceOne() {
        log.info("--------------------    init ---------------------");
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean("dataSources")
    public Map dataSources(){
        Map dataSources = new HashMap<>();
        dataSources.put(DataSourceType.center_user.getType(), centerUserDataSourceOne());
        dataSources.put(DataSourceType.user_center.getType(),userCenterDataSourceOne());
        return dataSources;
    }


    /**
     *               bean
     *
     * @return
     */
    @Bean(name = "routingDataSource")
    public MyAbstractRoutingDataSource roundRobinDataSouceProxy() {
        int size = Integer.parseInt("1");
        MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource(size);
        Map targetDataSources = new HashMap();
        targetDataSources.put(DataSourceType.center_user.getType(), dataSources());
        for (String type : dataSources().keySet()) {
            targetDataSources.put(type, dataSources().get(type));
        }
        proxy.setDefaultTargetDataSource(centerUserDataSourceOne());
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

    /**
     *     ,         
     * @return
     */
    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(roundRobinDataSouceProxy());
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager();
    }
}


DataSourceContextHolder.java
package com.br.auth.config;

/**
 * Created by jack-cooper on 2017/1/18.
 */
public class DataSourceContextHolder {

    private static final ThreadLocal contextHolder  = new ThreadLocal();

    /**
     *   setDataSourceType     
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}


DataSourceType.java
package com.br.auth.config;


/**
 * Created by jack-cooper on 2017/1/18.
 */
public enum DataSourceType {
    user("user", "  "),
    order("order", "    -    ");

    private String type;
    private String name;

    DataSourceType(String type, String name) {
        this.type = type;
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

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


MyAbstractRoutingDataSource.java
package com.br.auth.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by jack-cooper on 2017/1/18.
 */
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {

    private final int dataSourceNumber;

    private AtomicInteger count = new AtomicInteger(0);

    public MyAbstractRoutingDataSource(int dataSourceNumber) {
        this.dataSourceNumber = dataSourceNumber;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}


MybatisPlusConfig.java
package com.br.auth.config;

import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.enums.DBType;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import com.br.auth.util.PropertiesUtil;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author 10400
 * @create 2017-07-03 11:28
 */
@Configuration
@Import({DataSourceConfiguration.class})
@MapperScan("com.br.auth.mapper.*")
@EnableTransactionManagement
public class MybatisPlusConfig {
    @Autowired(required = false)
    private Interceptor[] interceptors;
    @Autowired
    private ResourceLoader resourceLoader = new DefaultResourceLoader();
    @Value("${spring.user.type}")
    private Class extends DataSource> dataSourceType;

    @Autowired(required = false)
    private MybatisProperties properties;
    @Autowired(required = false)
    private DatabaseIdProvider databaseIdProvider;
    @Autowired(required = false)
    private PropertiesBean propertiesBean;

    @Resource(name = "routingDataSource")
    private AbstractRoutingDataSource routingDataSource;
    /**
     * mybatis-plus    
* :http://mp.baomidou.com
*/ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * mybatis-autoconfigure 。 * mybatis-boot * * @return */ @Bean public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean() throws Exception { PropertiesUtil.setProperties(propertiesBean.getPropertiesBean()); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean(); mybatisPlus.setDataSource(routingDataSource); mybatisPlus.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { mybatisPlus.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } mybatisPlus.setConfiguration(properties.getConfiguration()); if (!ObjectUtils.isEmpty(this.interceptors)) { mybatisPlus.setPlugins(this.interceptors); } // MP , GlobalConfiguration globalConfig = new GlobalConfiguration(); globalConfig.setDbType(DBType.MYSQL.name()); // ID AUTO->`0`(" ID ") INPUT->`1`( ID") ID_WORKER->`2`(" ID") UUID->`3`(" ID") globalConfig.setIdType(0); globalConfig.setFieldStrategy(FieldStrategy.NOT_EMPTY.getKey()); mybatisPlus.setGlobalConfig(globalConfig); MybatisConfiguration mc = new MybatisConfiguration(); mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); //mapUnderscoreToCamelCase: ( first_name => firstName) mc.setMapUnderscoreToCamelCase(false); mybatisPlus.setConfiguration(mc); if (this.databaseIdProvider != null) { mybatisPlus.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { mybatisPlus.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations()); } return mybatisPlus; } }

DataSourceAop.java
package com.br.auth.config;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;



/**
 * desc:        
 * Created by jack-cooper on 2017/1/18.
 */
@Aspect
@Order(-1)
@Component
public class DataSourceAop {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Before("execution(* com.br.auth.service.centerUser..*.*(..))")
    public void setCenterUserDataSourceType() throws Exception {
        DataSourceContextHolder.setDataSourceType(DataSourceType.center_user.getType());
        log.info("dataSource == >: centerUser");
    }

    @Before("execution(* com.br.auth.service.userCenter..*.*(..))")
    public void setUserCenterDataSourceType() throws Throwable{
        DataSourceContextHolder.setDataSourceType(DataSourceType.user_center.getType());
        log.info("dataSource == >:userCenter");
    }


    @After("execution(* com.br.auth.service.*..*.*(..)) ")
    public void afterReturning() throws Throwable {
        DataSourceContextHolder.clearDataSourceType();
        log.info("=====> clear dataSource aop ");
    }
}


記事の参考:http://www.cnblogs.com/sweetchildomine/p/6977987.html http://www.cnblogs.com/softidea/p/7127874.html