dynamic-datasource 多数据源切换

DsProcessor 

配合@DS 动态切换数据源处理器,自己实现继承重新matches与 doDetermineDatasource 方法就可以了

@Component // 注入Spring中
@RequiredArgsConstructor
public class MyDsProcessor extends DsProcessor {
	// 必须已#开头才生效
	public final static String KEY = "#DS";
	
    /**
     * 抽象匹配条件 匹配才会走当前执行器否则走下一级执行器
     * @param key DS注解里的内容
     * @return 是否匹配
     */
	@Override
	public boolean matches(String key) {
		return Objects.equals(KEY, key);
	}

    /**
     * 抽象最终决定数据源
     * @param invocation 方法执行信息
     * @param key        DS注解里的内容
     * @return 数据源名称
     */
	@Override
	public String doDetermineDatasource(MethodInvocation invocation, String key) {
        return getDs();
	}
	
	/**
	 * 获取请求头中ds的值
	 * @return
	 */
	private String getDs() {
		javax.servlet.http.HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String header = request.getHeader("ds");
		return header;
	}

}
@DS(value = MyDsProcessor.KEY)
@Documented
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface TenantDS {

}

使用方式

启动后动态添加数据源

@Configuration
@RequiredArgsConstructor
public class DBConfig {

	private final DynamicRoutingDataSource drds;
	/**
	 * Hikari数据源创建器
	 * 配置详情取配置文件 spring.datasource.dynamic.hikari
	 */
	private final HikariDataSourceCreator dataSourceCreator;
	
	@PostConstruct
	public void initialization() {
		System.err.println(drds);
		createDataSource("sal");
		System.err.println("dataSourceProperty = "+dataSourceCreator);
	}

	/**
	 * 添加数据源
	 * @param ds 数据源名称
	 */
	private void createDataSource(String ds) {
		String url = "jdbc:mysql://127.0.0.1:3306/db_temp?autoReconnect=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8";
		String clsName ="com.mysql.cj.jdbc.Driver";
		String name = "root";
		DataSourceProperty config = new DataSourceProperty();
		config.setUrl(url);
		config.setDriverClassName(clsName);
		config.setUsername(name);
		config.setPassword(name);
		DataSource dataSource = dataSourceCreator.createDataSource(config);
		drds.addDataSource(ds, dataSource);
	}
	

}
spring:
  datasource:
    dynamic:
      hikari:
        # 池中维护的最小空闲连接数
        min-idle: 5
        # 池中最大连接数,包括闲置和使用中的连接
        max-pool-size: 20
        # 池中连接最长生命周期
        max-lifetime: 1800000
        # 自动提交从池中返回的连接
        is-auto-commit: true
        # 连接允许在池中闲置的最长时间
        idle-timeout: 30000
        # 等待来自池的连接的最大毫秒数
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/tmp?autoReconnect=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

DynamicDataSourceContextHolder

手动切换数据源

/**
 * 核心基于ThreadLocal的切换数据源工具类
 *
 * @author TaoYu Kanyuxia
 * @since 1.0.0
 */
public final class DynamicDataSourceContextHolder {

    /**
     * 为什么要用链表存储(准确的是栈)
     * <pre>
     * 为了支持嵌套切换,如ABC三个service都是不同的数据源
     * 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
     * 传统的只设置当前线程的方式不能满足此业务需求,必须使用栈,后进先出。
     * </pre>
     */
    private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedThreadLocal<Deque<String>>("dynamic-datasource") {
        @Override
        protected Deque<String> initialValue() {
            return new ArrayDeque<>();
        }
    };

    private DynamicDataSourceContextHolder() {
    }

    /**
     * 获得当前线程数据源
     *
     * @return 数据源名称
     */
    public static String peek() {
        return LOOKUP_KEY_HOLDER.get().peek();
    }

    /**
     * 设置当前线程数据源
     * <p>
     * 如非必要不要手动调用,调用后确保最终清除
     * </p>
     *
     * @param ds 数据源名称
     */
    public static String push(String ds) {
        String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds;
        LOOKUP_KEY_HOLDER.get().push(dataSourceStr);
        return dataSourceStr;
    }

    /**
     * 清空当前线程数据源
     * <p>
     * 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
     * </p>
     */
    public static void poll() {
        Deque<String> deque = LOOKUP_KEY_HOLDER.get();
        deque.poll();
        if (deque.isEmpty()) {
            LOOKUP_KEY_HOLDER.remove();
        }
    }

    /**
     * 强制清空本地线程
     * <p>
     * 防止内存泄漏,如手动调用了push可调用此方法确保清除
     * </p>
     */
    public static void clear() {
        LOOKUP_KEY_HOLDER.remove();
    }
}

工具封装

/**
 * 数据源切换工具
 */
@Slf4j
public class DynamicDSExecute {

	/**
	 * 切换到指定数据源并执行业务逻辑
	 *
	 * @param ds       目标数据源
	 * @param executor 业务逻辑执行器
	 */
	public static <T> T execute(String ds, Supplier<T> executor) {
		DynamicDataSourceContextHolder.push(ds);
		try {
			return executor.get();
		} finally {
			DynamicDataSourceContextHolder.poll();
		}
	}

    /**
     * 切换到指定数据源并执行业务逻辑
     *
     * @param ds       目标数据源
     * @param executor 业务逻辑执行器
     */
    public static <T> T execute(String ds, Supplier<T> executor, Supplier<T> failExecutor) {
        DynamicDataSourceContextHolder.push(ds);
        try {
            return executor.get();
        } catch (Throwable e) {
			log.error("执行业务逻辑发生异常", e);
			return failExecutor.get();
		} finally {
            DynamicDataSourceContextHolder.poll();
        }
    }
}

相关依赖

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
			<version>3.6.1</version>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值