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 {

}

使用方式

dynamic-datasource 多数据源切换_第1张图片

启动后动态添加数据源

@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 {

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

* 如非必要不要手动调用,调用后确保最终清除 *

* * @param ds 数据源名称 */ public static String push(String ds) { String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds; LOOKUP_KEY_HOLDER.get().push(dataSourceStr); return dataSourceStr; } /** * 清空当前线程数据源 *

* 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称 *

*/ public static void poll() { Deque deque = LOOKUP_KEY_HOLDER.get(); deque.poll(); if (deque.isEmpty()) { LOOKUP_KEY_HOLDER.remove(); } } /** * 强制清空本地线程 *

* 防止内存泄漏,如手动调用了push可调用此方法确保清除 *

*/ public static void clear() { LOOKUP_KEY_HOLDER.remove(); } }

工具封装

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

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

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

相关依赖

		
			com.baomidou
			mybatis-plus-boot-starter
			3.5.3.1
		
		
			com.baomidou
			dynamic-datasource-spring-boot-starter
			3.6.1
		
		
			com.mysql
			mysql-connector-j
			runtime
		

你可能感兴趣的:(java,servlet,开发语言)