zoukankan      html  css  js  c++  java
  • Spring+mybatis 实现aop数据库读写分离,多数据库源配置

    在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库。Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询。因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验。我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力。

    废话不多说,多数据源配置和主从数据配置原理一样

    1、首先配置  jdbc.properties 两个数据库 A 和 B

    #============ 双数据源 ======#
    
    #----------------------A servers--------------------------#
    A.driver=com.mysql.jdbc.Driver
    A.url=jdbc:mysql://localhost:3619/gps4?useUnicode=true&characterEncoding=utf8
    A.username=gpsadmin
    A.password=1qaz&619

    #----------------------B servers--------------------------# B.driver=com.mysql.jdbc.Driver B.url=jdbc:mysql://localhost:3619/gps6?useUnicode=true&characterEncoding=utf8 B.username=gpsadmin B.password=1qaz&619

     2、配置 spring-mybatis.xml 文件【重要】

    <!-- 引入配置文件 -->
        <bean id="propertyConfigurer"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="classpath:resources/jdbc.properties" />
        </bean>
        
    
        <!-- DBCP连接池 -->
        <!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
            destroy-method="close"> <property name="driverClassName" value="${driver}" 
            /> <property name="url" value="${url}" /> <property name="username" value="${username}" 
            /> <property name="password" value="${password}" /> 初始化连接大小 <property name="initialSize" 
            value="${initialSize}"></property> 连接池最大数量 <property name="maxActive" value="${maxActive}"></property> 
            连接池最大空闲 <property name="maxIdle" value="${maxIdle}"></property> 连接池最小空闲 <property 
            name="minIdle" value="${minIdle}"></property> 获取连接最大等待时间 <property name="maxWait" 
            value="${maxWait}"></property> </``> -->
        
        <!-- 【重点】 A 数据源 -->
        <bean name="dataSourceA" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${A.driver}" />
            <property name="url" value="${A.url}" />
            <property name="username" value="${A.username}" />
            <property name="password" value="${A.password}" />
        </bean>
        
        <!-- 【重点】 B 数据源 -->
        <bean name="dataSourceB" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${B.driver}" />
            <property name="url" value="${B.url}" />
            <property name="username" value="${B.username}" />
            <property name="password" value="${B.password}" />
        </bean>
    
        <!--【重点】 双数据源  配合  -->
        <bean id="dataSource" class="com.ifengSearch.common.database.DynamicDataSource">
            <property name="defaultTargetDataSource" ref="dataSourceB"/>
            <property name="targetDataSources">
                <map>
                    <entry key="dataSourceA" value-ref="dataSourceA"/>
                    <entry key="dataSourceB" value-ref="dataSourceB"/>
                </map>
            </property>
        </bean>
        
        <!-- 【重点】 加入 aop 自动扫描  DataSourceAspect  配置数据库注解aop -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        <bean id="manyDataSourceAspect" class="com.ifengSearch.common.database.DataSourceAspect" />
        <aop:config>
            <!-- 扫描 注解的 数据源 -->
            <aop:aspect id="c" ref="manyDataSourceAspect">
                <aop:pointcut id="tx" expression="execution(* com.ifengSearch.*.dao.*.*(..))"/>
                <aop:before pointcut-ref="tx" method="before"/>
            </aop:aspect>
        </aop:config>
        
        
        
         <!-- 配置数据连接 工厂   自动扫描mapping.xml文件 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!-- 自动扫描mapping.xml文件 -->
            <property name="mapperLocations" value="classpath:com/ifengSearch/*/mapping/*.xml"></property>
        </bean>
        
         <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
         <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                 <property name="basePackage" value="com.ifengSearch.*.dao" />
                 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
         </bean>
         
    
        <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx 事务 -->
        <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    View Code

    3、编写几个java类动态调用数据源【重要】

      a:自定义一个注解,负责动态调用数据源

          

    package com.ifengSearch.common.database;
    
    import java.lang.annotation.*;
    
    /**
     * 设置 数据源 注解标签的用法 写上注解标签,
     * 调用相应方法切换数据源咯(就跟你设置事务一样) 
     * 【也可以配置 主从数据库】
     * 
     * @author flm
     * @2017年9月12日
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface DataSource {
    
        public static String dataSourceA = "dataSourceA"; // A数据源
        public static String dataSourceB = "dataSourceB"; // B数据源
    
        String value();
    }

     b、数据源的获取 Object   aop实现 (反射)

    package com.ifengSearch.common.database;
    import java.lang.reflect.Method;
    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
    /**
     * 数据源的获取
     * aop实现 (反射)
     * @author flm
     * @2017年9月12日
     */
    public class DataSourceAspect{
        
        private Logger log = Logger.getLogger(DataSourceAspect.class);
        
        public void before(JoinPoint point)
        {
            Object target = point.getTarget();// 拦截的实体类
            String method = point.getSignature().getName();// 拦截的方法名称
            Class<?>[] classz = target.getClass().getInterfaces();
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                    .getMethod().getParameterTypes();// 拦截的方法参数类型
            
            try {
                Method m = classz[0].getMethod(method, parameterTypes);
                if (m != null && m.isAnnotationPresent(DataSource.class)) {
                    DataSource data = m
                            .getAnnotation(DataSource.class);
                    DataSourceHolder.setDataSource(data.value());
                    log.info("数据源的获取    DataSource: "+data.value());
                }
                
            } catch (Exception e) {
                log.error("数据源的获取  aop实现 出错:"+e.getMessage());
            }
        }
    }    

    c、DataSourceHolder  数据源操作  获取数据源 帮助类

    package com.ifengSearch.common.database;
    
    /**
     * 多数据源
     * 数据源操作  获取数据源
     * @author flm
     * @2017年9月12日
     */
    public class DataSourceHolder {
        
        //线程本地环境
        private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
        //设置数据源
        public static void setDataSource(String customerType) {
            dataSources.set(customerType);
        }
        //获取数据源
        public static String getDataSource() {
            return (String) dataSources.get();
        }
        //清除数据源
        public static void clearDataSource() {
            dataSources.remove();
        }
     
    }

    d、 我们还需要实现spring的抽象类AbstractRoutingDataSource,就是实现determineCurrentLookupKey方法:

    package com.ifengSearch.common.database;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * 多数据源
     * 获取数据源(依赖于spring)
     * @author flm
     * @2017年9月12日
     */
    public class DynamicDataSource extends AbstractRoutingDataSource{
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceHolder.getDataSource();
        }
    }

    4、接下来就可以看结果了

      我在dao层直接调用  

    public interface UserDao {
        
        /**
         * 登录判断 【数据源B】
         */
        @DataSource(value=DataSource.dataSourceB)
        public List<UserBean> getLoginUserList(@Param("loginName")String loginName,@Param("loginPwd")String loginPwd);
    
        /**
         * 查找上一级 服务商 【数据源A】
         */
        @DataSource(value=DataSource.dataSourceA)
        public UserBean getServerUser(@Param("u_last_id")Integer u_last_id);
    }

    经过本人验证 测试页面 成功了

    也可参考文章 http://www.cnblogs.com/surge/p/3582248.html

  • 相关阅读:
    了解大数据的特点、来源与数据呈现方式
    结对项目
    第四次作业
    阅读《构建之法》1-5章有感
    iOS 应用如何完全支持 IPv6-ONLY 网络?
    丙申年把真假美猴王囚禁在容器中跑 ASP.NET Core 1.0
    ASP.NET Core 1.0 部署 HTTPS (.NET Framework 4.5.1)
    推荐一款跨平台的 Azure Storage Explorer
    更改 Skype for Business Online 的 Sip 地址以匹配UPN
    C# 计算字符串在控制台中的显示长度
  • 原文地址:https://www.cnblogs.com/lemon-flm/p/7510774.html
Copyright © 2011-2022 走看看