当前位置:首页 > Java技术 > 【Java】一次SpringMVC+ Mybatis 配置多数据源经历

【Java】一次SpringMVC+ Mybatis 配置多数据源经历

2022年09月17日 15:12:15Java技术8

需求

现在在维护的是学校的一款信息服务APP的后台,最近要开发一些新功能,其中一个就是加入学校电影院的在线购票。在线购票实际上已经有一套系统了,但是是外包给别人开发的,我们拿不到代码只能拿到数据库,并且也不一定能很好的兼容之前的代码,所以需要基于这个数据库来进行新的开发。

现在用的后台是SpringMVC+Mybatis+MySQL开发的,购票用的是SQL Server 2008(好古老的东西了),因为要用一套用户体系所以不可能再去单独为了这个功能弄一个系统出来,因此要在原项目中兼容这个数据库。

问题

如何在一个web项目中使用两个数据源,并且不同的接口可以按需选择数据库。

方案

最开始的做法

因为我们的项目用的是Mybatis作为ORM框架,在其配置文件中可以配置数据源信息,原始配置如下:

spring-mybatis.xml

<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
	class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:jdbc.properties" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${jdbc.driver}" />
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
	<!-- 初始化连接大小 -->
	<property name="initialSize" value="${jdbc.initialSize}"></property>
	<!-- 连接池最大数量 -->
	<property name="maxActive" value="${jdbc.maxActive}"></property>
	<!-- 连接池最大空闲 -->
	<property name="maxIdle" value="${jdbc.maxIdle}"></property>
	<!-- 连接池最小空闲 -->
	<property name="minIdle" value="${jdbc.minIdle}"></property>
	<!-- 获取连接最大等待时间 -->
	<property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>

<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<!-- 自动扫描mapping.xml文件 -->
	<property name="mapperLocations" value="classpath:path/to/mapping/*.xml"></property>
</bean>

<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="path.to.dao" />
	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>

<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>

然后我就天真的认为是不是再新建一个dataSource的bean、sqlSessionFactory、mapperScannerConfigurer和transactionManager,把数据库连接信息改一下,就可以同时使用两个数据库了。但是尝试之后发现第二个数据库的mapping文件根本没有被初始化进spring的context中,报了Invalid bound statement (not found)这个错,查了一下说是配置文件不对等原因造成的。后来发现实际上因为上面的配置文件中的sqlSessionFactory在spring中是单例的,因此按照我的想法第二个sqlSessionFactory根本就不会被实例化。所以此方法行不通!

改进做法

最后是在这篇博客中找到了正确可行的解决方法:使用Spring提供的AbstractRoutingDataSource类来根据请求路由到不同的数据源。具体做法是
先设置两个不同的dataSource代表不同的数据源,再建一个总的dynamicDataSource,根据不同的请求去设置dynamicDataSource。代码如下:

配置文件spring-mybatis.xml

<!--统一的dataSource-->
<bean id="dynamicDataSource" class="path.to.DynamicDataSource" >
    <property name="targetDataSources">
        <map key-type="java.lang.String">
			<!--通过不同的key决定用哪个dataSource-->
            <entry value-ref="dataSource" key="dataSource"></entry>
            <entry value-ref="mssqlDataSource" key="mssqlDataSource"></entry>
        </map>
    </property>
	<!--设置默认的dataSource-->
    <property name="defaultTargetDataSource" ref="dataSource">
    </property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${jdbc.driver}" />
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
	<!-- 初始化连接大小 -->
	<property name="initialSize" value="${jdbc.initialSize}"></property>
	<!-- 连接池最大数量 -->
	<property name="maxActive" value="${jdbc.maxActive}"></property>
	<!-- 连接池最大空闲 -->
	<property name="maxIdle" value="${jdbc.maxIdle}"></property>
	<!-- 连接池最小空闲 -->
	<property name="minIdle" value="${jdbc.minIdle}"></property>
	<!-- 获取连接最大等待时间 -->
	<property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>

<!--电影票数据库是mssql2008,单独的数据库,配置如下-->
<bean id="mssqlDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc-mssql.driver}" />
    <property name="url" value="${jdbc-mssql.url}" />
    <property name="username" value="${jdbc-mssql.username}" />
    <property name="password" value="${jdbc-mssql.password}" />
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${jdbc.initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${jdbc.maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${jdbc.maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${jdbc.minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>

<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dynamicDataSource" />
	<!-- 自动扫描mapping.xml文件 -->
	<property name="mapperLocations" value="classpath:path/to/mapping/*.xml"></property>
</bean>

<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="path.to.dao" />
	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>

<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dynamicDataSource" />
</bean>

DynamicDataSource.java

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return CustomerContextHolder.getCustomerType();
    }
}

CustomerContextHolder.java

public class CustomerContextHolder {
    public static final String DATA_SOURCE_MYSQL = "dataSource";
    public static final String DATA_SOURCE_MSSQL = "mssqlDataSource";
	//用ThreadLocal来设置当前线程使用哪个dataSource
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static void setCustomerType(String customerType) {
        contextHolder.set(customerType);
    }
    public static String getCustomerType() {
        String dataSource = contextHolder.get();
        if (StringUtils.isEmpty(dataSource)) {
            return DATA_SOURCE_MYSQL;
        }else {
            return dataSource;
        }
    }
    public static void clearCustomerType() {
        contextHolder.remove();
    }
}

ServiceImpl.java

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MSSQL);

值得注意的是在CustomerContextHolder.java中使用了ThreadLocal类的set方法来设置当前线程要选择的dataSource,看一下set方法的源码:

ThreadLocal.set()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

显而易见,获取当前线程,并且使用一个hashmap把需要存储的值设置进去。因为tomcat是用的线程池来处理每个请求,所以用ThreadLocal可以保证线程安全问题。同时这个AbstractRoutingDataSource类也值得好好研究一下。

总结

其实这个方案不仅仅可以用来处理不同数据源的问题,同时业务量上来之后需要把数据库进行主从分离或是把一个库分为多个库,都需要用到这样的做法。这次暴露的问题确实也了解了不少,继续学习吧!

作者:puyangsky
来源链接:https://www.cnblogs.com/puyangsky/p/6133553.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/42600.html

标签: MyBatis
分享给朋友:

“【Java】一次SpringMVC+ Mybatis 配置多数据源经历” 的相关文章

MyBatis配置的pom文件

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/200...

mybatis实战之拦截器

mybatis实战之拦截器 在服务的开发过程中,往往存在这样的需求,针对业务,实现对数据库操作语句做统一的处理。比如对某些敏感数据如用户姓名、手机号等坐脱敏处理保存和查询、对未实现权限的查询通过添加关联查询实现权限控制查询结果等等。 这时,myba...

mybatis:在springboot中的配置

## Mybatis 配置 mybatis.type-aliases-package=com.xfind.core.entity.xianyu mybatis.mapper-locations=classpath:mapper/*.xml #使全局的映射器启用或禁用缓存。 mybati...

SSM:spring+springmvc+mybatis框架中的XML配置文件功能详细解释(转)

原文:https://blog.csdn.net/yijiemamin/article/details/51156189# 这几天一直在整合SSM框架,虽然网上有很多已经整合好的,但是对于里面的配置文件并没有进行过多的说明,很多人知其然不知其所以然,经过几天的搜索和整理,今天总算...

Mybatis分页插件PageHelper的配置和使用方法

Mybatis分页插件PageHelper的配置和使用方法

http://www.cnblogs.com/kangoroo/p/7998433.html   前言 在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。 前端...

Mybatis Generator最完整配置详解

Mybatis Generator最完整配置详解

背景 通过mybatis插件自动生成代码 MyBatis 介绍 MyBatis 是一款标准的 ORM 框架,被广泛的应用于各企业开发中。MyBatis 最早是 Apache 的一个开源项目 iBatis,2010 年这个项目由 Apache Software F...

Mybatis使用xml方式配置

Mybatis使用xml方式配置

官网地址:https://mybatis.org/mybatis-3/zh/getting-started.html 使用案例: 我们创建一个数据库叫做mybatis,在其下创建userinfo数据表,然后创建相应的字段,对应的数据库字段如下: 然后,...

Mybatis Generator配置详解

参考:http://www.jianshu.com/p/e09d2370b796          http://mbg.cndocs.tk <?xml version="1.0" encoding="UTF...

Mybatis配置

Mybatis配置

Mybatis配置文件并不复杂,它的所有元素如下: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Confi...

MyBatis配置下划线转驼峰

myBatis配置实现数据库字段下划线映射到java对象的驼峰式命名属性。 要实现上面的配置需要在mybatis-config.xml配置文件中添加如下设置: <?xml version="1.0" encoding="UTF-8" ?>...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。