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();
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM