SpringBoot整合Mybatis,多数据源,事务,支持java -jar 启动.

用了一段时间SpringBoot,之前配置MYBATIS ,在打包WAR 放到tomcat下正常,但是WAR已经过时了,现在流行直接打包JAR 丢到DOCKER 里,无奈JAR 启动的时候MAPPER 扫描有问题,只能说之前整合MYBATIS 的方式不对.

这次的整合应该是不存在加载顺序引起的问题,使用了一段时间,妥妥的,记录下来

pom.xml

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

application.yml,支持多个数据源,有多少就写多少个,但这是静态的多数据源方式,也可以动态往Spring的数据源路由里面丢

spring:
    datasource:
#      readSize: 1
#      name: writeDataSource
      type: com.alibaba.druid.pool.DruidDataSource
      write:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=true
        username: root
        password: 123
        initialSize: 10
        maxActive: 100
        maxWait: 60000
        minIdle: 5
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 'x'
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
DataSourceConfig.java
/**
 * Created by ZhenWeiLai on 2016/11/20.
 */
@Configuration
@EnableTransactionManagement
@MapperScan("com.lzw.**.dao")//mybatis扫描
public class DataSourceConfig  implements TransactionManagementConfigurer {
    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;

    @Primary
    @Bean(name = "writeDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.write")
    public DataSource writeDataSource() {
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    /**
     * 有多少个从库就要配置多少个
     * @return
     */
//    @Bean(name = "readDataSource")
//    @ConfigurationProperties(prefix = "spring.datasource.read")
//    public DataSource readDataSourceOne() {
//        return DataSourceBuilder.create().type(dataSourceType).build();
//    }


    /**
     * AbstractRoutingDataSource 这破玩意 继承了AbstractDataSource ,AbstractDataSource又实现了DataSource
     * 所以可以直接丢去构建 SqlSessionFactory
     * @return
     */
    @Bean("routingDataSource")
//    @Bean(name="routingDataSource",initMethod = "init",destroyMethod = "close")
    public MyRoutingDataSource dataSourceProxy(){
//        int size = Integer.parseInt(dataSourceSize);
        MyRoutingDataSource proxy = new MyRoutingDataSource();
        Map<Object,Object> dataSourceMap = new HashMap<>();
        DataSource writeSource =  writeDataSource();
//        DataSource readSource = getReadDataSource();

        dataSourceMap.put(TargetDataSource.WRITE.getCode(),writeSource);
//        dataSourceMap.put(TargetDataSource.READ.getCode(),readSource);

        proxy.setDefaultTargetDataSource(dataSourceMap.get(TargetDataSource.WRITE.getCode()));
        proxy.setTargetDataSources(dataSourceMap);
        return proxy;
    }


    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy());
        bean.setTypeAliasesPackage("com.lzw");
        bean.setVfs(SpringBootVFS.class);
        //添加XML目录
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources("classpath:**/mapper/**/*.xml"));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 事务配置,考虑多数据源情况下
     * @return
     */
    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSourceProxy());
    }

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


}
MyRoutingDataSource.java
/**
 * Created by ZhenWeiLai on 2016/11/22.
 */
public class MyRoutingDataSource extends AbstractRoutingDataSource {
    /**
     * 这里可以做简单负载均衡,暂时用不上
     */
//    private int dataSourceOrder;
//    private AtomicInteger count = new AtomicInteger(0);
    public MyRoutingDataSource() {
    }

//    public MyRoutingDataSource(int _dataSourceOrder){
//        this.dataSourceOrder = _dataSourceOrder;
//    }

    /**
     * 这个方法会根据返回的key去配置文件查找数据源
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getTargetDataSource();
    }
}
TargetDataSource.java
/**
 * Created by ZhenWeiLai on 2016/11/22.
 * 配置读写分离或者多数据源
 */
public enum TargetDataSource {

    WRITE("write","主库"), READ("read","从库"), CREATE("create","建库");

    final private String code;

    final private String name;

    TargetDataSource(String _code,String _name) {
        this.code = _code;
        this.name = _name;
    }

    public String getCode() {
        return code;
    }

    public String getName(){
        return name;
    }

    public static String getNameByCode(String _code){
        for(TargetDataSource item : TargetDataSource.values()){
            if(item.getCode().equals(_code)){
                return item.getName();
            }
        }
        return "";
    }
}

喜欢的话,可以使用AOP 注解切换数据源,但是要注意滥用AOP,加上全局统一AOP异常拦截,以及Spring AOP事务,顺序问题,处理不好可能会引起事务失效,或者异常拦截失效,也可能是数据源切换失效.

AOP事务如果先开启了,那么AOP切换数据源将无效

/**
 * Created by ZhenWeiLai on 2016/11/22.
 * 切换数据源的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSwitch {
    String targetDataSource() default "";
}
DataSourceContextHolder.java
/**
 * Created by ZhenWeiLai on 2016/11/22.
 * 多数据源/读写分离
 */
public class DataSourceContextHolder {
    private static final ThreadLocal<String> dataSourceLocal = new ThreadLocal<>();

    public static ThreadLocal<String> getDataSourceLocal() {
        return dataSourceLocal;
    }

    /**
     * 从库 可以有多个
     */
    public static void read() {
        dataSourceLocal.set(TargetDataSource.READ.getCode());
    }

    /**
     * 主库 只有一个
     */
    public static void write() {
        dataSourceLocal.set(TargetDataSource.WRITE.getCode());
    }

    public static String getTargetDataSource() {
        return dataSourceLocal.get();
    }
}
原文地址:https://www.cnblogs.com/sweetchildomine/p/6977987.html