1、在application.properties文件中配置多个数据源
#主数据源
spring.datasource.core.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.core.jdbc-url=jdbc:mysql://127.0.0.1:3306/boot_v2?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
spring.datasource.core.username=root
spring.datasource.core.password=root
#日志数据源
spring.datasource.log.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.log.jdbc-url=jdbc:mysql://127.0.0.1:3306/log?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
spring.datasource.log.username=root
spring.datasource.log.password=root
2、编写自定义注解TargetDataSource,后面将通过自定义注解@TargetDataSource + AOP的方式实现数据源动态切换
package com.ldy.bootv2.demo.datasource.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>类名: TargetDataSource</p>
* <p>描述: 数据源切换注解</p>
* <p>修改时间: 2019年4月18日 上午10:39:01</p>
* @author lidongyang
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface TargetDataSource {
public static final String LOG = "log";
public static final String CORE = "core";
String value() default CORE;
}
3、编写DataSourceContextHolder,根据当前线程来选择具体的数据源
package com.ldy.bootv2.demo.datasource;
/**
* <p>类名: DataSourceContextHolder</p>
* <p>描述:根据当前线程来选择具体的数据源</p>
* <p>修改时间: 2019年4月18日 上午10:34:48</p>
* @author lidongyang
*/
public class DataSourceContextHolder {
/**
* 使用ThreadLocal存储数据源
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
/**
* 指定要使用的数据源
*/
public static void setDataSource(String dbName) {
contextHolder.set(dbName);
}
/**
* 获取数据源
*/
public static String getDataSource() {
return (contextHolder.get());
}
/**
* 清空当前指定数据源,使用默认数据源
*/
public static void clearDataSource() {
contextHolder.remove();
}
}
4、编写DynamicDataSource,继承AbstractRoutingDataSource,动态设置数据源
package com.ldy.bootv2.demo.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* <p>类名: DynamicDataSource</p>
* <p>描述: 动态数据源</p>
* <p>修改时间: 2019年4月18日 上午10:33:33</p>
* @author lidongyang
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
5、编写多数据源配置类DataSourceConfig
package com.ldy.bootv2.demo.datasource.config;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import com.ldy.bootv2.demo.datasource.DynamicDataSource;
import com.ldy.bootv2.demo.datasource.annotation.TargetDataSource;
/**
* <p>类名: DataSourceConfig</p>
* <p>描述: 多数据源配置类</p>
* <p>修改时间: 2019年4月18日 上午10:29:17</p>
*
* @author lidongyang
*/
@Configuration
public class DataSourceConfig {
/**
* 默认数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.core")
public DataSource dataSourceCore() {
return DataSourceBuilder.create().build();
}
/**
* 日志数据库
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.log")
public DataSource dataSourceLog() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* 将数据库实例写入到targetDataSources属性中,并且使用defaultTargetDataSource属性设置默认数据源。
* Primary注解用于标识默认使用的 DataSource Bean,并注入到SqlSessionFactory的dataSource属性中去。
*/
@Primary
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSourceCore());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<Object, Object>();
dsMap.put(TargetDataSource.CORE, dataSourceCore());
dsMap.put(TargetDataSource.LOG, dataSourceLog());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* 配置@Transactional注解事物
* 使用dynamicDataSource作为transactionManager的入参来构造DataSourceTransactionManager
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
6、编写 DataSourceAspect,通过AOP动态切换数据源
package com.ldy.bootv2.demo.datasource.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.ldy.bootv2.demo.datasource.DataSourceContextHolder;
import com.ldy.bootv2.demo.datasource.annotation.TargetDataSource;
/**
* <p>类名: DynamicDataSourceAspect</p>
* <p>描述: 自定义注解@TargetDataSource + AOP的方式实现数据源动态切换。</p>
* <p>修改时间: 2019年4月18日 上午10:42:50</p>
* @author lidongyang
*/
@Aspect
@Component
@Order(-1) // 保证该AOP在@Transactional之前执行
public class DataSourceAspect {
private static Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
@Before("@annotation(dataSource)")
public void beforeSwitchDataSource(JoinPoint point, TargetDataSource dataSource) throws Throwable {
logger.info("切换数据库 [" + dataSource.value() + "] " + point.getSignature());
try {
// 切换数据源
DataSourceContextHolder.setDataSource(dataSource.value());
} catch (Exception e) {
logger.error("切换数据库失败,使用默认数据库!原因:" + e.getMessage());
}
}
@After("@annotation(dataSource)")
public void afterSwitchDataSource(JoinPoint point, TargetDataSource dataSource) {
logger.info("移除数据库 [" + dataSource.value() + "] " + point.getSignature());
//方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
DataSourceContextHolder.clearDataSource();
}
}
7、在service层通过自定义注解TargetDataSource指定要使用的数据源,不指定则使用配置中的默认数据源
(1)使用默认数据源,不用加自定义注解TargetDataSource
public List<UserEntity> getAll() {
return userMapper.getAll();
}
public UserEntity getOne(Integer id) {
return userMapper.getOne(id);
}
(2)使用指定数据源,需要通过自定义注解TargetDataSource指定要使用的数据源
@TargetDataSource(value=TargetDataSource.LOG)
public List<UserLogEntity> getAll() {
return userLogMapper.getAll();
}
@TargetDataSource(value=TargetDataSource.LOG)
public UserLogEntity getOne(Integer id) {
return userLogMapper.getOne(id);
}
最后附一张项目结构图