springboot注解配置多数据源
在项目遇到多数据源的问题,主要是针对部分数据需要单独处理应用场景;
1.本次项目配置基于springboot+mybatis+maven集成的,首先在yml配置相关如下:其中需要注意单数据源是url,多数据源要改成 jdbc-url,否则会报异常(Error querying database. Cause: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName)
server: port: 8081 spring: datasource: database1: jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver database2: jdbc-url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver #mybatis config mybatis: mapper-locations: classpath*:mapper/*/*.xml
2.配置好了,需要在启动类先加上@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})这个类位置:
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
3.因为需要用到springboot的aop所以需要导包,在pom.xml文件导入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
4.多数据源的结构以下:
内容直接给大家展示:
1.DataSourceAspect
package com.example.demo.config.db.dataSource; import lombok.extern.slf4j.Slf4j; 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.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 多数据源配置, 拦截器配置 * @author lxp * @create 2021-12-28 12:15 **/ @Aspect @Component @Slf4j // 优先级, 1标识最先执行 @Order(1) public class DataSourceAspect { @Pointcut("execution(* com.example.demo.mapper..*.*(..)))") public void dataSourcePoint() {} @Before("dataSourcePoint()") public void before(JoinPoint joinPoint) { Object target = joinPoint.getTarget(); MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); // 执行方法名 String methodName = methodSignature.getName(); // 方法参数 Class[] parameterTypes = methodSignature.getParameterTypes(); try { // 获取方法, 直接getClass获取对象可能为代理对象 Method method = target.getClass().getInterfaces()[0].getMethod(methodName, parameterTypes); // 添加默认数据源 String dataSource = DataSourceType.Master.getName(); if (null != method && method.isAnnotationPresent(MyDataSource.class)) { MyDataSource targetDataSource = method.getAnnotation(MyDataSource.class); dataSource = targetDataSource.value().getName(); } // 此处添加线程对应的数据源到上下文 // 在AbstractRoutingDataSource子类中拿到数据源, 加载后进行配置 JdbcContextHolder.putDataSource(dataSource); log.info("generate data source : " + dataSource); } catch (Exception e) { log.info("error", e); } } /** * 清除数据源, 方法执行完成后, 清除数据源 */ @After("dataSourcePoint()") public void after(JoinPoint joinPoint) { JdbcContextHolder.clear(); } }
2.DataSourceConfig
package com.example.demo.config.db.dataSource; 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 javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 数据源配置 */ @Configuration public class DataSourceConfig { @Bean(name = "database1") @ConfigurationProperties(prefix = "spring.datasource.database1") public DataSource dataSource1() { //database1数据源 return DataSourceBuilder.create().build(); } @Bean(name = "database2") @ConfigurationProperties(prefix = "spring.datasource.database2") public DataSource dataSource2() { //database2数据源 return DataSourceBuilder.create().build(); } @Bean(name="dynamicDataSource") @Primary //优先使用,多数据源 public DataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 设置默认数据源为first数据源 dynamicDataSource.setDefaultTargetDataSource(dataSource1()); // 配置多数据源, // 添加数据源标识和DataSource引用到目标源映射 Map<Object, Object> dataSourceMap = new HashMap<>(); dataSourceMap.put(DataSourceType.Master.getName(), dataSource1()); dataSourceMap.put(DataSourceType.Slave.getName(), dataSource2()); dynamicDataSource.setTargetDataSources(dataSourceMap); return dynamicDataSource; } @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } }
3.DataSourceType
package com.example.demo.config.db.dataSource; public enum DataSourceType { Master("database1"), Slave("database2"); private String name; private DataSourceType(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
4.DynamicDataSource
package com.example.demo.config.db.dataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override protected Object determineCurrentLookupKey() { logger.info("数据源为{}",JdbcContextHolder.getDataSource()); return JdbcContextHolder.getDataSource(); } }
5.JdbcContextHolder
package com.example.demo.config.db.dataSource; /** * 动态数据源的上下文 threadlocal */ public class JdbcContextHolder { private final static ThreadLocal<String> local = new ThreadLocal<>(); public static void putDataSource(String name) { local.set(name); } public static String getDataSource() { return local.get(); } /** * 清除数据源 */ public static void clear() { local.remove(); } }
6.MyDataSource
package com.example.demo.config.db.dataSource; 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 MyDataSource { DataSourceType value(); //默认主表 }
最后应用就比较简单了,默认情况有默认的数据库源,需要调用别的就加注解即可
最后通过调用接口可以看到数据是不同数据库的数据,完整的过程基本都有,多数据源要把dao层和xml文件隔离开更好的区分!!!