1、相关配置类的截图:
DatabaseContextHolder:
/**
* @author haijin
* @description: 线程安全类,存放数据源
* @date 2024/12/25 14:05
*/
public class DatabaseContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static final String PRIMARY_DATA_SOURCE = "primaryDataSource";
public static final String SMS_DATA_SOURCE = "ortherDataSource";
/**
* 放入
* @param type
*/
public static void setDataBase(String type){
contextHolder.set(type);
}
/**
* 获取
*/
public static String getDataBase(){
return contextHolder.get();
}
/**
* 清空
*/
public static void chearDataSource(){
contextHolder.remove();
}
}
DataSource:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author haijin
* @description: TODO
* @date 2024/12/25 14:16
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSource {
String value() default "primaryDataSource";
}
DataSourceAspect:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author haijin
* @description: 当有注解时,按照指定的数据源进行操作 没有注解时,按照默认的数据源进行操作
* @date 2024/12/25 14:17
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class DataSourceAspect {
/**
* 指定切入点
*/
@Pointcut("@annotation(com.gmcc.msb.config.DataSource)")
public void dataSourcePointCut(){
}
@After("dataSourcePointCut()")
public void after(){
DatabaseContextHolder.chearDataSource();
}
/**
* 环绕通知,包含了五种通知类型
* @return
*/
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point){
MethodSignature signature = (MethodSignature) point.getSignature();
Method method=signature.getMethod();
//默认数据源
String defaultDataSource= DatabaseContextHolder.PRIMARY_DATA_SOURCE;
//获取该注解
DataSource dataSource=method.getAnnotation(DataSource.class);
//存在注解,直接切换该注解对应的数据源
if(dataSource!=null){
defaultDataSource=dataSource.value();
}
//切换数据源
DatabaseContextHolder.setDataBase(defaultDataSource);
log.info("使用的数据源:【{}】",defaultDataSource);
//继续执行方法
Object result=null;
try {
result=point.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
//最后清除数据源
DatabaseContextHolder.chearDataSource();
}
return result;
}
}
DynamicDataSource:
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author haijin
* @description: 扩展 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法
* @date 2024/12/25 14:14
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.info("------------------当前数据源 {}", DatabaseContextHolder.getDataBase());
return DatabaseContextHolder.getDataBase();
}
}
DynamicDataSourceConfig:
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author haijin
* @description: 配置多数据源
* @date 2024/12/25 14:07
*/
@Configuration
public class DynamicDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ortherDataSource")
@ConfigurationProperties(prefix = "spring.datasource.orther")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(@Qualifier("primaryDataSource") javax.sql.DataSource dataSource1,
@Qualifier("ortherDataSource") DataSource dataSource2){
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DatabaseContextHolder.PRIMARY_DATA_SOURCE,dataSource1);
dataSourceMap.put(DatabaseContextHolder.ORTHER_DATA_SOURCE,dataSource2);
DynamicDataSource dynamicDataSource=new DynamicDataSource();
//该方法是AbstractRoutingDataSource的方法
dynamicDataSource.setTargetDataSources(dataSourceMap);
//根据实际需求,指定默认操作的库
dynamicDataSource.setDefaultTargetDataSource(dataSource1);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
//指定数据源(这个必须有,否则报错)
fb.setDataSource(ds);
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
// 设置 MyBatis 配置属性
fb.getObject().getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return fb.getObject();
}
@Bean
public MapperScannerConfigurer smsMapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
mapperScannerConfigurer.setBasePackage("xyz.haijin.dao");
return mapperScannerConfigurer;
}
@Bean(name = "transactionManager")
@Primary
public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
2、上面的内容配置完以后需要开启aop的注解:
在springboot的启动类上使用:
@Slf4j
@SpringBootApplication
@EnableAspectJAutoProxy
3、如果出现:
BeanPostProcessor before instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/tools/PointcutPrimitive
在pom文件上添加:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version> <!-- 确保使用与Spring AOP兼容的版本 -->
</dependency>
4、在nacos上的配置的数据源:
spring:
datasource:
primary:
jdbc-url: jdbc:mysql://127.0.0.1:3306/haijin?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
hikari:
connection-test-query: select 1
connection-timeout: 30000
minimum-idle: 0
maximum-pool-size: 50
max-lifetime: 180000
orther:
jdbc-url: jdbc:mysql://127.0.0.1:3306/haijin?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
hikari:
connection-test-query: select 1
connection-timeout: 30000
minimum-idle: 0
maximum-pool-size: 50
max-lifetime: 180000
jpa:
database: mysql
show-sql: false
generate-ddl: false
hibernate:
use-new-id-generator-mappings: false
ddl-auto: none
mybatis:
type-aliases-package: xyz.haijin.po
configuration:
map-underscore-to-camel-case: true
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
5、在对应的业务方法上使用注解来配置对应的数据源:
@DataSource(value = "ortherDataSource")
public Result test(HttpServletRequest request, TestVo testVo) {
...
6、参考文章:
手把手教你springboot优雅的实现多数据源,看这一篇就够了
spring将nacos中配置的多个数据源放在哪里 spring配置多个数据源多事务
java spring cloud 使用nacos配置多数据源(druid)AbstractRoutingDataSource
jdbcUrl is required with driverClassName错误解决
2024-12-31 start:
oracle pom依赖:
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
jdbc-url: jdbc:oracle:thin:@127.0.0.1:1521/oracl
driver-class-name: oracle.jdbc.driver.OracleDriver
username: oracl
password: 123456
end