在項目遇到多數據源的問題,主要是針對部分數據需要單獨處理應用場景;
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文件隔離開更好的區分!!!