SpringBoot+mybatis+Druid多数据源切换

1. 配置application.properties

#primary db
spring.datasource.primary.url=jdbc:mysql://127.0.0.1:3306/master?characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.primary.filters=stat
spring.datasource.primary.maxActive=20
spring.datasource.primary.initialSize=1
spring.datasource.primary.maxWait=60000
spring.datasource.primary.minIdle=1
spring.datasource.primary.timeBetweenEvictionRunsMillis=60000
spring.datasource.primary.minEvictableIdleTimeMillis=300000
spring.datasource.primary.validationQuery=select 'x'
spring.datasource.primary.testWhileIdle=true
spring.datasource.primary.testOnBorrow=false
spring.datasource.primary.testOnReturn=false
spring.datasource.primary.poolPreparedStatements=true
spring.datasource.primary.maxOpenPreparedStatements=20

#slave db1
spring.datasource.secondary.url=jdbc:mysql://127.0.0.1:3306/slave?characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.filters=stat
spring.datasource.secondary.maxActive=20
spring.datasource.secondary.initialSize=1
spring.datasource.secondary.maxWait=60000
spring.datasource.secondary.minIdle=1
spring.datasource.secondary.timeBetweenEvictionRunsMillis=60000
spring.datasource.secondary.minEvictableIdleTimeMillis=300000
spring.datasource.secondary.validationQuery=select 'x'
spring.datasource.secondary.testWhileIdle=true
spring.datasource.secondary.testOnBorrow=false
spring.datasource.secondary.testOnReturn=false
spring.datasource.secondary.poolPreparedStatements=true
spring.datasource.secondary.maxOpenPreparedStatements=20

△这里注意的是一定要指定主数据源(主数据源用primary标识)

2. 配置主数据源PrimaryDataSourceConfig

@Configuration
@MapperScan(basePackages ="xx.xxx.xx.mapper.primary",sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig{

    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    @Primary
    public DataSource primaryDataSource() { 
        return new DruidDataSource();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
         return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource)
    throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("xx.xx.xx.model");
        //分页插件
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        //添加插件
        bean.setPlugins(new Interceptor[]{pageHelper});
        //添加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);
        }
    }

    @Bean(name = "primarySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory")
        SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @Primary
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("primarySqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("xx.xx.xx.mapper.primary");
        Properties properties = new Properties();
        properties.setProperty("mappers", "xx.xx.xx.util.MyMapper");
        properties.setProperty("notEmpty", "false");
        properties.setProperty("IDENTITY", "MYSQL");
        mapperScannerConfigurer.setProperties(properties);
        return mapperScannerConfigurer;
    }
}

△多个数据源要标识哪一个为主数据源所以这里PrimaryDataSourceConfig设置为主数据源需要用@primary来标识是主数据源(默认使用主数据源)

3.配置次数据源SlaveOneDataSourceConfig

@Configuration
@MapperScan(basePackages ="xx.xx.xx.mapper.slave1",sqlSessionTemplateRef = "slaveOneSqlSessionTemplate")
public class SlaveOneDataSourceConfig{

    @Bean(name = "slaveOneDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource slaveOneDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "slaveOneTransactionManager")
    public PlatformTransactionManager slaveOneTransactionManager(@Qualifier("slaveOneDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "slaveOneSqlSessionFactory")
    public SqlSessionFactory slaveOneSqlSessionFactory(@Qualifier("slaveOneDataSource") DataSource dataSource) 
    throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("xx.xx.xx.model");
        //分页插件
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        //添加插件
        bean.setPlugins(new Interceptor[]{pageHelper});
        //添加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);
        }
    }

    @Bean(name = "slaveOneSqlSessionTemplate")
    public SqlSessionTemplate slaveOneSqlSessionTemplate(@Qualifier("slaveOneSqlSessionFactory")
    SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("slaveOneSqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("cn.ctx.admin.mapper.slave1");
        Properties properties = new Properties();
        properties.setProperty("mappers", "xx.xx.xx.util.MyMapper");
        properties.setProperty("notEmpty", "false");
        properties.setProperty("IDENTITY", "MYSQL");
        mapperScannerConfigurer.setProperties(properties);
        return mapperScannerConfigurer;
    }
}

@MapperScan(basePackages =”cn.ctx.admin.mapper”,sqlSessionTemplateRef = “sqlSessionTemplate”)
basePackages 对应的dao mybatis会根据不同的dao切换数据源

4. 多数据源使用事物

  • 程序入口需要加入 @EnableTransactionManagement 注解开启注解,等同于xml配置方式的 <tx:annotation-driven />
   //△多数据源 value 指定需要回滚的数据源一定要写
   @Transactional(value="slaveOneTransactionManager")
   @Override
   public Map<String, Object> addSlaveData() {
       Map<String,Object> map=new HashMap<String,Object>();
       try {
           int i=userInfoMapper.addSlaveData();
           if(i>0) {
               map.put("result","写入成功");
           }else {
               map.put("result","写入失败");
           }
       }catch (Exception e) {
           map.put("result","写入失败");
           //异常手动回滚,好处上层就无需去处理异常
           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
       }
       return map;
   }

△注意,发现事务不回滚
原因:
- 默认spring事务只在发生未被捕获的 RuntimeException 时才回滚。
- spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获 RuntimeException 的异常,但可以通过配置来捕获特定的异常并回滚
换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获而回滚
- 解决方案:
- 方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
- 方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常

猜你喜欢

转载自blog.csdn.net/BeiShangBuZaiLai/article/details/81565678