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

不知為何就叫呵呵發表於2017-06-14

注:自動切換,是為不同的資料來源,卻要對應相同的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值

package com.test.main.dataSource;

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類:

package com.test.main.dataSource;

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);
                sourceName = source.value();
                DynamicDataSourceHolder.setDataSource(sourceName);
            }
        } 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="base" class="com.alibaba.druid.pool.xa.DruidXADataSource"
        destroy-method="close" lazy-init="true">
        <property name="initialSize" value="5" />
        <property name="minIdle" value="5" />
        <property name="maxActive" value="20" />

        <property name="validationQuery" value="SELECT 1" />
        <property name="timeBetweenEvictionRunsMillis" value="2800000" />
        <property name="minEvictableIdleTimeMillis" value="600000" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="filters" value="stat" />

        <property name="logAbandoned" value="true" />     
    </bean>
    
    <!-- Druid連線池 -->
    <bean id="jpcar" parent="base">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.jpcar_url}" />
        <property name="username" value="#{jdbc.username}" />
        <property name="password" value="#{jdbc.password}" />
    </bean>
    <bean id="jpcarAtom" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="mysql/jpcar" />
        <property name="xaDataSource" ref="jpcar" />
        <property name="maintenanceInterval" value="28000" />
        <property name="testQuery" value="SELECT 1" />
    </bean>
    <bean id="jpcarData" parent="base">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.jpcarData_url}" />
        <property name="username" value="#{jdbc.username}" />
        <property name="password" value="#{jdbc.password}" />
    </bean>
    <bean id="jpcarDataAtom" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="mysql/jpcarData" />
        <property name="xaDataSource" ref="jpcarData" />
        <property name="maintenanceInterval" value="28000" />
        <property name="testQuery" value="SELECT 1" />
    </bean>

    <bean id="test_jpcar" parent="base">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.test_jpcar_url}" />
        <property name="username" value="#{jdbc.test_username}" />
        <property name="password" value="#{jdbc.test_password}" />
        
    </bean>
    <bean id="test_jpcarAtom" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="mysql/test_jpcar" />
        <property name="xaDataSource" ref="test_jpcar" />
        <property name="maintenanceInterval" value="28000" />
        <property name="testQuery" value="SELECT 1" />
    </bean>
    <bean id="test_jpcarData" parent="base">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.test_jpcarData_url}" />
        <property name="username" value="#{jdbc.test_username}" />
        <property name="password" value="#{jdbc.test_password}" />
        
    </bean>
    <bean id="test_jpcarDataAtom" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="mysql/test_jpcarData" />
        <property name="xaDataSource" ref="test_jpcarData" />
        <property name="maintenanceInterval" value="28000" />
        <property name="testQuery" value="SELECT 1" />
    </bean>
    <!-- 連線池配置結束 -->

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

    <bean id="jpcarDataDynamicDataSource" class="com.jpcar.main.dataSource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="jpcar" value-ref="jpcarAtom"></entry>
                <entry key="jpcarData" value-ref="jpcarDataAtom"></entry>
                <entry key="test_jpcarData" value-ref="test_jpcarDataAtom"></entry>
                <entry key="test_jpcar" value-ref="test_jpcarData"></entry>
            </map>
        </property>
    </bean>
    <bean id="jpcarDataFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="jpcarDataDynamicDataSource" />
        <property name="configLocation" value="classpath:etc/mybatis/mybatis-config.xml" />
        <property name="mapperLocations" value="classpath:com/**/*.xml" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.jpcar" />
        <property name="annotationClass" value="com.jpcar.main.annotations.MyBatisRepository" />
        <property name="sqlSessionFactory" ref="jpcarDataFactory"></property>
        <property name="sqlSessionFactoryBeanName" value="jpcarDataFactory"></property>
    </bean>

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


    <!-- 配置資料庫事務開始 -->
    <!-- atomikos事務管理器 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <description>UserTransactionManager</description>
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="90000" />
    </bean>
    <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager" />
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction" />
        </property>
        <property name="allowCustomIsolationLevels" value="true" />
    </bean>

    <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的資料來源

相關文章