zoukankan      html  css  js  c++  java
  • Java学习,从入门到放弃(一)SpringMVC+Maven+Mybits 多种数据库配置(mysql+sqlserver)AOP方式

    多数据库配置需求有两种,一种是因为项目太大,访问量太高,不得不分布多个数据库减轻访问压力,比较多的应用就是读写分离;另一种就是原本不同的两个数据库业务现在要整合到一起,甚至连数据库都不一样,一个mysql,一个sqlserver,小编目前的项目就是属于后者。

    要实现读写分离,首先得保证主从复制,即写库的数据能实时复制到读库里,这样才能保证数据无差别,这不是今天要学习的内容,小编项目目前没用到~~本篇讲的是多数据库配置。整合多种数据库的方式有两种:分包和AOP,本文只记录AOP方式。

    参考这篇文章:https://www.cnblogs.com/weixupeng/p/9720472.html (排版不太友好)

    1、由于使用Maven,首先pom.xml引入要链接数据库的驱动jar包依赖,mysql可以直接引入,但sqlserver和orcale要先自己下载到本地,然后手动引入后才能添加依赖,添加方式如这篇sqlserver示例的文章:https://www.cnblogs.com/dawnheaven/p/5738477.html ,也可以从别的渠道下载最新版本,但记得mvn时要写正确的版本号。

    mvn install:install-file -Dfile=sqljdbc4.jar -Dpackaging=jar -DgroupId=com.microsoft.sqlserver -DartifactId=sqljdbc4 -Dversion=4.0

    这里记得改成自己的版本 -Dfile="jar包的绝对路径+完整文件名称版本"
    此方式同样适用于Linux环境。

    2、配置properties文件,这个文件每个人使用的名称可能不同,有的人新建个db.properties,有的是config.properties,这个无关紧要,在spring.xml文件中配置对应的自己文件就可以了。

    3、配置spring.xml,这个文件也有很多不同的名字,有的文章用application-content.xml,有的文件叫springmvc.xml,我的叫spring-context.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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation=" 
              http://www.springframework.org/schema/beans 
              http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
              http://www.springframework.org/schema/context 
              http://www.springframework.org/schema/context/spring-context-3.0.xsd
              http://www.springframework.org/schema/mvc     
              http://www.springframework.org/schema/mvc/spring-mvc.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">

    有的说最后一行是 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 有的说是 http://www.springframework.org/schema/aop/spring-aop.xsd
    
    

    需要在pom.xml中添加依赖:

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.8.0</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.0</version>
            </dependency>

    其他的配置按别的文章都差不多,唯有一点需要注意!

        <bean id="dynamicDataSource" class="com.dataSourcer.ThreadLocalRountingDataSource">
            <property name="defaultTargetDataSource" ref="dataSource_daka"/>
            <property name="targetDataSources">
                <map key-type="com.dataSourcer.DataSources"> 这个地方如果java代码定义的枚举,则需要使用枚举类,否则可以使用java.lang.String
                    <entry key="daka" value-ref="dataSource_daka"></entry> 这里要与自定义枚举类的key值一致
                    <entry key="kaoqin" value-ref="dataSource_kaoqin"></entry> 
                </map>
            </property>
        </bean>

    4、定义自己的数据库枚举类。

    5、定义ThreadLocalRountingDataSource类继承自AbstractRoutingDataSource,这个基本都一样没什么特殊的。

    6、自定义注解类,这个很重要但没什么要说的。

    7、定义数据库管理类DataSourceTypeManager(有的用 DataSourceContextHolder 命名,个人感觉还是Manager好理解些)。此类中的ThreadLocal实现线程安全还是要加的,而且特别推荐以下写法:

        // ThreadLocal类是实现线程安全的关键,因为数据操作大部分都是并发执行,所以必须要考虑线程安全
        private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
     
            @Override
            protected DataSources initialValue() {
                return DataSources.daka;
            }
        };

    8、最后重头戏DynamicDataSourceAspect,其中可以定义切点,当然也可以定义在spring.xml中。

    最后以下为我的项目的代码汇总:

    jdbc.url = jdbc:mysql://IP:3306/数据库名称?useUnicode=true&amp;characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
    jdbc.username = 账号
    jdbc.password= 密码
    
    sql.url = jdbc:sqlserver://ip:1433;databaseName=数据库名称
    sql.username = 账号
    sql.password= 密码
    config.properties
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.8.0</version>
            </dependency>
            <dependency>
                    <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.0</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.40</version>
            </dependency>
            <dependency>
                    <groupId>com.microsoft.sqlserver</groupId>
                      <artifactId>sqljdbc4</artifactId>
                      <version>4.0</version>
              </dependency>sqlserver记得要手动下载添加依赖哦        
    pom.xml添加补充的依赖
    <!-- 有的叫spring-context.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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:cache="http://www.springframework.org/schema/cache"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation=" 
              http://www.springframework.org/schema/cache    
                http://www.springframework.org/schema/cache/spring-cache-4.2.xsd
              http://www.springframework.org/schema/beans 
              http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
              http://www.springframework.org/schema/context 
              http://www.springframework.org/schema/context/spring-context-3.0.xsd
              http://www.springframework.org/schema/mvc     
              http://www.springframework.org/schema/mvc/spring-mvc.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">
    
        <!-- 缓存配置(两种) -->
        <!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
        <cache:annotation-driven cache-manager="cacheManager" />
        <!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
        <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
            <property name="configLocation" value="classpath:ehcache.xml" />
        </bean>
        <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
            <property name="cacheManager" ref="cacheManagerFactory" />
        </bean>
    
        <!-- 把标记了@Controller注解的类转换为bean -->
        <context:component-scan base-package="com.test.controller,com.test.service" /> 
        
        <!-- 多媒体解析器 -->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="maxUploadSize" value="100000000"/>
            <property name="defaultEncoding" value="UTF-8"/>
        </bean>
            
        <mvc:annotation-driven>
            <mvc:message-converters>
                <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/html;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                        </list>
                    </property>
                    <property name="features">
                        <array>
                            <value>WriteMapNullValue</value>
                            <value>WriteNullStringAsEmpty</value>
                        </array>
                    </property>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>
        
        <!-- 配置数据源,这里就是你的properties文件 -->
        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
            <property name="location">  
                <value>/WEB-INF/classes/config.properties</value>  
            </property>  
            <property name="fileEncoding" value="utf-8" />  
        </bean>
        
        <bean id="dataSource_daka" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!-- 基本属性 url、user、password -->
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <!-- 配置初始化大小、最小、最大 -->
            <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="false" />
            <property name="testOnReturn" value="false" />
    
            <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
            <property name="poolPreparedStatements" value="false" />
            <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
        </bean>
        <!-- 测试多数据源1 -->
        <bean id="dataSource_kaoqin" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!-- 基本属性 url、user、password -->
            <property name="url" value="${sql.url}" />  
            <property name="username" value="${sql.username}" />  
            <property name="password" value="${sql.password}" /> 
            <property name="connectionProperties" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></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="false" />
            <property name="testOnReturn" value="false" />
    
            <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
            <property name="poolPreparedStatements" value="false" />
            <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
        </bean> 
        
        <bean id="dynamicDataSource" class="com.dataSourcer.ThreadLocalRountingDataSource">
            <property name="defaultTargetDataSource" ref="dataSource_daka"/>
            <property name="targetDataSources">
                <map key-type="com.dataSourcer.DataSources">
                    <entry key="daka" value-ref="dataSource_daka"></entry>
                    <entry key="kaoqin" value-ref="dataSource_kaoqin"></entry> 
                </map>
            </property>
        </bean>
        <!-- 开启AOP -->
        <aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true" />
        <!-- 注册切面Bean -->
        <bean id="dynamicDataSourceAspect" class="com.dataSourcer.DynamicDataSourceAspect"></bean>
    
        
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dynamicDataSource" />
            <!-- <property name="configLocation" value="classpath:mybatis-config.xml"></property> -->
            <property name="mapperLocations">
                <list>
                    <value>classpath:com/test/mapper/*.xml</value>
                </list>
            </property>
        </bean>
        
        <!-- 自动扫描所有的Mapper接口与文件 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.test.mapper"></property>
        </bean>
        
        <!-- 事务 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dynamicDataSource"></property>
        </bean>
        
        <tx:annotation-driven transaction-manager="transactionManager"/>
        
        
        
        
    
    </beans>
    spring.xml配置
    package com.dataSourcer;
    
    /**
     * 编写枚举类,表示数据源的key
     *
     */
    public enum DataSources {
        daka,
        kaoqin
    }
    枚举类DataSources
    package com.dataSourcer;
    /**
     * 编写线程安全的数据源切换类DataSourceTypeManager 
     *
     */
    public class DataSourceTypeManager {
        // ThreadLocal类是实现线程安全的关键,因为数据操作大部分都是并发执行,所以必须要考虑线程安全
        private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
     
            @Override
            protected DataSources initialValue() {
                return DataSources.daka;
            }
        };
     
        public static DataSources get() {
            return dataSourceTypes.get();
        }
     
        public static void set(DataSources dataSourceType) {
            dataSourceTypes.set(dataSourceType);
        }
     
        public static void reset() {
            dataSourceTypes.set(DataSources.daka);
        }
        
        public static void clear() {
            dataSourceTypes.remove();
        }
    
    }
    DataSourceTypeManager
    package com.dataSourcer;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    /**
     * 扩展类AbstractRoutingDataSource 
     *
     */
    public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceTypeManager.get();
        }
    
    }
    ThreadLocalRountingDataSource
    package com.dataSourcer;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 编写自定义注解类 
     *
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TargetDataSource {
    
        DataSources dataSourceKey() default DataSources.daka;
    }
    编写自定义注解类TargetDataSource
    package com.dataSourcer;
    
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    
    /**
     * 编写数据源切换切面类:DynamicDataSourceAspect 
     *
     */
    @Aspect
    @Order(-1)
    @Component
    public class DynamicDataSourceAspect {
        
        @Pointcut("execution(* com.test.service.*.*(..))")
        public void pointCut() {
            
        }
     
        /**
         * 执行方法前更换数据源
         *
         * @param joinPoint        切点
         * @param targetDataSource 动态数据源
         */
        @Before("@annotation(targetDataSource)")
        public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
            DataSources dataSourceKey = targetDataSource.dataSourceKey();
            if (dataSourceKey == DataSources.kaoqin) {
                DataSourceTypeManager.set(DataSources.kaoqin);
            } else {
                DataSourceTypeManager.set(DataSources.daka);
            }
        }
     
        /**
         * 执行方法后清除数据源设置
         *
         * @param joinPoint        切点
         * @param targetDataSource 动态数据源
         */
        @After("@annotation(targetDataSource)")
        public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
            DataSourceTypeManager.clear();;
        }
     
        @Before(value = "pointCut()")
        public void doBeforeWithSlave(JoinPoint joinPoint) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //获取当前切点方法对象
            Method method = methodSignature.getMethod();
            if (method.getDeclaringClass().isInterface()) {//判断是否为接口方法
                try {
                    //获取实际类型的方法对象
                    method = joinPoint.getTarget().getClass()
                            .getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
                } catch (NoSuchMethodException e) {
                }
            }
            if (null == method.getAnnotation(TargetDataSource.class)) {
                DataSourceTypeManager.set(DataSources.daka);
            }
        }
    }
    数据源切换切面类:DynamicDataSourceAspect

    最后的最后,在Service中使用方式:

    package com.test.service;
    
    import java.util.HashMap;
    import java.util.List;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.dataSourcer.DataSources;
    import com.dataSourcer.TargetDataSource;
    import com.test.mapper.KaoqinSqlserverMapper; 
    
    @Service
    public class KaoqinSqlserverService {
    @Autowired
    private KaoqinSqlserverMapper kaoqin; /** 员工最近10个月考勤情况汇总 */ @TargetDataSource(dataSourceKey = DataSources.kaoqin) public List<HashMap<String, Object>> userkaoqin_all(String emplid){ return kaoqin.userkaoqin_all(emplid); } /** 员工月度考勤详情 */ @TargetDataSource(dataSourceKey = DataSources.kaoqin) public List<HashMap<String, Object>> userkaoqin_details(String emplid, String minday, String maxday){ return kaoqin.userkaoqin_details(emplid, minday, maxday); } }

    下面这个文章是在Controller中调用的,俺没有测试,有兴趣的可以看看:https://www.cnblogs.com/haha12/p/10613549.html

  • 相关阅读:
    Go 环境变量相关操作
    Go命令行参数解析flag包
    go sync.once用法
    使用go语言编写IOS和Android程序
    go map的使用
    go runtime.Gosched()的作用分析
    go中的读写锁RWMutex
    go互斥锁Mutex
    go import使用及. _的作用解析
    利用channel在goroutins之间控制同步和传递数据
  • 原文地址:https://www.cnblogs.com/jying/p/11294615.html
Copyright © 2011-2022 走看看