导读-
在项目中可能有时候我们需要配置多套数据源,网上巴拉巴拉半天,都讲得太潦草了,对于第一次搞的人来说确实有点头疼,与其说在网上瞎找,还不如认真的自己动手操练一波,下面我就来介绍一下mybatis实现多套数据源的方式。
刚开始我也是和大家一样,利用mybatis的mybatis-generator-core-1.3.2从表生成xml文件,这里写个小插曲,照顾一下新手,(老司机可以绕道,可以跳过阅读),可以按照下面的方式进行
<generatorConfiguration>
<classPathEntry location="mysql-connector-java-5.1.25-bin.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
commentGenerator>
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1/test" userId="root" password="123456">
jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
javaTypeResolver>
<javaModelGenerator targetPackage="com.bufan.admin.commodity.entity" targetProject="src">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="com.bufan.admin.commodity.dao" targetProject="src">
<property name="enableSubPackages" value="true"/>
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.bufan.admin.commodity.dao" targetProject="src">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="tableName" domainObjectName="domainObjectName" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">table>
context>
generatorConfiguration>
通过完成配置文件的编写后,在mybatis-generator-core-1.3.2.jar同级目录,执行编译命令,就可以轻松完成又表生成实体类和xml的mapper文件啦。命令如下
java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
注意:若是原来有对应的xml文件,新生成的文件会追加到原来的mapper文件后面,尼玛,我就被坑了一次,后来查看居然有相同的方法在xml文件里面,项目启动报错。
相关包和文件请点击这里
言归正传,
首先,我们在项目中创建一个jdbc.properties文件,可以将全部的数据库地址写入到此配置文件中
jdbc.driver=com.mysql.jdbc.Driver
#服务器数据库
#jdbc.url=jdbc:mysql://67.209.190.39:3306/twjitm?useUnicode=true&characterEncoding=utf-8
#jdbc.username=root
#jdbc.password=123456
#本地数据库
#jdbc.url=jdbc:mysql://127.0.0.1:3306/twjitm?useUnicode=true&characterEncoding=utf-8
#本地服后台管理系统数据库地址
jdbc.url-admin=jdbc:mysql://127.0.0.1:3306/admin?useUnicode=true&characterEncoding=utf-8
jdbc.username-admin=root
jdbc.password-admin=123456
#商城网站地址
jdbc.url-shop=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8
jdbc.username-shop=root
jdbc.password-shop=123456
#正式服服务器地址
#最大连接数
c3p0.maxPoolSize=30000
#最小连接数
c3p0.minPoolSize=10
#关闭连接后不自动commit
c3p0.autoCommitOnClose=false
#获取连接超时时间
c3p0.checkoutTimeout=10000
#当获取连接失败重试次数
c3p0.acquireRetryAttempts=2
在进行配置
例如本例子中采用配置两套数据库来做测试,一个admin,一个shop两个数据源,在企业级开发中可能我们会对数据库采用很多分库分表策略,
其实原理都一样,
applicationContext.xml 中首先将properties文件加载进来,采用 context:property-placeholder 标签
可以同时加载多个这样的配置文件;下面就是我的项目中使用的,如有redis的配置文件,
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:property-placeholder
location="classpath:jdbc/jdbc.properties,classpath:redis/redis.properties"/>
beans>
这样,我们就能够在xml文件中使用properties中定义的数据了,然而,我觉得上面介绍的内容可能大家都觉得太普遍了,其实都是为了使得mybatis配置多套数据源做准备工作,同时,我们在maven项目中集成这一个功能,在spring配置文件applicationContext-mybatis.xml文件中
创建多套数据源地址,详细内容如下,
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.test.*.server" use-default-filters="false"/>
<bean id="admin" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url-admin}"/>
<property name="user" value="${jdbc.username-admin}"/>
<property name="password" value="${jdbc.password-admin}"/>
<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
<property name="minPoolSize" value="${c3p0.minPoolSize}"/>
<property name="autoCommitOnClose" value="${c3p0.autoCommitOnClose}"/>
<property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>
<property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}"/>
bean>
<bean id="shop" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url-shop}"/>
<property name="user" value="${jdbc.username-shop}"/>
<property name="password" value="${jdbc.password-shop}"/>
<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
<property name="minPoolSize" value="${c3p0.minPoolSize}"/>
<property name="autoCommitOnClose" value="${c3p0.autoCommitOnClose}"/>
<property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>
<property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}"/>
bean>
<bean id="dynamicDataSource" class="com.test.common.mybatis.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="admin" key="admin"/>
<entry value-ref="shop" key="shop"/>
map>
property>
<property name="defaultTargetDataSource" ref="admin">
property>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource"/>
<property name="typeAliasesPackage" value="com.bufan.admin.*.entity"/>
<property name="mapperLocations" value="classpath:mapper/*/*.xml"/>
<property name="configurationProperties">
<props>
<prop key="mapUnderscoreToCamelCase">trueprop>
props>
property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.test.admin.*.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
beans>
正如大家所看到的,添加了自定义一个类,一个是DynamicDataSource,其实DynamicDataSource,是继承了
spring的AbstractRoutingDataSource,充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。
在spring的官方文档上是这么介绍的
Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
calls to one of various target DataSources based on a lookup key. The latter is usually
(but not necessarily) determined through some thread-bound transaction context.
大概意思就是getConnection()根据查找lookup key键对不同目标数据源的调用,通常是通过(但不一定)某些线程绑定的事物上下文来实现。
通过这我们知道可以实现:
多数据源的动态切换,在程序运行时,把数据源数据源动态织入到程序中,灵活的进行数据源切换。 基于多数据源的动态切换,我们可以实现读写分离,这么做缺点也很明显,无法动态的增加数据源
DynamicDataSource.java 文件如下
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
System.out.println(CustomerContextHolder.getCustomerType());
return CustomerContextHolder.getCustomerType();
}
}
CustomerContextHolder.java文件内容输入,采用ThreadLocal模式来实现一个安全的线程访问,有关ThreadLocal类更多
内容,请点击这里
public class CustomerContextHolder {
public static final String DATASOURCE_ADMIN = "admin";
public static final String DATASOURCE_SHOP = "shop";
public static final ThreadLocal contextHolder = new ThreadLocal();
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
return contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
最后,比如要操作数据库的时候我们只需要在设置
CustomerContextHolder.setCustomerType(CustomerContextHolder.DATASOURCE_SHOP);
就代表要操作shop这个库了。
到此,我们就能实现mybatis动态的切换数据源。