天天看點

spring+atomikos+mybatis 多資料源事務(動态切換)

注:自動切換,是為不同的資料源,卻要對應相同的dao層;

1.與無事務版的一樣,建立DynamicDataSource類,繼承AbstractRoutingDataSource

package com.test.main.dataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override

    protected Object determineCurrentLookupKey() {

        return DynamicDataSourceHolder.getDataSource();

    }

}

建立輔助類DynamicDataSourceHolder,主要用于儲存目前線程所需的datasource的key值

public class DynamicDataSourceHolder {

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

    public static String getDataSource() {

        return THREAD_DATA_SOURCE.get();

    public static void setDataSource(String dataSource) {

        THREAD_DATA_SOURCE.set(dataSource);

    public static void clearDataSource() {

        THREAD_DATA_SOURCE.remove();

建立dao層切面,注解選擇資料源DataSourceAspect類:

import java.lang.annotation.Annotation;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.stereotype.Component;

import com.test.main.annotations.DataSource;

@Component

@Aspect

public class DataSourceAspect {

    @Before("execution(* com.test.model.dao.*.*.*(..))")

    public void intercept(JoinPoint point) throws Exception {

        Class<?> target = point.getTarget().getClass();

        MethodSignature signature = (MethodSignature) point.getSignature();

        for (Class<?> clazz : target.getInterfaces()) {

            resolveDataSource(clazz, signature.getMethod());

        }

        resolveDataSource(target, signature.getMethod());

    private void resolveDataSource(Class<?> clazz, Method method) {

        String sourceName = null;

        try {

            Class<?>[] types = method.getParameterTypes();

            if (clazz.isAnnotationPresent((Class<? extends Annotation>) DataSource.class)) {

                DataSource source = clazz.getAnnotation(DataSource.class);

                sourceName = source.value();

                DynamicDataSourceHolder.setDataSource(sourceName);

            }

            Method m = clazz.getMethod(method.getName(), types);

            if (m != null && m.isAnnotationPresent(DataSource.class)) {

                DataSource source = m.getAnnotation(DataSource.class);

        } catch (Exception e) {

            System.out.println(clazz + ":" + e.getMessage());

2.spring-db.xml 配置:

<?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:util="http://www.springframework.org/schema/util"

    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd

        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd

        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <util:properties id="jdbc"

        location="classpath:etc/mybatis/db.properties" />

    <!-- 連接配接池配置開始 -->

    <bean id="chicken" class="com.atomikos.jdbc.AtomikosDataSourceBean"

        init-method="init" destroy-method="close">

        <property name="xaDataSourceClassName"

            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />

        <property name="uniqueResourceName" value="mysql/chicken" />

        <property name="xaProperties">

            <props>

                <prop key="user">#{jdbc.username}</prop>

                <prop key="password">#{jdbc.password}</prop>

                <prop key="url">#{jdbc.chicken}</prop>

            </props>

        </property>

        <property name="minPoolSize" value="5" />

        <property name="maxPoolSize" value="20" />

        <property name="maxIdleTime" value="60" />

        <property name="reapTimeout" value="20000" />

    </bean>

    <bean id="test_chicken" class="com.atomikos.jdbc.AtomikosDataSourceBean"

        <property name="uniqueResourceName" value="mysql/test_chicken" />

                <prop key="url">#{jdbc.c_test}</prop>

    <bean id="fariyking" class="com.atomikos.jdbc.AtomikosDataSourceBean"

        <property name="uniqueResourceName" value="mysql/fariyking" />

                <prop key="url">#{jdbc.fariyking}</prop>

    <bean id="test_fariyking" class="com.atomikos.jdbc.AtomikosDataSourceBean"

        <property name="uniqueResourceName" value="mysql/test_fariyking" />

                <prop key="url">#{jdbc.f_test}</prop>

    <!-- 連接配接池配置結束 -->

    <!-- MyBatis整合開始 -->

    <bean id="c_dynamicDataSource" class="com.test.main.dataSource.DynamicDataSource">

        <property name="targetDataSources">

            <map key-type="java.lang.String">

                <entry key="chicken" value-ref="chicken"></entry>

                <entry key="test_chicken" value-ref="test_chicken"></entry>

            </map>

    <bean id="cFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

        <property name="dataSource" ref="c_dynamicDataSource" />

        <property name="configLocation" value="classpath:etc/mybatis/mybatis-config.xml" />

        <property name="mapperLocations" value="classpath:com/test/**/*.xml" />

    </bean>    

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

        <property name="basePackage" value="com.test.model.dao.c" />

        <property name="annotationClass" value="com.test.main.annotations.MyBatisRepository" />

        <property name="sqlSessionFactory" ref="cFactory"></property>

        <property name="sqlSessionFactoryBeanName" value="cFactory"></property>

    <bean id="f_dynamicDataSource" class="com.test.main.dataSource.DynamicDataSource">

                <entry key="fariyking" value-ref="fariyking"></entry>

                <entry key="test_fariyking" value-ref="test_fariyking"></entry>

    <bean id="fFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

        <property name="dataSource" ref="f_dynamicDataSource" />

        <property name="basePackage" value="com.test.model.dao.f" />

        <property name="sqlSessionFactory" ref="fFactory"></property>

        <property name="sqlSessionFactoryBeanName" value="fFactory"></property>

    <!-- MyBatis整合結束 -->

    <!-- 配置資料庫事務開始 -->

    <!-- atomikos事務管理器 -->

    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"

        <description>UserTransactionManager</description>

        <property name="forceShutdown">

            <value>true</value>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">

        <property name="transactionTimeout" value="3000" />

    <bean id="springTransactionManager"

        class="org.springframework.transaction.jta.JtaTransactionManager">

        <property name="transactionManager">

            <ref bean="atomikosTransactionManager" />

        <property name="userTransaction">

            <ref bean="atomikosUserTransaction" />

        <property name="allowCustomIsolationLevels" value="true" />

    <tx:annotation-driven transaction-manager="springTransactionManager" />

    <!-- 配置資料庫事務結束 -->

</beans>

2.1在同一個事務中,不同的資料源需要不同 SqlSessionFactoryBean,注意配置時,需要配置 MapperScannerConfigurer的:

<property name="sqlSessionFactoryBeanName" value="cFactory"></property>

value值為:SqlSessionFactoryBean 的 bean id;

2.2同一事務中的SqlSessionFactoryBean,對應MapperScannerConfigurer 中的 basePackage 不能範圍重合,不然在同一事務時,spring不會切換資料源,而是取先前與之重合的SqlSessionFactoryBean的資料源

本文轉自yunlielai51CTO部落格,原文連結:http://blog.51cto.com/4925054/2046456,如需轉載請自行聯系原作者