spring多数据源配置

多数据源配置,都差不多都是大同小异。不外乎多配置几个datasource,在架构执行service层代码时,选择自己所需要业务操作的数据源而已。根据业务量的多少配置手动选择数据源还是通过AOP自动选择数据源而已!

我这里介绍通过aop自动选择数据源配置(手动可以下来初步了解下,基本上没啥技术含量)

介绍我的数据源代码:

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

    <!-- 配置数据源 -->
    <!--统一的dataSource-->
<bean id="dynamicDataSource" class="com.dome.utils.datasource.DynamicDataSource" >
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <!--通过不同的key决定用哪个dataSource-->
            <entry value-ref="mysql" key="mysql"></entry>
            <entry value-ref="sqlserver" key="sqlserver"></entry>
        </map>
    </property>
    <!--设置默认的dataSource-->
    <property name="defaultTargetDataSource" ref="mysql">
    </property>
</bean>

<!-- mysql数据源 -->
    <bean id="mysql" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${dataSourceB.url}" />
        <property name="username" value="${dataSourceB.username}" />
        <property name="password" value="${dataSourceB.password}" />
        <property name="connectionProperties" value="${dataSourceB.driver}"></property>
        <property name="driverClassName" value="${dataSourceB.driver}"></property>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="50" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="1000" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <!-- 设置自动回收超时连接   -->
        <property name="removeAbandoned"  value="true"/>
        <!--  自动回收超时时间(以秒数为单位)  -->
        <property name="removeAbandonedTimeout" value="10"/> 

    </bean>
    <!-- sql server数据源 -->
    <bean id="sqlserver" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${dataSourceC.url}" />
        <property name="username" value="${dataSourceC.username}" />
        <property name="password" value="${dataSourceC.password}" />
        <property name="connectionProperties" value="${dataSourceC.driver}"></property>
        <property name="driverClassName" value="${dataSourceC.driver}"></property>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="50" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="1000" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <!-- 设置自动回收超时连接   -->
        <property name="removeAbandoned"  value="true"/>
        <!--  自动回收超时时间(以秒数为单位)  -->
        <property name="removeAbandonedTimeout" value="10"/> 

    </bean>

    <!-- SqlSessionFactory连接设置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource" />
        <!-- 扫描的实体包 -->
        <property name="typeAliasesPackage" value="com.dome.bean" />
        <!-- 扫描的mybatis配置 -->
        <property name="configLocation" value="classpath:config/mybatisConfig.xml" />
        <!--扫描mapper.xml配置  -->
        <property name="mapperLocations" value="classpath*:com/dome/mapper/**/*.xml" />
    </bean>

    <!--dao层的连接与数据库  -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.dome.dao.source" /><!-- 不能取名 .dao 会与其他冲突 -->
    </bean>
    
    <!-- 配置事务 -->    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dynamicDataSource"/>
    </bean>
        
      <!--配置哪些数据源需要开启事务-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
                <tx:method name="batch*"  propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="save*"   propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="insert*"   propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="import*" propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="add*"    propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="remove*" propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="delete*" propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                <tx:method name="update*" propagation="REQUIRED"  isolation="DEFAULT" rollback-for="Exception"/>
                  <tx:method name="find*"  read-only="true" />
                  <tx:method name="get*"  read-only="true" />
                  <tx:method name="select*"  read-only="true" />
                  <tx:method name="login*"  read-only="true" />
       </tx:attributes>
    </tx:advice>

<bean id="dataSourceExchange" class="com.dome.utils.datasource.DataSourceExchange"/>
    <!-- 配置哪些类的方法需要进行事务管理 -->
    <aop:config>
       <aop:pointcut id="allManagerMethod" expression="execution(* com.dome.service.*.*.*(..))"/>
       <aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod" order="2"/>
       <aop:advisor pointcut-ref="allManagerMethod" advice-ref="dataSourceExchange" order="1"/>
    </aop:config>  
</beans>

这里必须强调order级别的选择数字越小,级别越高,先执行

通过aop自动数据源切换,以及事物的开启都是由aop完成的,这里就要涉及到先后的问题,要不然事物完成在前,选择数据源配置在后,就会出现事物不起作用的问题;

DataSourceExchange;自动选择配置数据源类

package com.dome.utils.datasource;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

public class DataSourceExchange implements MethodBeforeAdvice,AfterReturningAdvice   
{  
  
    @Override  
    public void afterReturning(Object returnValue, Method method,  
            Object[] args, Object target) throws Throwable {  
        CustomerContextHolder.clearCustomerType();  
    } 
    @Override  
    public void before(Method method, Object[] args, Object target)  
            throws Throwable {  
          //这里DataSource是自定义的注解,不是java里的DataSource接口
        Method originalMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
        if (originalMethod.isAnnotationPresent(DbToLoad.class))   
        {   
            CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MSSQL);  
        }  
        else  
        {  
        //target是被织入增强处理的目标对象,通过获取getDataSourceName函数来获取target的数据源名称
            CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
        }  
          
    }
  
}

DataSourceExchange中 before方法与afterReturning的含义是分别在方法指向前执行数据源选择,方法后执行数据源清空;

都通过CustomerContextHolder类来实现;而由DynamicDataSource类来决定spring选择哪个数据源进行连接。以及一个自定义注解DbToLoad类,用来判断在哪些方法上需要执行数据源选择;直接在此方法上使用该注解就可实现数据源选择操作啦;

注意

我看见有很多博客都是通过method.isAnnotationPresent(xx.class)来判断是否有该注解 

我想说的是在通过aop自动选择数据源上通过该方法来判断此方法上是否有该注解 是错误的 这个并不能通过这样获取

不信的小朋友可以去尝试下就知道啦,必须通过

Method originalMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());

originalMethod.isAnnotationPresent(DbToLoad.class);

反射获取才行;

分别的类如下:

CustomerContextHolder:

package com.dome.utils.datasource;

import org.apache.commons.lang.StringUtils;

public class CustomerContextHolder {

    public static final String DATA_SOURCE_MYSQL = "mysql";
    public static final String DATA_SOURCE_MSSQL = "sqlserver";
    //用ThreadLocal来设置当前线程使用哪个dataSource
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static void setCustomerType(String customerType) {
        contextHolder.set(customerType);
    }
    public static String getCustomerType() {
        String dataSource = contextHolder.get();
        if (StringUtils.isEmpty(dataSource)) {
            return DATA_SOURCE_MYSQL;
        }else {
            return dataSource;
        }
    }
    public static void clearCustomerType() {
        contextHolder.remove();
    }
}

DynamicDataSource:

package com.dome.utils.datasource;

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

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

 

DbToLoad:

package com.dome.utils.datasource;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DbToLoad {
    String value() default "mysql";
}

这些都很基础,我就不一一去注解啥意思啦;

怎么去实现,验证呢!

首先service层的实现类:

    @DbToLoad
    @Override
    public PageInfo<User> listUsers__mysql(int pageNum,int pageSize) {
        xxxxxxxxxxxxxx;
        
    }

在此方法上使用@DbToLoad这个自定义注解就可以啦

程序运行时,aop就会自动去判断使用哪个数据源;

有问题的可以留言给我!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ctrl+C+V程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值