什么意思呢?就是一个项目配置多个数据库,比如在一个方法里,操作两个不同的数据库中的表!
我们的核心思想是:使用AOP+注解的方法来实现;
1、pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
2、启动类配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})//排除自动注入数据源
@EnableAspectJAutoProxy(exposeProxy = true) //aop代理
@MapperScan(basePackages = "com.casicloud.mapper")
3、application.yml 配置
spring:
datasource:
cloud:
url: jdbc:mysql://xxxx:3306/cloud?useUnicode=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
test:
url: jdbc:mysql://xxxx:3306/test?useUnicode=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4、需要定义一个enum类:
/**
* 数据源枚举类
*/
public enum DBTypeEnum {
CLOUD_DB("cloud"),
TEST_DB("test")
;
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private DBTypeEnum(String value) {
this.value = value;
}
}
5、一个注解类
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DB {
DBTypeEnum value() default DBTypeEnum.CLOUD_DB;
}
6、动态数据源切换类: AbstractRoutingDataSource
AbstractRoutingDataSource 类中 TargetDataSources(Map) 存放 key-datasource
重写的方法是获取key的方法
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbKey();
}
}
7、使用ThreadLocal存放数据源的key
public class DbContextHolder {
private static final ThreadLocal dbKey=new ThreadLocal<>();
public static void setDbKey(DBTypeEnum dbTypeEnum){
dbKey.set(dbTypeEnum.getValue());
}
public static String getDbKey(){
return (String)dbKey.get();
}
public static void cleanDbKey(){
dbKey.remove();
}
}
8、Druid数据源,这是真正的数据源
@Configuration
public class DruidConfig {
//cloud 数据源
@ConfigurationProperties(prefix = "spring.datasource.cloud")
@Bean("cloud")
public DataSource getCloudDateSource(){
return new DruidDataSource();
}
//test数据源
@ConfigurationProperties(prefix = "spring.datasource.test")
@Bean("test")
public DataSource getTestDateSource(){
return new DruidDataSource();
}
//往动态数据源切换类中set key-datasource
@Bean
@Primary
public DataSource multipartDataSource(@Qualifier("cloud") DataSource cloudDataSource,@Qualifier("test") DataSource testDataSource){
DynamicDataSource dynamicDataSource=new DynamicDataSource();
Map<Object,Object> targetDataSource=new HashMap<>();
targetDataSource.put(DBTypeEnum.CLOUD_DB.getValue(),cloudDataSource);
targetDataSource.put(DBTypeEnum.TEST_DB.getValue(),testDataSource);
dynamicDataSource.setTargetDataSources(targetDataSource);
return dynamicDataSource;
}
// 往sqlSessionFactory中set数据源
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(multipartDataSource(getCloudDateSource(),getTestDateSource()));
//这里可以配置你的mapper.xml 的路径
// PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
// sqlSessionFactoryBean.setMapperLocations(patternResolver.getResources("classpath:com/casicloud/mapper/*Mapper.xml"));
return sqlSessionFactoryBean.getObject();
}
}
9、AOP 代理
@Component
@Aspect
@Order(-100)
public class DataSourceSwitchAspect {
@Pointcut("execution(* com.casicloud.service.impl..*.*(..))")
private void dbAspect() {
}
@Before("dbAspect()")
public void ChangeDataSource(JoinPoint joinPoint){
Class<?> className = joinPoint.getTarget().getClass();
//获得访问的方法名
String methodName = joinPoint.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)joinPoint.getSignature()).getParameterTypes();
try {
Method method = className.getMethod(methodName, argClass);
//注解在class上
if(className.isAnnotationPresent(DB.class)){
DB annotation = className.getAnnotation(DB.class);
DbContextHolder.setDbKey(annotation.value());
}
//注解在method上
if(method.isAnnotationPresent(DB.class)){
DB annotation = method.getAnnotation(DB.class);
DbContextHolder.setDbKey(annotation.value());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
10、到此,就可以实现了
通过在切入点上加注解,来实现动态的数据切换
@Override
@DB(DBTypeEnum.CLOUD_DB)
public void addUser(User user) {
userMapper.addUser(user);
}
@Override
@DB(DBTypeEnum.TEST_DB)
public void addStudent(Student student) {
studentMapper.addStudent(student);
}
两个数据库,而我在controller中可以这样操作!
@RequestMapping(value = "/addUserAndStudent",method = RequestMethod.GET)
public void addUserAndStudent(){
userService.addStudent(new Student(idWorker.nextId(),"和凯凯","男",19));
userService.addUser(new User(idWorker.nextId(),"和凯凯","123456",LocalDateTime.now()));
}
最后附上Githup链接:https://github.com/1277782574/SpringBootDynamicDataSource