ssm框架 mysql 一对多_SSM框架之多数据源配置

多数据源的应用场景:主要是数据库拆分后,怎样让多个数据库结合起来来达到业务需求。

SSM框架(Spring+SpringMVC+MyBatis(MyBatis-Plus))是目前最常用的,此次仍然是maven工程。

关于这个多数据源例子,我已经上传到我的github上,地址为:https://github.com/youcong1996/study_simple_demo.git

不过需要注意的是,分支为demo1,不是主分支,如图所示:

8d54cb364d14303feec9c938bb704ca2.png

如果下面的示例,你们看不懂或者不能理解,可以git clone我的地址

在编程的世界里,简洁即完美。

如何实现多数据源?

一句话,三个类加xml配置即可达到这个目的。

一、编写三个类

AbstractDynamicDataSource.java

packagecom.blog.datasource;importjava.util.Map;importjavax.sql.DataSource;importorg.apache.commons.collections.MapUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.BeansException;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 动态数据源父类

* @create ll

* @update

* @updateDate*/

public abstract class AbstractDynamicDataSource extendsAbstractRoutingDataSourceimplementsApplicationContextAware {/**日志*/

protected Logger logger =LoggerFactory.getLogger(getClass());/**默认的数据源KEY*/

protected static final String DEFAULT_DATASOURCE_KEY = "defaultDataSource";/**数据源KEY-VALUE键值对*/

public MaptargetDataSources;/**spring容器上下文*/

private staticApplicationContext ctx;public void setApplicationContext(ApplicationContext applicationContext) throwsBeansException {

ctx=applicationContext;

}public staticApplicationContext getApplicationContext() {returnctx;

}public staticObject getBean(String name) {returnctx.getBean(name);

}/***@paramtargetDataSources the targetDataSources to set*/

public void setTargetDataSources(MaptargetDataSources) {this.targetDataSources =targetDataSources;super.setTargetDataSources(targetDataSources);//afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的

super.afterPropertiesSet();

}/*** 创建数据源

*@paramdriverClassName 数据库驱动名称

*@paramurl 连接地址

*@paramusername 用户名

*@parampassword 密码

*@return数据源{@linkT}

* @Author : ll. create at 2017年3月27日 下午2:44:34*/

public abstractT createDataSource(String driverClassName, String url, String username,

String password);/*** 设置系统当前使用的数据源

*

数据源为空或者为0时,自动切换至默认数据源,即在配置文件中定义的默认数据源

*@seeorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()*/@OverrideprotectedObject determineCurrentLookupKey() {

logger.info("【设置系统当前使用的数据源】");

Map configMap =DBContextHolder.getDBType();

logger.info("【当前数据源配置为:{}】", configMap);if(MapUtils.isEmpty(configMap)) {//使用默认数据源

returnDEFAULT_DATASOURCE_KEY;

}//判断数据源是否需要初始化

this.verifyAndInitDataSource();

logger.info("【切换至数据源:{}】", configMap);returnconfigMap.get(DBContextHolder.DATASOURCE_KEY);

}/*** 判断数据源是否需要初始化

* @Author : ll. create at 2017年3月27日 下午3:57:43*/

private voidverifyAndInitDataSource() {

Map 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键值对集合添加新的数据源

*@paramkey 新的数据源键

*@paramdataSource 新的数据源*/

private voidaddTargetDataSource(String key, T dataSource) {this.targetDataSources.put(key, dataSource);super.setTargetDataSources(this.targetDataSources);//afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的

super.afterPropertiesSet();

}

}

DBContextHolder.java

packagecom.blog.datasource;importjava.util.HashMap;importjava.util.Map;public classDBContextHolder {/**数据源的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> contextHolder = new ThreadLocal>();public static void setDBType(MapdataSourceConfigMap) {

contextHolder.set(dataSourceConfigMap);

}public static MapgetDBType() {

Map dataSourceConfigMap =contextHolder.get();if (dataSourceConfigMap == null) {

dataSourceConfigMap= new HashMap();

}returndataSourceConfigMap;

}public static voidclearDBType() {

contextHolder.remove();

}

}

DruidDynamicDataSource.java

packagecom.blog.datasource;importjava.sql.SQLException;importjava.util.List;importorg.apache.commons.lang3.StringUtils;importcom.alibaba.druid.filter.Filter;importcom.alibaba.druid.pool.DruidDataSource;/*** Druid数据源

* @update

* @updateDate*/

public class DruidDynamicDataSource extends AbstractDynamicDataSource{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 ListfilterList;/** 创建数据源

* @see com.cdelabcare.pubservice.datasource.IDynamicDataSource#createDataSource(java.lang.String, java.lang.String, java.lang.String, java.lang.String)*/@OverridepublicDruidDataSource createDataSource(String driverClassName, String url, String username,

String password) {

DruidDataSource parent= (DruidDataSource) super.getApplicationContext().getBean(

DEFAULT_DATASOURCE_KEY);

DruidDataSource ds= newDruidDataSource();

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 newRuntimeException(e);

}

addFilterList(ds);returnds;

}private voidaddFilterList(DruidDataSource ds) {if (filterList != null) {

List 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

三、单元测试

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 map = new HashMap();

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);

Listlist = ud.selectList(null);

for (User user : list) {

System.out.println(user);

}

}

}

测试后,控制台如图:

7d6d336e48563f3d7321c69192220262.png

小结:

其实配置多数据源有很多方式,有aop,也有配置多个bean的方式,当然了,只要能达到目的就是王道,当然了,我也强调一点,不是实现完就不管了,背后的为什么比只要实现就好更重要。

其实,有一点我想说的是,有些时候遇到难题,最好的方式是迎面而上解决这个问题,而不是逃避或者独自焦躁。同时直面问题,也是解决焦躁的最好方式。这个我已经深有体会了。

另外补充到,上传至github上的多数据源示例同时也是ssm框架的搭建。有哪位朋友不会搭建框架,可以参考我的这个。希望能对你们有什么帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值