SpringMVC+Mybatis 多数据源配置
2018-03-28 14:18
525 查看
方案
最开始的做法
因为我们的项目用的是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类也值得好好研究一下。
总结
其实这个方案不仅仅可以用来处理不同数据源的问题,同时业务量上来之后需要把数据库进行主从分离或是把一个库分为多个库,都需要用到这样的做法。相关文章推荐
- Spring MVC+Mybatis 多数据源配置及发现的几个问题
- SpringMVC+Mybatis 多数据源配置
- SSM(Spring+SpringMVC+MyBatis)框架详细整合和多数据源配置
- Spring MVC +MyBatis 多数据源配置
- spring+springMVC+mybatis 多数据源配置
- Spring MVC+Mybatis 多数据源配置
- spring MVC+MyBatis 多数据源配置
- spring+springMVC+Mybatis下的logback日志
- SpringMVC+Spring+mybatis 实现登录过程(使用后台验证框架)
- SSM(Spring+SpringMVC+Mybatis) 整合
- Springmvc+mybatis+maven查询分页完美封装版
- spring mvc +Mybatis3.1 整合的时候异常
- SpringMVC + MyBatis + Mysql + Redis(作为二级缓存) 配置
- MyBatis+Spring+Spring MVC整合开发
- Spring+SpringMVC+MyBatis整合进阶篇(四)RESTful实战(前端代码修改)
- SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)【申明:来源于网络】
- springmvc+mybatis+sql server实现简单登录功能【转】
- springmvc+mybatis+mysql 根据生日计算年龄
- springmvc+spring+mybatis+jquery+js动态读取省市区数据
- SpringMVC + MyBatis + Mysql + Redis(作为二级缓存) 配置