SSM框架之多数据源配置
多数据源的应用场景:主要是数据库拆分后,怎样让多个数据库结合起来来达到业务需求。
SSM框架(Spring+SpringMVC+MyBatis(MyBatis-Plus))是目前最常用的,此次仍然是maven工程。
关于这个多数据源例子,我已经上传到我的github上,地址为:https://github.com/youcong1996/study_simple_demo.git
不过需要注意的是,分支为demo1,不是主分支,如图所示:
如果下面的示例,你们看不懂或者不能理解,可以git clone我的地址
在编程的世界里,简洁即完美。
如何实现多数据源?
一句话,三个类加xml配置即可达到这个目的。
一、编写三个类
AbstractDynamicDataSource.java
package com.blog.datasource; import java.util.Map; import javax.sql.DataSource; import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /**
* 动态数据源父类
* @create ll
* @update
* @updateDate
*/
public abstract class AbstractDynamicDataSource<T extends DataSource> extends AbstractRoutingDataSource
implements
ApplicationContextAware { /** 日志 */
protected Logger logger = LoggerFactory.getLogger(getClass());
/** 默认的数据源KEY */
protected static final String DEFAULT_DATASOURCE_KEY = "defaultDataSource"; /** 数据源KEY-VALUE键值对 */
public Map<Object, Object> targetDataSources; /** spring容器上下文 */
private static ApplicationContext ctx; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
} public static ApplicationContext getApplicationContext() {
return ctx;
} public static Object getBean(String name) {
return ctx.getBean(name);
} /**
* @param targetDataSources the targetDataSources to set
*/
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
super.setTargetDataSources(targetDataSources);
// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
super.afterPropertiesSet();
} /**
* 创建数据源
* @param driverClassName 数据库驱动名称
* @param url 连接地址
* @param username 用户名
* @param password 密码
* @return 数据源{@link T}
* @Author : ll. create at 2017年3月27日 下午2:44:34
*/
public abstract T createDataSource(String driverClassName, String url, String username,
String password); /**
* 设置系统当前使用的数据源
* <p>数据源为空或者为0时,自动切换至默认数据源,即在配置文件中定义的默认数据源
* @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
*/
@Override
protected Object determineCurrentLookupKey() {
logger.info("【设置系统当前使用的数据源】");
Map<String, Object> configMap = DBContextHolder.getDBType();
logger.info("【当前数据源配置为:{}】", configMap);
if (MapUtils.isEmpty(configMap)) {
// 使用默认数据源
return DEFAULT_DATASOURCE_KEY;
}
// 判断数据源是否需要初始化
this.verifyAndInitDataSource();
logger.info("【切换至数据源:{}】", configMap);
return configMap.get(DBContextHolder.DATASOURCE_KEY);
} /**
* 判断数据源是否需要初始化
* @Author : ll. create at 2017年3月27日 下午3:57:43
*/
private void verifyAndInitDataSource() {
Map<String, Object> configMap = DBContextHolder.getDBType();
Object obj = this.targetDataSources.get(configMap.get(DBContextHolder.DATASOURCE_KEY));
if (obj != null) {
return;
}
logger.info("【初始化数据源】");
T datasource = this.createDataSource(configMap.get(DBContextHolder.DATASOURCE_DRIVER)
.toString(), configMap.get(DBContextHolder.DATASOURCE_URL).toString(),
configMap.get(DBContextHolder.DATASOURCE_USERNAME).toString(),
configMap.get(DBContextHolder.DATASOURCE_PASSWORD).toString());
this.addTargetDataSource(configMap.get(DBContextHolder.DATASOURCE_KEY).toString(),
datasource);
} /**
* 往数据源key-value键值对集合添加新的数据源
* @param key 新的数据源键
* @param dataSource 新的数据源
*/
private void addTargetDataSource(String key, T dataSource) {
this.targetDataSources.put(key, dataSource);
super.setTargetDataSources(this.targetDataSources);
// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
super.afterPropertiesSet();
} }
DBContextHolder.java
package com.blog.datasource; import java.util.HashMap;
import java.util.Map; public class DBContextHolder {
/** 数据源的KEY */
public static final String DATASOURCE_KEY = "DATASOURCE_KEY";
/** 数据源的URL */
public static final String DATASOURCE_URL = "DATASOURCE_URL";
/** 数据源的驱动 */
public static final String DATASOURCE_DRIVER = "DATASOURCE_DRIVER";
/** 数据源的用户名 */
public static final String DATASOURCE_USERNAME = "DATASOURCE_USERNAME";
/** 数据源的密码 */
public static final String DATASOURCE_PASSWORD = "DATASOURCE_PASSWORD"; private static final ThreadLocal<Map<String, Object>> contextHolder = new ThreadLocal<Map<String, Object>>(); public static void setDBType(Map<String, Object> dataSourceConfigMap) {
contextHolder.set(dataSourceConfigMap);
} public static Map<String, Object> getDBType() {
Map<String, Object> dataSourceConfigMap = contextHolder.get();
if (dataSourceConfigMap == null) {
dataSourceConfigMap = new HashMap<String, Object>();
}
return dataSourceConfigMap;
} public static void clearDBType() {
contextHolder.remove();
}
}
DruidDynamicDataSource.java
package com.blog.datasource; import java.sql.SQLException;
import java.util.List; import org.apache.commons.lang3.StringUtils; import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource; /**
* Druid数据源
* @update
* @updateDate
*/
public class DruidDynamicDataSource extends AbstractDynamicDataSource<DruidDataSource> { private boolean testWhileIdle = true;
private boolean testOnBorrow = false;
private boolean testOnReturn = false; // 是否打开连接泄露自动检测
private boolean removeAbandoned = false;
// 连接长时间没有使用,被认为发生泄露时长
private long removeAbandonedTimeoutMillis = 300 * 1000;
// 发生泄露时是否需要输出 log,建议在开启连接泄露检测时开启,方便排错
private boolean logAbandoned = false; // 只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true,使用oracle时可以设定此值。
// private int maxPoolPreparedStatementPerConnectionSize = -1; // 配置监控统计拦截的filters
private String filters; // 监控统计:"stat" 防SQL注入:"wall" 组合使用: "stat,wall"
private List<Filter> filterList; /*
* 创建数据源
* @see com.cdelabcare.pubservice.datasource.IDynamicDataSource#createDataSource(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public DruidDataSource createDataSource(String driverClassName, String url, String username,
String password) {
DruidDataSource parent = (DruidDataSource) super.getApplicationContext().getBean(
DEFAULT_DATASOURCE_KEY);
DruidDataSource ds = new DruidDataSource();
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setDriverClassName(driverClassName);
ds.setInitialSize(parent.getInitialSize());
ds.setMinIdle(parent.getMinIdle());
ds.setMaxActive(parent.getMaxActive());
ds.setMaxWait(parent.getMaxWait());
ds.setTimeBetweenConnectErrorMillis(parent.getTimeBetweenConnectErrorMillis());
ds.setTimeBetweenEvictionRunsMillis(parent.getTimeBetweenEvictionRunsMillis());
ds.setMinEvictableIdleTimeMillis(parent.getMinEvictableIdleTimeMillis()); ds.setValidationQuery(parent.getValidationQuery());
ds.setTestWhileIdle(testWhileIdle);
ds.setTestOnBorrow(testOnBorrow);
ds.setTestOnReturn(testOnReturn); ds.setRemoveAbandoned(removeAbandoned);
ds.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis);
ds.setLogAbandoned(logAbandoned); // 只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true,参照druid的源码
ds.setMaxPoolPreparedStatementPerConnectionSize(parent
.getMaxPoolPreparedStatementPerConnectionSize()); if (StringUtils.isNotBlank(filters))
try {
ds.setFilters(filters);
} catch (SQLException e) {
throw new RuntimeException(e);
} addFilterList(ds);
return ds;
} private void addFilterList(DruidDataSource ds) {
if (filterList != null) {
List<Filter> targetList = ds.getProxyFilters();
for (Filter add : filterList) {
boolean found = false;
for (Filter target : targetList) {
if (add.getClass().equals(target.getClass())) {
found = true;
break;
}
}
if (!found)
targetList.add(add);
}
}
}
}
二、修改配置文件
主要是修改spring-mybatis.xml
<!-- 配置数据源 -->
<bean name="defaultDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/> <!-- 初始化连接大小 -->
<property name="initialSize" value="0"/>
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20"/>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="20"/>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0"/>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000"/> <property name="validationQuery" value="${validationQuery}"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="testWhileIdle" value="true"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000"/> <!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true"/>
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800"/>
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true"/> <!-- 监控数据库 -->
<property name="filters" value="mergeStat"/>
</bean> <bean id="druidDynamicDataSource" class="com.blog.datasource.DruidDynamicDataSource">
<property name="defaultTargetDataSource" ref="defaultDataSource" />
<property name="targetDataSources">
<map>
<entry key="defaultDataSource" value-ref="defaultDataSource"/>
<!-- 这里还可以加多个dataSource -->
</map>
</property>
</bean> <!-- Spring整合Mybatis,更多查看文档:http://mp.baomidou.com -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="druidDynamicDataSource" />
<!-- 自动扫描Mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mybatis/system/*.xml"/>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<property name="typeAliasesPackage" value="com.blog.entity"/>
<property name="plugins">
<array>
<!-- 分页插件配置 -->
<bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">
</bean>
</array>
</property>
<!-- 全局配置注入 -->
<property name="globalConfig" ref="globalConfig" />
</bean> <!-- 配置事务管理 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDynamicDataSource"/>
</bean>
三、单元测试
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.blog.datasource.DBContextHolder;
import com.blog.entity.User;
import com.blog.mapper.PostDao;
import com.blog.service.UserService; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring.xml")
public class BlogTest { @Autowired
private UserService ud; @Test
public void testName() throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put(DBContextHolder.DATASOURCE_KEY, "localhost");
map.put(DBContextHolder.DATASOURCE_DRIVER, "com.mysql.jdbc.Driver");
map.put(DBContextHolder.DATASOURCE_URL,
"jdbc:mysql://127.0.0.1:3306/blog_test?useUnicode=true&characterEncoding=UTF-8");
map.put(DBContextHolder.DATASOURCE_USERNAME, "root");
map.put(DBContextHolder.DATASOURCE_PASSWORD, "1234");
DBContextHolder.setDBType(map); List<User> list = ud.selectList(null);
for (User user : list) {
System.out.println(user);
}
} }
测试后,控制台如图:
小结:
其实配置多数据源有很多方式,有aop,也有配置多个bean的方式,当然了,只要能达到目的就是王道,当然了,我也强调一点,不是实现完就不管了,背后的为什么比只要实现就好更重要。
其实,有一点我想说的是,有些时候遇到难题,最好的方式是迎面而上解决这个问题,而不是逃避或者独自焦躁。同时直面问题,也是解决焦躁的最好方式。这个我已经深有体会了。
另外补充到,上传至github上的多数据源示例同时也是ssm框架的搭建。有哪位朋友不会搭建框架,可以参考我的这个。希望能对你们有什么帮助。
SSM框架之多数据源配置的更多相关文章
- ssm框架使用详解&配置两个数据源
学习ssm框架已经快一年了,今天把这个框架总结一下. SSM 就是指 spring.SpringMVC和Mybatis.先说一下基本概念(百度上搜的) 1.基本概念 1.1.Spring Spring ...
- springmvc(二) ssm框架整合的各种配置
ssm:springmvc.spring.mybatis这三个框架的整合,有耐心一步步走. --WH 一.SSM框架整合 1.1.整合思路 从底层整合起,也就是先整合mybatis与spring,然后 ...
- 一步一步教你用IntelliJ IDEA 搭建SSM框架(2)——配置mybatis-geneator
我们要搭建整个SSM框架,所以要继续上篇文章没有完成的工作,下面配置mybatis-geneator,自动生成mybatis代码. 在上篇文章中的pom.xml的配置文件中已经加了mybatis-ge ...
- ssm框架搭建的基本配置(一站式教会你搭建)
首先是需要的jar包: <dependency> <groupId>org.springframework</groupId> <artifactId> ...
- mysql之整合ssm多数据源配置
一,基于SSM框架的多数据源配置 1.创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识 public class DynamicDataSourceHolder { ...
- SSM框架整合模板
SSM框架整合--MAVEN依赖 spring方面(包含了springmvc): spring-webmvc:spring与mvc的整合依赖,主要包括spring的核心包和springmvc需要的包 ...
- 详解intellij idea搭建SSM框架(spring+maven+mybatis+mysql+junit)(下)
在上一篇(详解intellij idea 搭建SSM框架(spring+maven+mybatis+mysql+junit)(上))博文中已经介绍了关于SSM框架的各种基础配置,(对于SSM配置不熟悉 ...
- SSM框架中数据库无法连接的问题
首先是SSM框架中所有的配置都是没有问题的,而且项目在其他人的环境上也能正常访问数据库:那么最有可能的就是数据库版本的问题导致数据库连接不上,服务器给我的报错是: 15:37:25.902 [C3P0 ...
- SSM框架中的注解,配置和控制器相关笔记
常规SSM实例 探索SSM理论的前提,应该是在对框架基础的运作方式有一定了解,以下是个人Android后台项目,用SSM框架快速搭建,以下是代码,主要 观察结构. 代码结构: model实体类 Ida ...
随机推荐
- 八、cent OS下tomcat启用APR并发模式
Tomcat支持三种接收请求的处理方式: BIO.NIO.APR ,本文记录tomcat配置APR模式,也是首选的模式.(Tomcat7 或以下,在 Linux 系统中默认使用BIO方式) 安装依赖库 ...
- Linux_vim文本编辑器指令整理
一般指令模式 : 可以移动光标,可以删除字符和删除整列,可以复制粘贴 编辑模式 : 按下"i, I, o, O, a, A, r, R"任意一个字母时进入;按下ESC退出编辑模式 ...
- maven(04)--一个简单的项目
简单介绍 一个maven项目,使用hibernate框架,实现向mysql数据库中添加和获取操作,其他操作也是类似 如果你没有hibernate,那么也不要紧,这里主要介绍如何在一个maven项目中引 ...
- ES6的新知识点
一.变量 原有变量: var的缺点: 1.可以重复声明 2.无法限制修改 3.没有块级作用域 新增变量: let :不能重复声明,变量-可以修改,块级作用域 const:不能重复声明,变量-不可以修改 ...
- My SQL查询语言
基础查询 一.语法select 查询列表from 表名;二.特点1.查询列表可以是字段.常量.表达式.函数,也可以是多个2.查询结果是一个虚拟表 三.示例1.查询单个字段select 字段名 from ...
- java unsupported major.minor version 51.0 解决
1.概述 出现如题所述异常 是因为jdk高版本 编译后的class文件 运行在低版本的jre环境下(如jdk7编译 运行在jdk6环境下) 2. 解决方案 在eclipse等ide中重新编译 指定编译 ...
- Hnoi2004 金属包裹
传送门 三维凸包模板题……只是听了听计算几何的课之后心血来潮想写的…… 我的做法很无脑是吧……暴力枚举三个点组成的三角形,然后枚举剩下的点,判断其余点是否都在这个三角形的同一侧,是的话则说明这个三角形 ...
- cnpm 安装
国内npm 安装比较慢,可选择cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org
- JavaScript中实现DI的原理(二)
JavaScript中实现DI的原理 在JavaScript中实现DI,看起来难,实际上原理很简单,它的核心技术是Function对象的toString().我们都知道,对一个函数对象执行toStri ...
- babel的使用以及安装配置
简介 babel是一个广泛使用的转码器,可以将ES6代码转化为ES5代码,从而在现有环境执行,这意味着,你可以现在就用ES6编写程序,而不用担心现有环境是否支持. 安装及配置 npm install ...