本文将介绍使用Spring Boot集成Mybatis并实现主从库分离的实现(适用于多数据源),网上有很多讲述多数据源的,有的太复杂,有的不生效,我下面的是直接可以用的,而且是最简单的,这里写出来,和大家共同学习,如有不对,请指出来,感谢,本人企鹅:761173739,废话少说,直接来:
com.sunny.study
multiple-datasource
0.0.1-SNAPSHOT
jar
springboot-multiple-datasource
多数据源
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-undertow
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.4
org.springframework.boot
spring-boot-starter-test
test
org.apache.commons
commons-lang3
com.alibaba
druid
1.0.13
mysql
mysql-connector-java
org.springframework.boot
spring-boot-configuration-processor
true
注意:项目是通过扩展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration来实现多数据源注入的,这也是最简洁的方式
# 数据库访问配置
# 使用druid数据源
druid:
type: com.alibaba.druid.pool.DruidDataSource
master:
url: jdbc:mysql://127.0.0.1:3306/test_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 3
maxWait: 60000
minIdle: 3
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
slave:
url: jdbc:mysql://127.0.0.1:3306/test_db2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 3
maxWait: 60000
minIdle: 3
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration {
@Value("${druid.type}")
private Class extends DataSource> dataSourceType;
@Bean(name="masterDataSource")
@Primary
@ConfigurationProperties(prefix = "druid.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "druid.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
}
@Configuration
@AutoConfigureAfter({DataSourceConfiguration.class})
public class MybatisConfiguration extends MybatisAutoConfiguration {
public MybatisConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider,
ResourceLoader resourceLoader, ObjectProvider databaseIdProvider,
ObjectProvider> configurationCustomizersProvider) {
super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
}
private static Log logger = LogFactory.getLog(MybatisConfiguration.class);
@Resource(name = "masterDataSource")
private DataSource masterDataSource;
@Resource(name = "slaveDataSource")
private DataSource slaveDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
logger.info("-------------------- 重载父类 sqlSessionFactory init ---------------------");
return super.sqlSessionFactory(roundRobinDataSouceProxy());
}
public AbstractRoutingDataSource roundRobinDataSouceProxy(){
DbRouteDataSource proxy = new DbRouteDataSource();
@SuppressWarnings("unchecked")
Map
@Configuration
@AutoConfigureAfter({DataSourceConfiguration.class})
public class MybatisConfiguration extends MybatisAutoConfiguration {
public MybatisConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider,
ResourceLoader resourceLoader, ObjectProvider databaseIdProvider,
ObjectProvider> configurationCustomizersProvider) {
super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
}
private static Log logger = LogFactory.getLog(MybatisConfiguration.class);
@Resource(name = "masterDataSource")
private DataSource masterDataSource;
@Resource(name = "slaveDataSource")
private DataSource slaveDataSource;
@Bean
@Override
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
logger.info("-------------------- 重载父类 sqlSessionFactory init ---------------------");
return super.sqlSessionFactory(roundRobinDataSouceProxy());
}
public AbstractRoutingDataSource roundRobinDataSouceProxy(){
DbRouteDataSource proxy = new DbRouteDataSource();
Map
public class DbContextHolder {
public enum DbType{
/***
* 主数据源
*/
MASTER,
/***
* 从数据源
*/
SLAVE
}
private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDbType(DbType dbType){
if(dbType==null){
throw new NullPointerException();
}else{
CONTEXT_HOLDER.set(dbType);
}
}
public static DbType getDbType(){
return CONTEXT_HOLDER.get()==null? DbType.MASTER:CONTEXT_HOLDER.get();
}
public static void clearDbType() {
CONTEXT_HOLDER.remove();
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SlaveDataSource {
}
@Aspect
@Component
public class SlaveDataSourceInterceptor implements Ordered {
public static final Logger logger = LoggerFactory.getLogger(SlaveDataSourceInterceptor.class);
@Around("@annotation(slaveDataSource)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, SlaveDataSource slaveDataSource) throws Throwable {
try {
logger.debug("set database connection to read only");
DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
Object result = proceedingJoinPoint.proceed();
return result;
}finally {
DbContextHolder.clearDbType();
logger.debug("restore database connection");
}
}
@Override
public int getOrder() {
return 0;
}
}
@SlaveDataSource
@RequestMapping(value = "/abc/xxx", method = RequestMethod.POST)
public Object queryList(@RequestBody Object vo) throws BaseException {
return "ok";
}