1. 创建两个数据库master和slave
2. 在application.yml添加如下配置
spring:
profiles: dev
datasource:
master:
url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=GBK
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
slave:
url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=GBK
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
3. 创建切换数据源注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/***
*
* @Title: springstarter
* @author shy
* @Description 自定义注解
* @data 2019年3月28日 下午3:52:49
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DS {
String value();
}
4.多数据源配置类
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
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;
/***
*
* @Title: springstarter
* @author shy
* @Description 多数据源配置类
* @data 2019年3月28日 下午3:56:13
*
*/
@Configuration
public class DataSourceConfig {
/***
* @Description: master数据源
* @param @return
* @return DataSource
* @throws @throws
*/
@Bean(name = "master")
@ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中对应属性的前缀
public DataSource dataSourceMaster() {
return DataSourceBuilder.create().build();
}
/***
* @Description: slave数据源
* @param @return
* @return DataSource
* @throws @throws
*/
@Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource dataSourceSlave() {
return DataSourceBuilder.create().build();
}
/***
*
* @Description: 通过AOP在不同数据源之间动态切换
* @param @return
* @return DataSource
* @throws @throws
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSourceMaster());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<Object, Object>();
dsMap.put("master", dataSourceMaster());
dsMap.put("slave", dataSourceSlave());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* 配置@Transactional注解事物
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
5. 动态切换数据源
/***
*
* @Title: springstarter
* @author shy
* @Description 动态切换数据源;
* @data 2019年3月28日 下午3:47:51
*
*/
@Slf4j
public class DataSourceContextHolder {
/***
* 默认数据源
*/
public static final String DEFAULT_DS = "master";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
/**
* @return the contextholder
*/
public static ThreadLocal<String> getContextholder() {
return contextHolder;
}
// 设置数据源名
public static void setDB(String dbType) {
log.info("切换到{"+dbType+"}数据源");
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
6. 获取当前数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDB();
}
}
7. AOP的方式实现数据源动态切换
import java.lang.reflect.Method;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
/***
* @Title: springstarter
* @author shy
* @Description AOP的方式实现数据源动态切换
* @data 2019年3月28日 下午3:59:04
*
*/
@Aspect
@Order(-10)//保证该AOP在@Transactional之前执行
@Component
@Slf4j
public class DynamicDataSourceAspect {
@SuppressWarnings("rawtypes")
@Before("@annotation(DS)")
public void beforeSwitchDS(JoinPoint point) {//, DS ds
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
log.info("当前访问的CLASS :" + className.getName());
log.info("当前访问的METHOD :" + methodName);
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
// 取出注解中的数据源名
dataSource = annotation.value();
log.info("取出注解中的数据源名 :" + dataSource);
}
} catch (Exception e) {
e.printStackTrace();
log.info("beforeSwitchDS Exception : " + e);
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
@After("@annotation(DS)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
/* @AfterThrowing("@annotation(DS)")
public void afterThrowing(Exception e) {
}*/
}
8. @DS使用
@DS不能在Service和dao层接口的方法上使用,而是在Service实现类中使用
@DS("slave")
@Override
public List<Map<String, Object>> getLoggerInfoList(String targetName, Integer action, Page page) {
Integer startSize = page.getStartSize(page.getPageNum(), page.getPageSize());
List<Map<String, Object>> loggerInfoList = loggerMapper.getLoggerInfoList(targetName, action, startSize, page.getPageSize());
return loggerInfoList;
}
@DS("master")
@Override
public List<User> getUserListByPage(Page page) throws Exception {
Integer pageSatrtSize = page.getStartSize(page.getPageNum(), page.getPageSize());
Integer pageSize = page.getPageSize();
String order = "user_id";
UserExample example = new UserExample();
//Criteria criteria = example.createCriteria();
int count = userMapper.countByExample(example);
page.setTotalSize(count);
List<User> userList = userMapper.getUserListByPage(pageSatrtSize, pageSize, order);
String sexStr = "";
if (userList != null && userList.size() > 0) {
for (User user : userList) {
Date birthday = user.getBirthday();
String birthdayStr = CommonUtils.dateFormat(birthday, Constant.YYMMDD);
user.setBirthdayStr(birthdayStr);
Integer sex = user.getSex() == null ? Constant.SECRECY_SEX : user.getSex();
if (sex.equals(Constant.SECRECY_SEX)) {
sexStr = Constant.SECRECY;
} else if (sex.equals(Constant.MAN_SEX)) {
sexStr = Constant.MAN;
} else if (sex.equals(Constant.WOMAN_SEX)) {
sexStr = Constant.WOMAN;
}
user.setSexStr(sexStr);
}
}
return userList;
}
9. 结果
slave数据源:
master数据源: