java 通过代码实现动态选择数据源

来源:互联网 发布:心内事无人知 蔡秋凤 编辑:程序博客网 时间:2024/06/06 00:41

类注解

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Specify DataSource (指定数据源,用于执行事物切面时,指定其他数据源)<br> * 注意:<br> * {@code @SpecifyDS}放在类级别上等同于该类的每个公有方法都放上了{@code @SpecifyDS}<br> * {@code @SpecifyDS}只对公有法有效(与Spring的{@code @Transactional}只对公有方法有效道理是一样的,因为都是Spring AOP代理) *  */@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface SpecifyDS {    String value();}

线程独享的数据源对象

public class DataSourceHolder {        public static final ThreadLocal<String> holder = new ThreadLocal<String>();    public static void setDataSource(String name) {        holder.set(name);    }    public static String getDataSouce() {        return holder.get();    }}

动态设置数据源

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 获取当前ThreadLocal设置的dataSource (动态设置的dataSource) */public class DynamicDataSource extends AbstractRoutingDataSource {        @Override    protected Object determineCurrentLookupKey() {        return DataSourceHolder.getDataSouce();    }}

数据源自动选择的切面类

import java.lang.reflect.Method;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.transaction.annotation.Transactional;public class DataSourceAspect implements org.springframework.core.Ordered {        private static final String READ_ONLY_METHOD_PREFFIX = "select";    private static final String DEFAULT_MASTER_SUFFIX = "MasterDataSource";    private static final String DEFAULT_SLAVE_SUFFIX = "SlaveDataSource";    private static final int DEFAULT_ORDER = -1;        /**     * CUD【创建(Create)、更新(Update)和删除(Delete)】所使用的数据源     * <br>默认使用 {主库}     */    private String cudDBSuffix = DEFAULT_MASTER_SUFFIX;        /**     * R【读取(Read)】所使用的数据源     * <br>默认使用 {从库}     */    private String readDBSuffix = DEFAULT_SLAVE_SUFFIX;        /**     * Spring Aspect的顺序,如果一个方法上有多个切面,则值越小的先执行。     */    private int order = DEFAULT_ORDER;        /**     * 执行事物切面时默认使用的数据源(通常配置为项目的主数据库的主库)     */    private String defaultTransDb;        public void before(JoinPoint point) {        if (DataSourceHolder.getDataSouce() == null) {            // 根据包路径名 获取dataSource的key前缀,这样效率太低,改成根据Mapper前缀来获取            Class<?>[] classz = point.getTarget().getClass().getInterfaces();            String interfacePath = classz[0].getName();            // 获取dao和mapper之间的包名,这儿包名与xml的dataSource的key前缀相同            int daoIndex = interfacePath.indexOf("dao");            int mapperIndex = interfacePath.indexOf("mapper");            String packet = interfacePath.substring(daoIndex + 4, mapperIndex - 1);            if (point.getSignature().getName().indexOf(READ_ONLY_METHOD_PREFFIX) != -1) {                DataSourceHolder.setDataSource(packet + readDBSuffix);            } else {                DataSourceHolder.setDataSource(packet + cudDBSuffix);            }        }    }        public void beforeTrans(JoinPoint point) {        Signature signature = point.getSignature();        MethodSignature methodSignature = (MethodSignature) signature;        Method method = methodSignature.getMethod();        // 获取目标类        Class<?> target = point.getTarget().getClass();        Method targetMethod = null;        try {            targetMethod = target.getMethod(method.getName(), method.getParameterTypes());        } catch (NoSuchMethodException e) {            e.printStackTrace();        }        // 根据目标类方法中的注解,选择数据源        if (targetMethod != null) {            Transactional transactional = targetMethod.getAnnotation(Transactional.class);            if (transactional != null) {                SpecifyDS specifyDSCls = target.getAnnotation(SpecifyDS.class);                SpecifyDS specifyDS = targetMethod.getAnnotation(SpecifyDS.class);                if (specifyDS != null) {                    DataSourceHolder.setDataSource(specifyDS.value());                } else if (specifyDSCls != null) {                    DataSourceHolder.setDataSource(specifyDSCls.value());                } else {                    DataSourceHolder.setDataSource(defaultTransDb);                }            }        }    }    public void after() {        DataSourceHolder.setDataSource(null);    }            @Override    public int getOrder() {        return order;    }        public void setOrder(int order){        this.order = order;    }    /**     * @return the cudDBSuffix     */    public String getCudDBSuffix() {        return cudDBSuffix;    }    /**     * @param cudDBSuffix the cudDBSuffix to set     */    public void setCudDBSuffix(String cudDBSuffix) {        this.cudDBSuffix = cudDBSuffix;    }    /**     * @return the readDBSuffix     */    public String getReadDBSuffix() {        return readDBSuffix;    }    /**     * @param readDBSuffix the readDBSuffix to set     */    public void setReadDBSuffix(String readDBSuffix) {        this.readDBSuffix = readDBSuffix;    }    /**     * @return the defaultTransDb     */    public String getDefaultTransDb() {        return defaultTransDb;    }    /**     * @param defaultTransDb the defaultTransDb to set     */    public void setDefaultTransDb(String defaultTransDb) {        this.defaultTransDb = defaultTransDb;    }}

JDBC配置文件

<?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"   xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://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"   default-autowire="byName"><!--druid--><bean id="MasterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.map.master.url}"/><property name="username" value="${jdbc.map.master.username}"/><property name="password" value="${jdbc.map.master.password}"/><property name="initialSize" value="5"/><property name="minIdle" value="5"/><property name="maxActive" value="10"/><property name="timeBetweenEvictionRunsMillis" value="5000"/><property name="minEvictableIdleTimeMillis" value="60000"/><property name="validationQuery" value="SELECT 'x'"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="false"/><property name="testOnReturn" value="false"/><property name="removeAbandoned" value="true"/><property name="removeAbandonedTimeout" value="1800"/><property name="logAbandoned" value="true"/><property name="filters" value="stat"/><property name="connectionProperties" value="druid.stat.slowSqlMillis=100"/></bean><bean id="SlaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.map.slave.url}"/><property name="username" value="${jdbc.map.slave.username}"/><property name="password" value="${jdbc.map.slave.password}"/><property name="initialSize" value="5"/><property name="minIdle" value="5"/><property name="maxActive" value="10"/><property name="timeBetweenEvictionRunsMillis" value="5000"/><property name="minEvictableIdleTimeMillis" value="30000"/><property name="validationQuery" value="SELECT 'x'"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="false"/><property name="testOnReturn" value="false"/><property name="removeAbandoned" value="true"/><property name="removeAbandonedTimeout" value="1800"/><property name="logAbandoned" value="true"/><property name="filters" value="stat"/><property name="connectionProperties" value="druid.stat.slowSqlMillis=100"/></bean>    <!-- 配置DynamicDataSource代替普通dataSource,将实际数据源的DataSource赋值给它 --><bean id="dataSource" class="com.groupname.commons.db.DynamicDataSource"><property name="targetDataSources"><map key-type="java.lang.String"><entry key="MasterDataSource" value-ref="MasterDataSource"/><entry key="SlaveDataSource" value-ref="SlaveDataSource"/></map></property>          <!-- 默认主库 --><property name="defaultTargetDataSource" ref="MasterDataSource"/></bean><!-- 配置数据库注解aop,mapper层选择数据源 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>     <bean id="multiDataSourceAspect" class="com.groupname.commons.db.DataSourceAspect">        <!-- 执行事物切面时默认使用的数据源(通常配置为项目的主数据库的主库)。如果不配置,将默认使用上面的defaultTargetDataSource -->        <property name="defaultTransDb" value="mapMasterDataSource" />        <!-- 以下是默认配置,一般无需修改 -->        <!-- CUD【创建(Create)、更新(Update)和删除(Delete)】所使用的数据源。默认使用 {主库} -->        <!-- <property name="cudDBSuffix" value="MasterDataSource" /> -->        <!-- R【读取(Read)】所使用的数据源。默认使用 {从库} -->        <!-- <property name="readDBSuffix" value="SlaveDataSource" /> -->    </bean>    <aop:config><aop:aspect ref="multiDataSourceAspect" >            <!-- 为DAO层的Mapper自动选择数据源:默认规则是select开头的方法选择从库,其他像update、insert等方法选择主库。                 可更改multiDataSourceAspect的slaveSuffix的值来改变默认策略 --><aop:pointcut id="daoDataSourceAutoSelect" expression="execution(* com.groupname.*.center.dao.*.mapper.*.*(..))"/><aop:before pointcut-ref="daoDataSourceAutoSelect" method="before"/><aop:after pointcut-ref="daoDataSourceAutoSelect" method="after"/>                        <!-- 为事务控制自动选择数据源:扫描有@Transactional注解的service方法,默认切换到上面配置的defaultTransDb数据源去执行事务。                 如果要切换其他数据源,可以在方法上再加一个@SpecifyDS({value}),在value中注明是哪个数据源ID -->            <aop:pointcut id="transDataSourceAutoSelect" expression="execution(* com.groupname.*.center.service.*.*(..))||execution(* com.groupname.*.manager.service.biz.*.*(..))"/>            <aop:before pointcut-ref="transDataSourceAutoSelect" method="beforeTrans"/>            <aop:after pointcut-ref="transDataSourceAutoSelect" method="after"/></aop:aspect></aop:config><!-- 定义事务管理器  --><bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 事务支持注解  --><tx:annotation-driven transaction-manager="transactionManager"/><!--编程式事务使用--><bean id="txDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition"></bean></beans>


3 0
原创粉丝点击