zoukankan      html  css  js  c++  java
  • 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();
        }
    }
  • 相关阅读:
    Postman+Newman+Git+JenKins+钉钉(接口自动化测试持续集成)CI_知识回顾与整理
    GitHub 使用入门
    python中json文件处理涉及的四个函数json.dumps()和json.loads()、json.dump()和json.load()的区分
    python实现:重复列表按重复次数排序
    python中lambda匿名函数的用法
    Python中各种符号的意义
    OSI七层协议和TCP/IP的4层、5层协议
    Jmeter中传参输入位置[chrome中区分是否json传参]
    Jmeter中http cookie Manager、Http Header Manager
    jQuery 获取屏幕高度、宽度
  • 原文地址:https://www.cnblogs.com/sweetchildomine/p/6977987.html
Copyright © 2011-2022 走看看