spring+myBatisi实现多数据源动态切换

来源:互联网 发布:湖南教师网络研修平台 编辑:程序博客网 时间:2024/06/16 04:22

        目前网上实现动态数据源的文章很多,基本都是根据spring的AOP实现的,本来以为简单,复制粘贴即可,结果遇到不少问题,一是遇到AOP与事务之间执行顺序的问题,二是service实现类遇到注解无法获取的问题,前一个问题使用order解决了,后一个问题则是放到service接口类中就可以获取。具体如下:


1、自定义注解:

123456789101112131415
import java.lang.annotation.ElementType;import java.lang.annotation.Target;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;/** * RUNTIME * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。 * @author yangGuang * */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface DataSource {String value();}

2、基于spring的aop实现多数据源(读和写两个数据源):
1)写一个ChooseDataSource: 类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey方法

12345678910
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class ChooseDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return HandleDataSource.getDataSource();}}

2)利用ThreadLocal解决线程安全问题:
12345678910
public class HandleDataSource {public static final ThreadLocal<String> holder = new ThreadLocal<String>();public static void putDataSource(String datasource) {holder.set(datasource);}public static String getDataSource() {return holder.get();}    }

3)定义一个数据源切面类,通过aop访问,获取方法上的自定义注解,然后根据注解内容尽情判断,动态设置数据源:

123456789101112131415161718192021222324252627282930313233343536
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.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;//@Aspect//@Componentpublic class DataSourceAspect {//@Pointcut("execution(* com.apc.cms.service.*.*(..))")  public void pointCut(){};    //  @Before(value = "pointCut()") public void before(JoinPoint point){Object target = point.getTarget();System.out.println(target.toString());String method = point.getSignature().getName();System.out.println(method);Class<?>[] classz = target.getClass().getInterfaces();Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();try {Method m = classz[0].getMethod(method, parameterTypes);System.out.println(m.getName());if (m != null && m.isAnnotationPresent(DataSource.class)) {DataSource data = m.getAnnotation(DataSource.class);HandleDataSource.putDataSource(data.value());}} catch (Exception e) {e.printStackTrace();}}}

4)配置applicationContext.xml:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
<!-- 主库数据源 --> <bean id="writeDataSource" class="com.jolbox.bonecp.BoneCPDataSource"  destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://172.22.14.6:3306/cpp?autoReconnect=true"/><property name="username" value="root"/><property name="password" value="root"/><property name="partitionCount" value="4"/><property name="releaseHelperThreads" value="3"/><property name="acquireIncrement" value="2"/><property name="maxConnectionsPerPartition" value="40"/><property name="minConnectionsPerPartition" value="20"/><property name="idleMaxAgeInSeconds" value="60"/><property name="idleConnectionTestPeriodInSeconds" value="60"/><property name="poolAvailabilityThreshold" value="5"/></bean><!-- 从库数据源 --><bean id="readDataSource" class="com.jolbox.bonecp.BoneCPDataSource"  destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://172.22.14.7:3306/cpp?autoReconnect=true"/><property name="username" value="root"/><property name="password" value="root"/><property name="partitionCount" value="4"/><property name="releaseHelperThreads" value="3"/><property name="acquireIncrement" value="2"/><property name="maxConnectionsPerPartition" value="40"/><property name="minConnectionsPerPartition" value="20"/><property name="idleMaxAgeInSeconds" value="60"/><property name="idleConnectionTestPeriodInSeconds" value="60"/><property name="poolAvailabilityThreshold" value="5"/></bean><!-- transaction manager, 事务管理 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 注解自动载入 --><context:annotation-config /><!--enale component scanning (beware that this does not enable mapper scanning!)--><context:component-scan base-package="com.apc.cms.persistence.rdbms" /><context:component-scan base-package="com.apc.cms.service"> <context:include-filter type="annotation"  expression="org.springframework.stereotype.Component" />  </context:component-scan> <context:component-scan base-package="com.apc.cms.auth" /><!-- enable transaction demarcation with annotations --><tx:annotation-driven /><!-- define the SqlSessionFactory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="typeAliasesPackage" value="com.apc.cms.model.domain" /></bean><!-- scan for mappers and let them be autowired --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.apc.cms.persistence" /><property name="sqlSessionFactory" ref="sqlSessionFactory" /></bean><bean id="dataSource" class="com.apc.cms.utils.ChooseDataSource"><property name="targetDataSources">    <map key-type="java.lang.String">    <!-- write --> <entry key="write" value-ref="writeDataSource"/>   <!-- read --> <entry key="read" value-ref="readDataSource"/>    </map>    </property>  <property name="defaultTargetDataSource" ref="writeDataSource"/>  </bean>  <!-- 激活自动代理功能 --><aop:aspectj-autoproxy proxy-target-class="true"/><!-- 配置数据库注解aop --><bean id="dataSourceAspect" class="com.apc.cms.utils.DataSourceAspect" /><aop:config><aop:aspect id="c" ref="dataSourceAspect"><aop:pointcut id="tx" expression="execution(* com.apc.cms.service..*.*(..))"/><aop:before pointcut-ref="tx" method="before"/></aop:aspect></aop:config><!-- 配置数据库注解aop -->

3、测试,如果是在service中实现,需要放到接口类中,放到实现类中无法获取注解。

详情可见:http://www.iteye.com/problems/58681

public interface TestService {
@DataSource("write")  
Document update(User user) throws Exception;
        @DataSource("read")          Document getDocById(long id) throws Exception;
}

原创粉丝点击