多数据库配置需求有两种,一种是因为项目太大,访问量太高,不得不分布多个数据库减轻访问压力,比较多的应用就是读写分离;另一种就是原本不同的两个数据库业务现在要整合到一起,甚至连数据库都不一样,一个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

Java学习,从入门到放弃(一)SpringMVC+Maven+Mybits 多种数据库配置(mysql+sqlserver)AOP方式的更多相关文章

  1. Java性能测试从入门到放弃-概述篇

    Java性能测试从入门到放弃-概念篇 辅助工具 Jmeter: Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试.JMeter 可以用于对服务器.网络 ...

  2. Java入门-浅析Java学习从入门到精通【转】

    一. JDK (Java Development Kit)  JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库 ...

  3. 真正的Java学习从入门到精通

    http://www.it.com.cn/f/edu/059/6/169189.htm 一. 工具篇JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行 ...

  4. Java学习从入门到精通(2) [转载]

    Java Learning Path(二).书籍篇 学习一门新的知识,不可能指望只看一本,或者两本书就能够完全掌握.需要有一个循序渐进的阅读过程.我推荐Oreilly出版的Java系列书籍. 在这里我 ...

  5. Python学习从入门到放弃?我不允许!!!

    嗨,大家好 这里是汐仔 很多人都说学习python学习python,打开书本,三分钟,从入门到放弃. 这怎么可以!!!大家能选择python的原因可能是看它既简单,好入门,现在俨然是语言中的一匹黑马. ...

  6. Java学习从入门到精通(1) [转载]

    Java Learning Path (一).工具篇 一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envi ...

  7. java学习从“菜鸟”到“放弃”

    今天学到java的对象和类中, 由于刚考完c++面向对象与程序设计这门课,对于c++中的类掌握自认为不错,就开始过渡到java. 今天面对的问题,在书写一个类的时候,发现了许多与c++不同的地方. 比 ...

  8. Java性能测试从入门到放弃-详解篇

    Jmeter组件分类说明 Jmeter的组件可以放在任意位置 线程池:用于创建线程.每个线程会"批次顺序"执行任务,因此后面的任务可根据前面的任务决定具体的操作.          ...

  9. java学习-初级入门-面向对象⑥-类与对象-静态static

    这次我们来学习静态(static) 知识点 1.静态方法只能调用静态变量 2.静态变量属于整个Class,会随着发生变化. 案例:定义一个自动增长的学生类. 题目要求: 定义一个学生类,除了姓名.性别 ...

随机推荐

  1. Distributed PostgreSQL on a Google Spanner Architecture – Storage Layer

    转自:https://blog.yugabyte.com/distributed-postgresql-on-a-google-spanner-architecture-storage-layer/ ...

  2. 转 Storm JAVA_HOME is incorrectly set.

    问题可能有两个原因: 1.在环境变量中未设置JAVA_HOME变量名称. 解决办法: 在环境变量中添加. 或者在storm中的bin文件下有一个storm-config.cmd,使用文本打开,查询JA ...

  3. Linux 文件基本属性: chown修改所属组 和 chmod修改文件属性命令

    [root@www /]# ls -l total 64 dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin -rwxrwxr-x 4 root root 4096 ...

  4. git用ssh方式下载代码

    1.运行Git Bash客户端,执行ls ~/.ssh; 如果列出下图这两个rsa文件,那应该就不需要配置ssh key了,如果不放心就将这几个文件删掉,重新生成. 文件的默认目录:C:\Users\ ...

  5. 部署K8S集群

    1.Kubernetes 1.1.概念 kubernetes(通常称为k8s)用于自动部署.扩展和管理容器化应用程序的开源系统.它旨在提供“跨主机集群的自动部署.扩展以及运行应用程序容器的平台”.支持 ...

  6. ICEM——倒角的处理

    原视频下载地址: https://pan.baidu.com/s/1miHMOuk 密码: knc4

  7. PLSQL命令行创建用户 以及 JDBC简单操作

    目录 PLSQL Developer命令行创建用户以及表 课堂要点 ​ JDBC 主外键约束 踩坑之路 设置ORACLE_HOME环境变量 PLSQL Developer命令行创建用户以及表 打开Co ...

  8. 关于Delphi中二维数组赋初始值

    dctb:array[1..2,1..38] of Single=((0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ...

  9. JVM 启动类加载器2

    在运行期,一个Java类是由该类的完全限定名(binary name,二进制名)和用于加载该类的定义类加载器(defining loading)所共同决定的.如果同样名字(即相同的完全限定名)的类由两 ...

  10. 【idea】idea远程调试代码

    一.前置条件 1.idea的代码和远程服务器代码保持一致 二.远程服务器配置 服务启动时,需要给jvm添加指定参数,进行启动 -agentlib:jdwp=transport=dt_socket,se ...