其实一个工程多数据源比较常见。
比如我们将所有工程中使用到的定时任务都持久化到同一个数据库,或者我们为工作流框架单独提供一个数据源。
其实这些都好说,毕竟这些情况都是框架替我们做一些工作,我们几乎没有必要去访问这些数据;或者框架已经提供了方便的API去访问持久化数据,我们不用去考虑这方面的事情。
比较烦的情况是,一个工程必须有多个数据源,而且我们经常通过API访问。
无论是使用ORM框架还是JDBC或者SQLTemplate,对于多个数据源尽量保证相同的访问方式,毕竟这些代码到处都是,开发时谁都不想考虑对于哪个数据源应该使用哪种访问方式。
正好我本地的工程是用的MyBatis,连接池使用DBCP,那就以这些为例,记录一下多数据源的配置。
(对于Mybatis本身的配置就直接无视了,不同的org.mybatis.spring.SqlSessionFactoryBean对象可以使用各自的Mybatis配置也可以共享同一个配置,看自己的需要了。)
先从配置数据源开始,鉴于我用的是DBCP...算了,随便配置一个吧,具体参数看http://commons.apache.org/proper/commons-dbcp/configuration.html。
下面是简单配置:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/oasys?useUnicode=true&characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="" /> <property name="initialSize" value="2" /> <property name="maxActive" value="10" /> <property name="maxWait" value="-1" /> <property name="removeAbandonedTimeout" value="60" /> <property name="removeAbandoned" value="true" /> <property name="defaultAutoCommit" value="true" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <property name="numTestsPerEvictionRun" value="10" /> <property name="timeBetweenEvictionRunsMillis" value="900000" /> <property name="poolPreparedStatements" value="true" /> <property name="maxOpenPreparedStatements" value="50" /> </bean>
接下来再弄个Oracle的,太麻烦了,主要是bean id和一部分属性不同,剩下的我们不需要关心:
<bean id="oracleDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@//localhost:1521/orcl" /> <property name="username" value="alvez" /> <property name="password" value="123" /> <!-- 略 --> </bean>
两种数据源则两种会话,我们需要两个SqlSessionFactory,即:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="oracleDataSource" /> <property name="configLocation" value="classpath:myBatis/myBatis-Configuration.xml" /> </bean> <bean id="_sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="myDataSource" /> <property name="configLocation" value="classpath:myBatis/myBatis-Configuration1.xml" /> </bean>
接下来就是DAO Bean了,可能我会使用如下这种方式:
<bean id="mybatisDao" class="pac.alvez.dao.MybatisDao"> <property name="sqlSessionFactory" ref="SqlSessionFactory1"></property> </bean>
但这样几乎没什么实际意义,工程中的DAO Bean可能会很多,而且会继续扩展下去。
于是,通常我们会这样用,顺便带上另一个数据源:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="pac.alvez.dao.oracle" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="pac.alvez.dao.mysql" /> <property name="sqlSessionFactoryBeanName" value="_sqlSessionFactory" /> </bean>
可能有些人希望不同数据源的DAO Bean存在于相同的package下,甚至不同数据源的SQL定义在相同的DAO Bean中。
我不知道应该如何实现这种效果,而且....用起来难道不混乱吗?
剩下的就是transaction了,几乎没什么特别的:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="oracleDataSource" /> </bean> <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="myDataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <tx:annotation-driven transaction-manager="transactionManager1" />
使用的时候需要注意,由于使用的是自动检测DAO Bean的方式,即便不同数据源的DAO放在不同的package中也不能同名。
否则:
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'TestMapper' for bean class [pac.alvez.dao.mysql.TestMapper] conflicts with existing, non-compatible bean definition of same name and class [org.mybatis.spring.mapper.MapperFactoryBean