spring整合mybatis动态切换数据源+事务管理

公司因为业务需要,需要把同一份服务的数据根据不同标识分别存放到两个数据库中(数据结构一样),刚开始为了尽快满足需求,使用了个简版(代码切换数据源),闲下来以后实在觉得太low,查看了一些demo以后,整理出来了适合自己项目的配置。话不多说,上步骤。

1、配置数据源

<!--引入外部资源文件-->
    <context:property-placeholder location="/WEB-INF/properties/db.properties" ignore-unresolvable="true"/>
<!--配置数据源 1-->
    <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="maxActive" value="10"/>
        <property name="minIdle" value="5"/>
    </bean>
    <!--配置数据源 2-->
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="${hongmeng.url}"/>
        <property name="username" value="${hongmeng.username}"/>
        <property name="password" value="${hongmeng.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="maxActive" value="10"/>
        <property name="minIdle" value="5"/>
    </bean>
    <!--配置多数据源映射关系-->
    <bean id="dataSource" class="com.hlsoft.heguivedio.datasources.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="dataSource1" value-ref="dataSource1"></entry>
                <entry key="dataSource2" value-ref="dataSource2"></entry>
            </map>
        </property>
        <!--设置主数据库为默认数据源-->
        <property name="defaultTargetDataSource" ref="dataSource1"/>
    </bean>

    <!--配置工厂-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.hlsoft.heguivedio.pojo"/>
        <property name="configLocation" value="/WEB-INF/sqlMapConfig.xml"/>
    </bean>

    <!--配置映射器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hlsoft.heguivedio.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

上面的配置都是spring整合mybatis的基本配置,唯一不同的是数据源工厂加载数据源时没有直接使用我们常用的数据库连接池,而是使用了DynamicDataSource,这个类在很多demo中都有写,这正是能帮助我们动态切换数据源的关键一步,下面是这个类的代码

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }
}

从这个类中可以看到,只有一个重写的determineCurrentLookupKey方法,这里不深究它如何帮助我们切换数据源,其实是我他喵的自己没搞懂,不过这不妨碍我们实现功能,接下来继续贴代码

public class DataSourceHolder {
    //线程本地环境
    private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
    //设置数据源
    public static void setDataSource(String customerType) {
        dataSources.set(customerType);
    }
    //获取数据源
    public static String getDataSource() {
        return (String) dataSources.get();
    }
    //清除数据源
    public static void clearDataSource() {
        dataSources.remove();
    }

这个类中能看到我们熟悉的ThreadLocal类,上一个类中正是调用了getDataSource方法获取线程中设置好的数据源来指定我们接下来要使用的数据源,关于数据源切换工作已准备就绪,接下来配置如何指定线程中的数据源

2、设置数据源

其实设置数据源的方式很简单,只要调用DataSourceHolder中的setDataSource方法就能成功,如:

DataSourceHolder.setDataSource("dataSource1");
//这里设置的“dataSource1”是在数据源配置文件中已经配好的第一个数据源,同理,设置第二个数据源时传入dataSource2就可以

不过,采用这种方法,未免有些emm…,所以,这个时候我们会想到注解,那玩意多好用,逼格也显然比在代码中重复加上面这一句高了不少,不多逼逼,搞定注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String name() default DataSource.master;

    String master = "dataSource1";

    String slave = "dataSource2";
}

定义一个注解,用来切换数据源,注解中我已经设置好了配置文件中配好的两个数据源字符串,在这里根据自己需求设置就行,当然,也可以不写,使用注解时直接往里填字符串就行,纯看个人喜好。到了这里,只剩下一件事,怎么获取注解中的数据源并配置到线程(也就是我们定义好的DataSourceHolder 的ThreadLocal中),这个过程呢,绝逼又是一段重复代码,这个时候,要用到spring的AOP了,让它在合适的时机,正确的节点帮咱把这事干了

AOP配置

aop帮我们往代码里加一段重复代码,帮咱实现一些必要的功能,不过首先,咱得先写好这段代码,别急,下面这段就是

public class DataSourceAspect {

    public void before(JoinPoint point) {
        Object target = point.getTarget();
        String method = point.getSignature().getName();
        Class<?>[] classz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            Method m = classz[0].getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource data = m.getAnnotation(DataSource.class);
                DataSourceHolder.setDataSource(data.name());
                System.out.println(DataSourceHolder.getDataSource());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //清除数据源,使用默认数据源
    public void after() {
        DataSourceHolder.clearDataSource();
    }
}

这里定义了两端两个方法,before是aop切入方法之前要执行的,after是方法结束之后要执行的,before的功能就是上面我叨叨了半天可以帮我们获取注解数据源并配置到线程的,after呢,不用说了吧,懂的都懂(切记,一定要写,否则会因为线程缓存出现一些奇奇怪怪的问题,本人已经深受其害)

代码准备就绪,接下来开始配置

<!-- 开启aop,对类代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
    <!-- 配置数据库注解aop -->
    <bean id="dataSourceAspect" class="com.hlsoft.heguivedio.datasources.DataSourceAspect" />
    <aop:config>
        <aop:aspect id="c" ref="dataSourceAspect">
            <aop:pointcut id="tx" expression="execution(* com.hlsoft.heguivedio.service..*.*(..))"/>
            <aop:before pointcut-ref="tx" method="before"/>
            <aop:after pointcut-ref="tx" method="after"/>
        </aop:aspect>
    </aop:config>

这里配置的service包下放置的就是我需要切入的方法,数据源切换到这里就完成了,接下来展示一段调用的方法

public interface cwx_channelService {
	@DataSource(name = DataSource.slave)
    void channelsTest();
}

public class cwx_channelServiceImpl implements cwx_channelService {
	@Override
    @Transactional("jzHm")
    public void channelsTest() {
        List<Reviewer> reviewers = audioFrequencyMapper.channelsTest();
        for (Reviewer reviewer : reviewers) {
            System.out.println(reviewer);
        }
    }
}

代码中能看到DataSource注解加到了接口方法上,这是为了事务考虑,设置事务前必须先设置好数据源,否则事务使用默认数据源后,再切换数据源就不再生效

3、事务控制

数据源切换已经搞定,接下来搞定事务,下面是常规的spring事务管理配置

<!--配置事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

有了这段配置之后,使用transactionManager注解控制事务即可,但在多数据源项目中,需要配置对应数据源的事务管理器,否则spring只识别默认数据源事务管理器,然后导致数据源切换也不能成功,更别提事务管理了,增加数据源事务管理器配置如下

 <!--主数据库事务管理器类 -->
    <bean id="txManagerHl" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource1" />
        <qualifier value="jzHl" />
    </bean>
    <!--从库事务管理器类 -->
    <bean id="txManagerHm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource2" />
        <qualifier value="jzHm" />
    </bean>

上面增加了我项目中的两个数据源的事务管理器(分别对应不同的数据源),qualifier配置的内容主要用来识别要使用哪个事务管理器(可随意配置),事务管理时填入配置的两个字符串即可切换事务管理,配置完成后同时将spring事务管理配置进行修改,看图

<!--配置事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
        <qualifier value="mybatis"/>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

这里和默认配置唯一不同的是加上了qualifier ,mybatis是固定配置,不能修改,完成这些配置之后,多数据源的事务管理就能成功使用了,使用方法看上方第二部分完成后的调用方法,使用哪个数据源的事务就写上事务管理器中qualifier 的值

数据源切换及事务控制配置大致是以上的步骤,有说明不清楚或者有误的地方欢迎大家指正。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot 是一个用于快速构建 Java 应用程序的框架。它可以与多种其他框架和组件进行整合,以实现更丰富的功能。在这里,我们将讨论如何使用 Spring Boot 整合 Druid、MyBatis、JTA 分布式事务以及多数据源,同时使用 AOP 注解实现动态切换。 首先,我们可以在 Spring Boot 集成 Druid 数据源。Druid 是一个高性能的 JDBC 连接池,可以提供监控和统计功能。我们可以通过在 pom.xml 文件添加相关的依赖,并在 application.properties 文件配置数据源信息,来实现 Druid 的集成。 接下来,我们可以整合 MyBatis 框架,它是一种优秀的持久化解决方案。我们可以使用 MyBatis 来操作数据库,并将其与 Druid 数据源进行整合。为此,我们需要在 pom.xml 文件添加 MyBatisMyBatis-Spring 的依赖,并配置 MyBatis 的相关配置文件。 此外,我们还可以使用 JTA(Java Transaction API)实现分布式事务。JTA 可以在分布式环境协调多个参与者的事务操作。我们可以在 pom.xml 文件添加 JTA 的依赖,并在 Spring Boot 的配置文件配置 JTA 的相关属性,以实现分布式事务的支持。 在实现多数据源时,我们可以使用 Spring Boot 的 AbstractRoutingDataSource 来实现动态切换数据源。这个类可以根据当前线程或其他条件选择不同的数据源来进行数据操作。我们可以通过继承 AbstractRoutingDataSource 并实现 determineCurrentLookupKey() 方法来指定当前数据源的 key。然后,在配置文件配置多个数据源,并将数据源注入到 AbstractRoutingDataSource ,从而实现动态切换。 最后,我们可以使用 AOP(Aspect Oriented Programming)注解来实现动态切换。AOP 是一种编程范式,可以通过在代码插入特定的切面(Aspect)来实现横切关注点的处理。我们可以在代码使用注解来标记需要切换数据源的方法,然后使用 AOP 技术来拦截这些方法,并根据注解指定的数据源信息来进行数据源切换。 综上所述,通过整合 Druid、MyBatis、JTA 分布式事务以及多数据源,并使用 AOP 注解实现动态切换,我们可以在 Spring Boot 实现强大而灵活的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值