一、application.yml配置文件
server:
port: 8080
servlet:
context-path: /cm
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
username: username
password: password
url: jdbc:mysql://xxxxx:xxxx/test?autoReconnect=true&useCompression=true&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT
initial-size: 1
min-idle: 1
max-active: 50
max-wait: 60000
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
username: xxxx
password: xxxxxx
url: jdbc:mysql://xxxx:xxxx/test?autoReconnect=true&useCompression=true&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT
initial-size: 1
min-idle: 1
max-active: 50
max-wait: 60000
mybatis:
mapper-locations: classpath:mapper
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.type}")
public Class extends DataSource> dataSourceType;
@Bean(name = "masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.druid.master")
public DataSource masterDataSource(){
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.slave")
public DataSource slaveDataSource(){
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean
public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource){
Map
三、mybatis配置
package com.cm.server.busi.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
@AutoConfigureAfter({ DataSourceConfig.class })
public class MybatisConfig {
@Resource(name = "routingDataSource")
private DataSource routingDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(routingDataSource);
// 下面的不可少,这个有了,就不用配置文件里的
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
五、通过ThreadLocal将数据源设置到每个线程上下文中
package com.cm.server.busi.config;
import com.cm.server.busi.enums.DataSourceTypeEnum;
public class DatabaseContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DataSourceTypeEnum databaseType) {
if (databaseType == null) {
throw new NullPointerException();
}
contextHolder.set(databaseType);
}
public static DataSourceTypeEnum getDatabaseType() {
return contextHolder.get() == null ? DataSourceTypeEnum.master : (DataSourceTypeEnum) contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
六、通过自动义注解,实现AOP拦截,达到切换数据源的目的。注:@EnableAspectJAutoProxy这个注解加上才能使自定义的注解生效
package com.cm.server.busi.annotation;
import com.cm.server.busi.config.DatabaseContextHolder;
import com.cm.server.busi.enums.DataSourceTypeEnum;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
// 加了下面这个注解,自定义的标签才会生效
@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)
public class DataSourceAop {
@Pointcut("@annotation(com.cm.server.busi.annotation.ReadDataSource) && execution(* com.cm.server.busi.service.impl..*.*(..))")
public void readPointcut(){}
@Pointcut("@annotation(com.cm.server.busi.annotation.MasterDataSource) && execution(* com.cm.server.busi.service.impl..*.*(..))")
public void writePointcut(){}
@Before("readPointcut()")
public void readBefore(JoinPoint joinPoint) {
DatabaseContextHolder.setDatabaseType(DataSourceTypeEnum.slave);
System.out.println("USE DATASOURCE SLAVE");
}
@Before("writePointcut()")
public void writeBefore(JoinPoint joinPoint) {
DatabaseContextHolder.setDatabaseType(DataSourceTypeEnum.master);
System.out.println("USE DATASOURCE MASTER");
}
@After("readPointcut() || writePointcut()")
public void after() {
DatabaseContextHolder.clearDatabaseType();
}
}
访问只读库注解
package com.cm.server.busi.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ReadDataSource {
}
访问读写库注解
package com.cm.server.busi.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MasterDataSource {
}
附上dataType枚举类
package com.cm.server.busi.enums;
public enum DataSourceTypeEnum {
master,slave;
}
7、最后在业务层代码加上相应的注解即可
package com.cm.server.busi.service.impl;
import com.cm.server.busi.annotation.MasterDataSource;
import com.cm.server.busi.annotation.ReadDataSource;
import com.cm.server.busi.mapper.CmServerBusiMapper;
import com.cm.server.busi.model.DataVo;
import com.cm.server.busi.service.ICmServerBusiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class CmServerBusiServiceImpl implements ICmServerBusiService {
@Autowired
private CmServerBusiMapper cmServerBusiMapper;
@Override
@ReadDataSource
public Map selectDataInfo() {
return cmServerBusiMapper.selectDataInfo();
}
@Override
@MasterDataSource
public void updateDataInfo(DataVo dataVo) {
cmServerBusiMapper.updateDataInfo(dataVo);
}
}
八、测试
参考:SpringBoot+MyBatis+MySQL读写分离 - 废物大师兄 - 博客园



