Spring mybatis 多数据源切换

实现原理:继承spring-jdbc 的抽象类org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,重写其lookup数据源的逻辑

先上spring配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.szmcob.pis.runners.cache"/>
    <aop:aspectj-autoproxy/>
    <bean id="ds_szkdx" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://10.249.91.70:3306/szkdx?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="lasttrain"/>
        <property name="password" value="Szmcob@123"/>
    </bean>
    <!--pis-->
    <bean id="ds_pis_data" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://10.249.35.2:3306/pis_data?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="yongjidu"/>
        <property name="password" value="www.szmcob.com"/>
    </bean>
    <!--当日计划运行图同步库-->
    <bean id="ds_szmcob_stg" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:postgresql://10.249.35.2:25308/szmcob_stg"/>
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="username" value="last"/>
        <property name="password" value="last@06360"/>
    </bean>
    <bean id="dataSource" class="com.szmcob.pis.runners.cache.support.DynamicDataSource">
        <property name="dataSourceLookup" ref="dataSourceLookup"/>
        <property name="targetDataSources">
            <map key-type="java.lang.String" value-type="javax.sql.DataSource">
                <entry key="ds_szkdx" value-ref="ds_szkdx"/>
                <entry key="ds_pis_data" value-ref="ds_pis_data"/>
                <entry key="ds_szmcob_stg" value-ref="ds_szmcob_stg"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" value="ds_szkdx"/>
    </bean>
    <bean id="dataSourceLookup" class="com.szmcob.pis.runners.cache.support.DynamicDataSourceLookup"/>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.szmcob.pis.runners.cache.model"/>
    </bean>

    <bean id="mapperScan" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.szmcob.pis.runners.cache.mapper"/>
    </bean>

    <bean id="jedisClusterClient" class="redis.clients.jedis.JedisCluster">
        <constructor-arg name="nodes">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="10.249.35.5"/>
                    <constructor-arg name="port" value="22400"/>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="10.249.35.6"/>
                    <constructor-arg name="port" value="22400"/>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="10.249.35.7"/>
                    <constructor-arg name="port" value="22400"/>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg name="timeout" value="15000"/>
    </bean>
</beans>

以上配置了3个数据源,然后自定义一个数据源继承自AbstractRoutingDataSource类和一个dataSourceLookup

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> activeDataSource = new ThreadLocal<>();

    private DynamicDataSourceLookup lookup;

    private Map<Object, Object> dataSources;

    @Override
    protected Object determineCurrentLookupKey() {
        return activeDataSource.get();
    }


    public static void setActive(String dataSourceId){
        //System.out.println("设置数据源" + dataSourceId);
        activeDataSource.set(dataSourceId);
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        System.out.println("设置setTargetDataSources");
        super.setTargetDataSources(targetDataSources);
        //this.dataSources = targetDataSources;
        lookup.setDataSources(targetDataSources);
    }

    @Override
    public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
        System.out.println("设置setDataSourceLookup");
        lookup = (DynamicDataSourceLookup)dataSourceLookup;
        super.setDataSourceLookup(lookup);
    }

    public static void clean(){
        activeDataSource.remove();
    }
}

重写了determineCurrentLookupKey、setTargetDataSources和setDataSourceLookup方法,重写setTargetDataSources是为了将targetDataSources列表设置到lookup实例,否则lookup的时候是无法根据数据源id来获取到数据源实例的。

public class DynamicDataSourceLookup implements DataSourceLookup {
    private Map<Object, Object> dataSources;

    @Override
    public DataSource getDataSource(String dataSource) throws DataSourceLookupFailureException {
        System.out.println("查找数据源" + dataSource);
        return (DataSource) dataSources.get(dataSource);
    }

    public Map<Object, Object> getDataSources() {
        return dataSources;
    }

    public void setDataSources(Map<Object, Object> dataSources) {
        this.dataSources = dataSources;
    }
}

这里重写的getDataSource方法就需要用到DynamicDataSource实例中set进来的数据源列表了。当日这个列表不一定要维护在looup里边,还有其他实现方式,大同小异吧。

动态数据源设置好后,需要有个方式去触发或者去切换数据源,这里就使用注解的方式来做了。

首先定义注解:

value就是数据源的ID了,和文件开头的spring配置中的数据源ID一致

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DataSource {
    DataSourceEntry value() default DataSourceEntry.SZKDX;
}

接下来我们希望将切换数据源的注解加到mybatis mapper接口上来控制数据源切换:

@Mapper
@DataSource(DataSourceEntry.SZKDX)
public interface StationIInfoMapper {
    @Select("SELECT * FROM stationinfo WHERE lineNo=#{lineNo} ORDER BY stationorder+0 ASC")
    List<StationInfo> selectByLineNo(String lineNo);
}
@Mapper
@DataSource(DataSourceEntry.PIS_DATA)
public interface PlatformsMapper {
    @Select("SELECT * FROM platforms WHERE lineid=#{lineId}")
    List<PlatformInfo> selectByLineId(int lineId);
}

要能够完成切换,最后还需要用到spring aop。因为spring配置中我们启动了注解配置,所以这里就使用注解来注册一个aop切面来干这个事了。

@Aspect
@EnableAspectJAutoProxy
@Component
public class DynamicDataSourceAspect {

    private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

    @Pointcut("@annotation(com.szmcob.pis.runners.cache.support.DataSource)")
    public void pointCut(){
    }

    @Pointcut("execution(* com.szmcob.pis.runners.cache.mapper.*.*(..))")
    public void pointCut2(){
    }

    @Around(value = "pointCut2()")
    public Object setDataSource(ProceedingJoinPoint joinPoint){
        DataSource annotation = (DataSource)joinPoint.getSignature().getDeclaringType().getAnnotation(DataSource.class);
        DataSourceEntry entry = annotation.value();
        DynamicDataSource.setActive(entry.getDataSourceId());
        logger.info("数据源已切换至" + entry.getDataSourceId());
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

上面定义的基本意思就是当满足pointCut()条件的时候/类,执行@Around配置的方法。这里的逻辑就只是对DynamicDataSource设置当前的数据源。

pointCut定义切面切入的点,execution()表达式指定什么包,什么类, @annotation()指定什么注解的类

 

测试:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-mysql2redis.xml");
PlatformsMapper platformsMapper = ctx.getBean(PlatformsMapper.class);
//方法调用...
StationIInfoMapper stationInfoMapper = ctx.getBean(StationIInfoMapper.class);
//方法调用...

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用Mybatis配置多数据源时,有两种常见的方式。一种是通过手动配置多个数据源,另一种是使用Mybatis-Plus-Dynamic来实现。 对于手动配置多个数据源的方式,你可以在Spring Boot项目的配置文件中指定每个数据源的配置信息,包括URL、用户名、密码等。然后,在Mybatis的配置文件中,可以使用<dataSource>标签为每个数据源配置相应的属性。这样,在使用Mybatis时,可以根据需要选择不同的数据源进行数据操作。引用 [2] 另一种方式是使用Mybatis-Plus-Dynamic。Mybatis-Plus-Dynamic是Mybatis-Plus提供的一个扩展,它可以简化多数据源的配置过程。使用Mybatis-Plus-Dynamic,你只需要在配置文件中指定每个数据源的配置信息,并在代码中通过DynamicDataSourceContextHolder来切换数据源。这样,就可以方便地在不同的数据源之间进行切换。引用<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Mybatis配置多数据源](https://blog.csdn.net/weixin_44385360/article/details/128121969)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [mybatis多数据源](https://blog.csdn.net/qq_38353700/article/details/118583828)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dyyaries

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

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

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

打赏作者

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

抵扣说明:

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

余额充值