数据源配置
<!-- alibaba的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
创建用来保存数据源名字的线程安全的类。通过ThreadLocal栈封闭方式保证线程安全。
public class DataSourceContextHolder {
public static final String Mater = "master";
public static final String Slave1 = "slave1";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String name){
contextHolder.set(name);
}
public static String getDataSource(){
return contextHolder.get();
}
public static void cleanDataSource(){
contextHolder.remove();
}
}
实现AbstractRoutingDataSource并重写determineCurrentLookupKey()方法
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
AbstractRoutingDataSource类源码分析,通过resolvedDataSources中以map形式保存着多个数据源。通过重写的determineCurrentLookupKey()来动态的获得当前线程需要的数据源名字。
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
配置其他基础数据源
@Configuration
public class MultipleDateSourceConfig {
/**
*
* @return
*/
@Bean("master")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource creeateMasterDataSource(){
return new DruidDataSource();
}
@Bean("slave1")
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource creeateSlave1DataSource(){
return new DruidDataSource();
}
/**
* 设置动态数据源,通过@Primary 来确定主DataSource
* @return
*/
@Bean
@Primary
public DataSource createDynamicdataSource(@Qualifier("master") DataSource master,@Qualifier("slave1") DataSource slave1){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(master);
//配置多数据源
Map<Object, Object> map = new HashMap<>();
map.put("master",master);
map.put("slave1",slave1);
dynamicDataSource.setTargetDataSources(map);
return dynamicDataSource;
}
}
注意:
1.@Bean创建类的名字默认方法名,指定bean名字通过value或name属性。
2.createDynamicdataSource()中DataSource默认按类型注入,但是会报错。需要通过@Qualifier来指定注入bean的名字。往mybatis的sqlSessionFactory类注入DataSource是按类型注入。
3.通过@Primary注解来说明优先注入此bean。
上述的@ConfigurationProperties(prefix = "spring.datasource.master")注解从application.yml读取配置信息并注入
spring:
datasource:
#type: com.alibaba.druid.pool.DruidDataSource
master:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3307/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
name: master
# 监控统计拦截的filters 有stat,wall,log4j
filters: stat
slave1:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
name: slave1
filters: stat
注意:添加filters的stat属性后,我们可以在localhost:8080/druid/sql.html 看到Druid对sql的分析管理信息
我们是手动配置DataSource,需要注解掉springboot的自动数据源配置类DataSourceAutoConfiguration
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan("com.test.mspringboot.mapper") //扫描mapper包
public class MSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MSpringBootApplication.class, args);
}
}
动态切换配置
通过自定义注解+AOP的方式来实现动态数据源的切换
引入jar
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DataSource {
String value() default "master";
}
定义切面
@Aspect
@Component
@Order(1) //需要加入切面排序
public class DynamicDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
//切入点只对@Service注解的类上的@DataSource方法生效
@Pointcut(value="@within(org.springframework.stereotype.Service) && @annotation(dataSource)" )
public void dynamicDataSourcePointCut(DataSource dataSource){}
@Before(value = "dynamicDataSourcePointCut(dataSource)")
public void switchDataSource(DataSource dataSource) throws Throwable{
logger.info("##############数据源 :{}###############",dataSource.value());
DataSourceContextHolder.setDataSource(dataSource.value());
}
@After(value="dynamicDataSourcePointCut(dataSource)")
public void after(DataSource dataSource){
DataSourceContextHolder.cleanDataSource();
}
}
注意:需要使用@Order(1) 注解,保证数据源的切换在数据源的获取之前。
使用
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@DataSource(DataSourceContextHolder.Slave1)
@Transactional
@Override
public User searchUserById(int id) throws Exception {
return userMapper.selectByPrimaryKey(id);
}
}