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();语句,手动回滚,这样上层就无需去处理异常
赏
使用支付宝打赏
使用微信打赏
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章