结合Spring实现数据库读写分离

**数据库配置为一个主库 多个从库 主库用于写操作 从库只读操作 
读写分离实现即为配置两个数据源,一个用于读写 连接主库 假设为ds_wr,一个用于只读 连接从库 假设为ds_r。

对数据库读操作时,操作ds_r数据源。 
对数据源写操作时,操作ds_wr数据源。

读写分离可以有两种实现方式

第一种

写两套mappper 
mapper写两套 一套用于读写 一套用于只读 **

    <bean id="sqlSessionFactory_wr" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource_wr"/>  
        <!-- 自动扫描mapping.xml文件-->  
        <!--  
        <property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping/*.xml" />  
         -->  
        <property name="mapperLocations" value="classpath:mapping1/*.xml"/>  
    </bean>  
    <bean id="sqlSessionFactory_r" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource_r"/>  
        <!-- 自动扫描mapping.xml文件-->  
        <!--  
        <property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping2/*.xml" />  
         -->  
        <property name="mapperLocations" value="classpath:mapping/*.xml"/>  
    </bean>  
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.ifeng.auto.we_provider.dao1"/>  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_wr"/>  
    </bean>  
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.ifeng.auto.we_provider.dao2"/>  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_r"/>  
    </bean>  

dao1包中mapper是读写方法 对应xml文件在mapping1文件夹中 
dao2包中mapper是只读方法 对应xml文件在mapping2文件夹中

    User user = userMapper2.get(1);  
    user.setName("Susan")  
    userMapper1.update(user);  

第二种

通过实现AbstractRoutingDataSource类来动态管理数据源,本篇博客主要讲这部分内容

利用面向切面思维,每一次进入service方法前,选择数据源

首先pom.xml中添加依赖

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->  
    <dependency>  
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjrt</artifactId>  
        <version>1.7.4</version>  
    </dependency>  
            <dependency>  
                <groupId>org.aspectj</groupId>  
                <artifactId>aspectjweaver</artifactId>  
                <version>1.7.4</version>  
            </dependency>  

实现AbstractRoutingDataSource类 作为数据源

    package com.ifeng.auto.we_provider.common.db;  

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

    /** 
     * Created by Terry on 2016/7/23. 
     */  
    public class DynamicDataSource extends AbstractRoutingDataSource {  

        @Override  
        protected Object determineCurrentLookupKey() {  
            return DynamicDataSourceHolder.getDataSouce();  
        }  

    }  

用ThreadLcoal管理当前数据源

    package com.ifeng.auto.we_provider.common.db;  

    public class DynamicDataSourceHolder {  
        public static final ThreadLocal<String> holder = new ThreadLocal<String>();  

        public static void putDataSource(String name) {  
            holder.set(name);  
        }  

        public static String getDataSouce() {  
            return holder.get();  
        }  
    }  

用注解的形式实现AOP管理数据源

    package com.ifeng.auto.we_provider.annotation;  

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

    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.METHOD)  
    public @interface DataSource {  
        String value();  
    }  

切面类

    package com.ifeng.auto.we_provider.common.proxy;  


    import com.ifeng.auto.we_provider.annotation.DataSource;  
    import com.ifeng.auto.we_provider.common.db.DynamicDataSourceHolder;  
    import org.aspectj.lang.JoinPoint;  
    import org.aspectj.lang.reflect.MethodSignature;  

    import java.lang.reflect.Method;  

    /** 
     * Created by Terry on 2016/7/23. 
     */  
    public class DataSourceAspect {  
        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();  
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())  
                    .getMethod().getParameterTypes();  
            try {  
                Method m = classz.getMethod(method, parameterTypes);  
                System.out.println(m.getName());  
                if (m != null && m.isAnnotationPresent(DataSource.class)) {  
                    DataSource data = m.getAnnotation(DataSource.class);  
                    DynamicDataSourceHolder.putDataSource(data.value());  
                }  

            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  

将注解放在service实现类的方法前,自动设置当前数据源为注解中数据源。

接下来配置aop以及数据源。

mybatis.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:context="http://www.springframework.org/schema/context"  
           xmlns:aop="http://www.springframework.org/schema/aop"  
           xsi:schemaLocation="http://www.springframework.org/schema/beans  
                          http://www.springframework.org/schema/beans/spring-beans-4.1.xsd  
                          http://www.springframework.org/schema/context    
                          http://www.springframework.org/schema/context/spring-context-4.1.xsd  
                          http://www.springframework.org/schema/aop  
                          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
        <!-- 引入配置文件 -->  
        <context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"/>  

        <!-- 数据源配置 -->  
        <bean id="dataSource_wr" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
            <property name="url" value="${db.url}"/>  
            <property name="username" value="${db.username}"/>  
            <property name="password" value="${db.password}"/>  
            <property name="connectionProperties" value="${db.driver}"></property>  

            <!-- 配置初始化大小、最小、最大 -->  
            <property name="initialSize" value="1"/>  
            <property name="minIdle" value="1"/>  
            <property name="maxActive" value="20"/>  

            <!-- 配置获取连接等待超时的时间 -->  
            <property name="maxWait" value="60000"/>  

            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
            <property name="timeBetweenEvictionRunsMillis" value="60000"/>  

            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
            <property name="minEvictableIdleTimeMillis" value="300000"/>  

            <property name="validationQuery" value="SELECT 'x'"/>  
            <property name="testWhileIdle" value="true"/>  
            <property name="testOnBorrow" value="true"/>  
            <property name="testOnReturn" value="false"/>  
        </bean>  
        <bean id="dataSource_r" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
            <property name="url" value="${db1.url}"/>  
            <property name="username" value="${db1.username}"/>  
            <property name="password" value="${db1.password}"/>  
            <property name="connectionProperties" value="${db.driver}"></property>  

            <!-- 配置初始化大小、最小、最大 -->  
            <property name="initialSize" value="1"/>  
            <property name="minIdle" value="1"/>  
            <property name="maxActive" value="20"/>  

            <!-- 配置获取连接等待超时的时间 -->  
            <property name="maxWait" value="60000"/>  

            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
            <property name="timeBetweenEvictionRunsMillis" value="60000"/>  

            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
            <property name="minEvictableIdleTimeMillis" value="300000"/>  

            <property name="validationQuery" value="SELECT 'x'"/>  
            <property name="testWhileIdle" value="true"/>  
            <property name="testOnBorrow" value="true"/>  
            <property name="testOnReturn" value="false"/>  
        </bean>  

        <bean id="dataSource" class="com.ifeng.auto.we_provider.common.db.DynamicDataSource">  
            <property name="targetDataSources">  
                <map key-type="java.lang.String">  
                    <!-- write -->  
                    <entry key="write" value-ref="dataSource_wr"/>  
                    <!-- read -->  
                    <entry key="read" value-ref="dataSource_r"/>  
                </map>  

            </property>  
            <property name="defaultTargetDataSource" ref="dataSource_wr"/>  
        </bean>  

        <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件-->  
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
            <property name="dataSource" ref="dataSource"/>  
            <!-- 自动扫描mapping.xml文件-->  
            <!--  
            <property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping/*.xml" />  
             -->  
            <property name="mapperLocations" value="classpath:mapping/*.xml"/>  
        </bean>  

        <bean id="transactionManager"  
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
            <property name="dataSource" ref="dataSource" />  
        </bean>  

        <aop:aspectj-autoproxy proxy-target-class="true"/>  
        <bean id="dataSourceAspect" class="com.ifeng.auto.we_provider.common.proxy.DataSourceAspect"/>  
        <aop:config>  
        <aop:aspect id="c" ref="dataSourceAspect">  
        <aop:pointcut id="tx" expression="execution(* com.ifeng.auto.we_provider.service..*.*(..))"/>  
        <aop:before pointcut-ref="tx" method="before"/>  
        </aop:aspect>  
        </aop:config>  

        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage" value="com.ifeng.auto.we_provider.dao"/>  
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>  
        </bean>  

    </beans>  

在service实现类中

    @DataSource("write")  
    public void savetag(UserTag userTag) {  
        userTagMapper.addUserTag(userTag);  
    }  
    @DataSource("read")  
    public UserTag getUserTagByUUID(String uuid) {  
        return userTagMapper.getUserTagByUUID(uuid);  
    }  

至此 读写分离实现

猜你喜欢

转载自blog.csdn.net/LJJ1338/article/details/81561422
今日推荐