Springboot多数据源的问题

我爱海鲸 2024-12-31 17:57:38 暂无标签

简介aop、nacos、切面

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

你好:我的2025