SpringAop+Mybatis 实现动态切换数据库操作

      在平常的项目开发中,会遇到一个应用中访问多个数据源的需求,本文将通过使用SpringAop+Mybatis与spring-jdbc的

AbstractRoutingDataSource实现动态切换数据源;

1.定义多个数据源的枚举:

public enum DataSourceType {
        //第一个数据源
        DATASOURCE_ONE,
        //第二个数据源
        USERCENTER_TWO     
}

2.定义动态扫描的注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DynamicDataSourceAnnotation {
    //dataSource 自定义注解的参数
    DataSourceType dataSource() default DataSourceType.DATASOURCE_ONE;
}

3.定义全局数据源的管理类,默认访问DATASOURCE_ONE

public class DataSourceTypeManager {
    private static final ThreadLocal<DataSourceType> dataSourceTypes  = new ThreadLocal<DataSourceType>() {
        protected DataSourceType initialValue() {
            return DataSourceType.DATASOURCE_ONE;
        }
    };
    public static DataSourceType get(){
        return dataSourceTypes.get();
    }

    public static void set(DataSourceType dataSourceType){
        dataSourceTypes.set(dataSourceType);
    }
    public static void reset(){
        dataSourceTypes.set(DataSourceType.DATASOURCE_ONE);
    }
}

4.实现AbstractRoutingDataSource动态路由数据源:

  

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger logger = LoggerFactory.getLogger(AbstractRoutingDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("dataSourceType = "+ DataSourceTypeManager.get());
        return DataSourceTypeManager.get();
    }
}

5.定义AOP的Aspect动态切换数据源:

@Aspect
@Component
@Order(0)
public class DynamicDataSourceAspect {

    @Pointcut("execution( * com.test.datasource.manage.dao.mapper.*.*(..))")
    public void invanyMethod() {
    };

    //前置通知,设置成目标数据源
    @Before("invanyMethod()")
    public void beforeinv(JoinPoint jp) throws Exception {
        //获得注解
        DynamicDataSourceAnnotation meta = giveAnnotation(jp);

        DataSourceTypeManager.set(meta.dataSource());
    }

    //执行结束后设置成住数据源
    /**
     * 获得方法上的注解
     * @param joinPoint 切入点
     * @return
     * @throws Exception
     */
    private DynamicDataSourceAnnotation giveAnnotation(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(DynamicDataSourceAnnotation.class);
        }
        return null;
    }

    @After("invanyMethod()")
    public void after(JoinPoint jp) throws Exception {
       DataSourceTypeManager.set(DataSourceType.DATASOURCE_ONE);
    }

6.mybatis配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- 配置第一个数据源 -->
    <bean name="dataSource_one" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value=""/>
        <property name="url" value="$"/>
        <property name="username" value=""/>
        <property name="password" value=""/>
        <!-- 初始化连接大小 -->
        <property name="initialSize" value=""/>
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value=""/>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value=""/>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value=""/>
    </bean>

    <!-- 配置第二个数据源 -->
    <bean name="dataSource_two" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value=""/>
        <property name="url" value=""/>
        <property name="username" value=""/>
        <property name="password" value=""/>
        <!-- 初始化连接大小 -->
        <property name="initialSize" value=""/>
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value=""/>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value=""/>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value=""/>
    </bean>
    <!-- 多数据源配置 -->
    <bean id="dataSource" class="com.test.datasource.manage.mybatis.DynamicDataSource">
        <!-- 默认使用dataSourceA的数据源 -->
        <property name="defaultTargetDataSource" ref="dataSource_one"></property>
        <property name="targetDataSources">
            <map key-type="com.test.datasource.manage.mybatis.DataSourceType">
                <entry key="DATASOURCE_ONE" value-ref="dataSource_one"></entry>
                <entry key="DATASOURCE_TWO" value-ref="dataSource_two"></entry>
            </map>
        </property>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath*:mappers/*.xml"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.test.datasource.manage.dao.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="annotationClass"  value="com.test.datasource.manage.mybatis.MyBatisRepository"/>    //自己定义的mapper扫描注解 
    </bean>
    <!-- AOP自动代理功能 -->
    <aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyBatisRepository {

}

7.在mybatis的Mapper中的方法中添加注解,就可以进行数据源的动态切换:

@MyBatisRepository
public interface TestMapper {
	@DynamicDataSourceAnnotation(dataSource = DataSourceType.DATASOURCE_ONE)
	List<Entity> getEntityList(List<Integer> hids);

	@DynamicDataSourceAnnotation(dataSource = DataSourceType.DATASOURCE_TWO)
	Integer addEntityBatch(List<Entity> entityList);

}	  

注:动态切换数据源的注解比较灵活,可以在业务service的方法级别注解,此时需要修改aop的aspect,以便能够处理扫描到的DynamicDataSourceAnnotation,若在一个业务方法中使用到多个数据源,则需要在第一调用之后,重新获取spring的bean已获得代理对象进行数据源切换,如下:

@Component
public class TestLogic {

	@Autowired
    private TestMapper testMapper;
	
    @DynamicDataSourceAnnotation(dataSource = DataSourceType.DATASOURCE_ONE)
    public void testMethod1(List<Integer> hids,List<Entity> entityList) {
        List<Entity>  list = testMapper.getEntityList(hids);
        getThis().addEntityBatch(entityList);
    }

    @DynamicDataSourceAnnotation(dataSource = DataSourceType.DATASOURCE_TWO)
    public Ingteger addEntityBatch(List<Entity> entityList){
       return testMapper.addEntityBatch(entityList);
    }

    private TsetLogic getThis() {
        return (TestLogic) SpringContextUtil.getBean(this.getClass());
    }  
}

这种方法实现比较繁琐,个人建议直接在Mapper中的方法添加注解。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值