spring配置双数据源

spring配置双数据源

前段时间有个需求,需要将数据存到两个数据库中,一个库中存放主信息,一个库中存放特殊信息,看来是要使用双数据源了,搞起来吧

既然是双数据源,先不管怎么切换,配置得先搞起来

数据源配置

<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>

    <!-- 初始化连接大小 -->
    <property name="initialSize" value="5"/>
    <!-- 连接池最大使用连接数量 -->
    <property name="maxActive" value="10"/>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="1"/>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="3000"/>

    <property name="poolPreparedStatements" value="true"/>
    <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>

    <!-- 检测连接是否有效的sql -->
    <property name="validationQuery" value="SELECT 1"/>
    <!-- 申请连接时执行validationQuery检测连接是否有效 -->
    <property name="testOnBorrow" value="false"/>
    <!-- 归还连接时执行validationQuery检测连接是否有效 -->
    <property name="testOnReturn" value="false"/>
    <!-- 申请连接时检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 -->
    <property name="testWhileIdle" value="true"/>

    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    <property name="minEvictableIdleTimeMillis" value="300000"/>

    <!-- 打开removeAbandoned功能 -->
    <property name="removeAbandoned" value="false"/>
    
</bean>

<!-- 数据源 -->
<bean name="adDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="url" value="${adurl}"/>
    <property name="username" value="${adusername}"/>
    <property name="password" value="${adpassword}"/>

    <!-- 初始化连接大小 -->
    <property name="initialSize" value="5"/>
    <!-- 连接池最大使用连接数量 -->
    <property name="maxActive" value="10"/>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="1"/>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="3000"/>

    <property name="poolPreparedStatements" value="true"/>
    <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>

    <!-- 检测连接是否有效的sql -->
    <property name="validationQuery" value="SELECT 1"/>
    <!-- 申请连接时执行validationQuery检测连接是否有效 -->
    <property name="testOnBorrow" value="false"/>
    <!-- 归还连接时执行validationQuery检测连接是否有效 -->
    <property name="testOnReturn" value="false"/>
    <!-- 申请连接时检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 -->
    <property name="testWhileIdle" value="true"/>

    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    <property name="minEvictableIdleTimeMillis" value="300000"/>

    <!-- 打开removeAbandoned功能 -->
    <property name="removeAbandoned" value="false"/>
    
</bean>

<!-- 动态数据源 -->
    <bean id="dynamicDataSource" class="com.zhanghe.webconfig.datasource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="video" value-ref="dataSource"/>
                <entry key="ad" value-ref="adDataSource"/>
            </map>
        </property>
        <!-- 默认数据源 -->
        <property name="defaultTargetDataSource" ref="dataSource"/>
    </bean>

动态数据源

AbstractRoutingDataSource
AbstractRoutingDataSource

配置好了数据源之后,需要进行定义动态数据源,继承AbstractRoutingDataSource,AbstractRoutingDataSource是基于特定的查找key路由到特定的数据源。它内部维护了一组目标数据源,并且做了路由key与目标数据源之间的映射,提供基于key查找数据源的方法。

public class DynamicDataSource extends AbstractRoutingDataSource {
    private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
       // 获取当前数据源key
        String key = DataSourceHolder.getCurDataSource();
        LOGGER.info("{}线程 获取到的数据源key--->{}",Thread.currentThread().getName(),key);
    // 如果没有的话,则使用默认的key
        if(StringUtils.isBlank(key)){
            key = DataSourceHolder.getDefaultDataSource();
        }
        LOGGER.info("{}线程 数据源选择--->{}",Thread.currentThread().getName(),key);
        return key;
    }
}

// 存储当前数据源的key
public class DataSourceHolder {
    private static final ThreadLocal<String> CUR_DATA_SOURCE = new ThreadLocal<>();

    private static final String DEFAULT_DATA_SOURCE = "video";

    public static String getCurDataSource(){
        return CUR_DATA_SOURCE.get();
    }

    public static String getDefaultDataSource(){
        return DEFAULT_DATA_SOURCE;
    }

    public static void setCurDataSource(String dataSource){
        if(StringUtils.isNotBlank(dataSource)){
            CUR_DATA_SOURCE.set(dataSource);
        }
    }

    public static void clearDataSource(){
        CUR_DATA_SOURCE.remove();
    }
}

配置是都搞定了,那怎么切换呢,可以看到在动态数据源中其实是根据key来进行路由获取数据源的,其实就是怎么改变这个key,而且是动态改变,就用spring aop来进行解决吧

数据源切换

首先定义一个注解@DataSource,来标识当前方法要使用的数据源

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {

    String name() default "video";
}

然后来进行aop的逻辑来根据注解的name属性来存储key

@Order(1)  // 这里要注意一下,由于spring中的@Transactional也是使用的aop来开启事务的,而切换数据源要在开启事务之前,所以我将@Order设置为了1
@Aspect
@Component
public class DataSourceAspect {
    private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    @Pointcut(value="@annotation(com.zhanghe.webconfig.datasource.DataSource)")
    public void pointcut(){

    }

    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint){
        String name = getDataSourceName(joinPoint);
        LOGGER.info("{}线程拦截切换数据源{}",Thread.currentThread().getName(),name);
        DataSourceHolder.setCurDataSource(name);
    }

    @After(value = "pointcut()")
    public void after(){
        DataSourceHolder.clearDataSource();
    }

    /**
     * 获取数据源lookupkey
     * @param joinPoint
     * @return
     */

    public String getDataSourceName(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method != null) {
            DataSource dataSource = method.getAnnotation(DataSource.class);

            return dataSource.name();
        } else {
            return null;
        }
    }
}

这就大功告成了,双数据源的配置就搞定了

使用

在使用的时候在方法上配置对应的数据源即可,注意需要新开事务

@Transactional(propagation = Propagation.REQUIRES_NEW)
@DataSource(name = "ad")
public void deleteExpireData() {

https://zhhll.icu/2021/框架/spring/基础/18.spring配置双数据源/

本文由 mdnice 多平台发布

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值