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

 

posted @ 2019-08-06 12:26  一 定 会 去 旅 行  阅读(771)  评论(0编辑  收藏  举报