当前位置:文档之家› Hibernate3 MyBatis3.2 配置多数据源

Hibernate3 MyBatis3.2 配置多数据源

Hibernate3   MyBatis3.2  配置多数据源
Hibernate3   MyBatis3.2  配置多数据源

在演示开始之前你的项目已经成功的整合完成的情况下才行,如果你还不知道怎么使用Spring 整合MyBatis和Spring整合Hibernate的话。建议参考之前的文章:MyBatis3整合Spring3、SpringMVC3、Struts2、Spring、Hibernate整合ExtJS这两篇文章结合起来就可以完成整合是几大框架了。这里重点介绍动态切换DataSource数据源的方法!

1、datasource的配置applicationContext-datasource.xml

xmlns:aop="https://www.doczj.com/doc/9812252288.html,/schema/aop"

xmlns:tx="https://www.doczj.com/doc/9812252288.html,/schema/tx"

xmlns:xsi="https://www.doczj.com/doc/9812252288.html,/2001/XMLSchema-instance"

xsi:schemaLocation="https://www.doczj.com/doc/9812252288.html,/schema/beans

https://www.doczj.com/doc/9812252288.html,/schema/beans/spring-beans-3.2.xsd

https://www.doczj.com/doc/9812252288.html,/schema/aop

https://www.doczj.com/doc/9812252288.html,/schema/aop/spring-aop-3.2.xsd

https://www.doczj.com/doc/9812252288.html,/schema/tx

https://www.doczj.com/doc/9812252288.html,/schema/tx/spring-tx-3.2.xsd ">

after_transaction

true

true

rollback-for="https://www.doczj.com/doc/9812252288.html,ng.Exception"/>

classpath:com/hoo/framework/mybatis/mybatis-common.xml

classpath:com/hoo/**/resultmap/*-resultmap.xml

classpath:com/hoo/**/mapper/*-mapper.xml

classpath:com/hoo/**/mapper/**/*-mapper.xml

上面分配配置了Oracle和MySQL数据源,MultipleDataSource为自定义的DataSource,它是继承AbstractRoutingDataSource实现抽象方法即可。

2、MultipleDataSource实现AbstractRoutingDataSource抽象数据源中方法,定义CustomerContextHolder来动态切换数据源。代码如下:

package com.hoo.framework.spring.support;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

* function: Spring 多数据源实现

* @author hoojo

* @createDate 2013-9-27 上午11:24:53

* @file MultipleDataSource.java

* @package com.hoo.framework.spring.support

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

public class MultipleDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return CustomerContextHolder.getCustomerType();

}

}

CustomerContextHolder

package com.hoo.framework.spring.support;

/**

* function: 多数据源

* @author hoojo

* @createDate 2013-9-27 上午11:36:57

* @file CustomerContextHolder.java

* @package com.hoo.framework.spring.support

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

public abstract class CustomerContextHolder {

public final static String DA TA_SOURCE_ORACLE = "oracleDataSource";

public final static String DA TA_SOURCE_MYSQL = "mySqlDataSource";

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

}

}

其中,常量对应的applicationContext-datasource.xml中的multipleDataSource中的targetDataSource 的key,这个很关键不要搞错了。

3、测试看能否切换数据源

package com.hoo.framework.service.impl;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.hoo.framework.dao.BaseDao;

import com.hoo.framework.log.ApplicationLogging;

import com.hoo.framework.spring.support.CustomerContextHolder;

/**

* function:多数据源测试服务接口测试

* @author hoojo

* @createDate 2013-10-10 上午11:18:18

* @file MultipleDataSourceServiceImplTest.java

* @package com.hoo.framework.service.impl

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

@ContextConfiguration({ "classpath:applicationContext-datasource.xml", "classpath:applicationContext-base.xml" })

@RunWith(SpringJUnit4ClassRunner.class)

public class MultipleDataSourceServiceImplTest extends ApplicationLogging {

@Autowired

private BaseDao dao;

@Test

public void testDao() {

info(dao.toString());

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString()); CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

info(dao.findBySql("select * from city limit 2").toString());

}

}

运行上面的测试用例后可以发现能查询到数据,如果我们注释掉其中的一项setCustomerType就会出现查询错误。在其中一个数据库没有找到对应的table。

至此,切换数据源也算成功了大半,剩下的就是如何在实际的业务中完成数据源的“动态”切换呢?!难道还是要像上面一样在每个方法上面写一个setCustomerType来手动控制数据源!答案是“当然不是”。我们用过Spring、hibernate后就会知道,先去使用hibernate的时候没有用spring,事务都是手动控制的。自从用了Spring大家都轻松了很多,事务交给了Spring来完成。所以到了这里你大概知道怎么做了,如果你还不知道~嘿嘿……(Spring那你就懂了个皮毛,最经典的部分你没有学到)

所以就是利用Spring的Aop进行切面编程,拦截器Interceptor在这里是一个很好的选择。它可以在方法之前或方法之后完成一些操作,而且控制的粒度可以细到具体的方法中的参数、返回值、方法名等。在这里控制数据源动态切换最好不过了!

4、上面是手动切换数据源,我们用Spring的Aop拦截器整个更自动化的方法来切换数据源。Spring配置文件applicationContext-base.xml

xmlns:xsi="https://www.doczj.com/doc/9812252288.html,/2001/XMLSchema-instance"

xmlns:aop="https://www.doczj.com/doc/9812252288.html,/schema/aop"

xmlns:util="https://www.doczj.com/doc/9812252288.html,/schema/util"

xmlns:context="https://www.doczj.com/doc/9812252288.html,/schema/context"

xsi:schemaLocation="https://www.doczj.com/doc/9812252288.html,/schema/beans

https://www.doczj.com/doc/9812252288.html,/schema/beans/spring-beans-3.2.xsd

https://www.doczj.com/doc/9812252288.html,/schema/util

https://www.doczj.com/doc/9812252288.html,/schema/util/spring-util-3.2.xsd

https://www.doczj.com/doc/9812252288.html,/schema/aop

https://www.doczj.com/doc/9812252288.html,/schema/aop/spring-aop-3.2.xsd

https://www.doczj.com/doc/9812252288.html,/schema/context

https://www.doczj.com/doc/9812252288.html,/schema/context/spring-context-3.2.xsd">

com.hoo.*.service.impl.*Service*\.*.*

com.hoo.*.mapper.*Mapper*\.*.*

上面的拦截器是拦截Service和Mapper的Java对象中的执行方法,所有在service.impl包下的ServiceImpl和mapper包下的Mapper接口将会被DataSourceMethodInterceptor拦截到,并通过其中的规律动态设置数据源。

3、拦截器DataSourceMethodInterceptor.java拦截具体的业务,并通过业务代码中的方法和接口、实现类的规律进行动态设置数据源

package com.hoo.framework.spring.interceptor;

import https://www.doczj.com/doc/9812252288.html,ng.reflect.Proxy;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import https://www.doczj.com/doc/9812252288.html,ng.ClassUtils;

import org.springframework.beans.factory.InitializingBean;

import com.hoo.framework.log.ApplicationLogging;

import com.hoo.framework.spring.support.CustomerContextHolder;

/**

* function: 动态设置数据源拦截器

* @author hoojo

* @createDate 2013-9-27 下午02:00:13

* @file DataSourceMethodInterceptor.java

* @package com.hoo.framework.spring.interceptor

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

public class DataSourceMethodInterceptor extends ApplicationLogging implements MethodInterceptor, InitializingBean {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

Class clazz = invocation.getThis().getClass();

String className = clazz.getName();

if (ClassUtils.isAssignable(clazz, Proxy.class)) {

className = invocation.getMethod().getDeclaringClass().getName();

}

String methodName = invocation.getMethod().getName();

Object[] arguments = invocation.getArguments();

trace("execute {}.{}({})", className, methodName, arguments);

if (className.contains("MySQL")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DA TA_SOURCE_MYSQL);

} else if (className.contains("Oracle")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else if (methodName.contains("MySQL")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DA TA_SOURCE_MYSQL);

} else if (methodName.contains("Oracle")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else {

CustomerContextHolder.clearCustomerType();

}

/*

if (className.contains("MySQL") || methodName.contains("MySQL")) { CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

} else if (className.contains("Oracle") || methodName.contains("Oracle")) { CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else {

CustomerContextHolder.clearCustomerType();

}

*/

Object result = invocation.proceed();

return result;

}

@Override

public void afterPropertiesSet() throws Exception {

log.trace("afterPropertiesSet……");

}

}

上面的代码是在接口或实现中如果出现MySQL就设置数据源为DATA_SOURCE_MYSQL,如果

有Oracle就切换成DATA_SOURCE_ORACLE数据源。

4、编写实际的业务接口和实现来测试拦截器是否有效

MultipleDataSourceService 接口

package com.hoo.server.datasource.service;

/**

* function: 多数据源测试服务接口

* @author hoojo

* @createDate 2013-10-10 上午11:07:31

* @file MultipleDataSourceService.java

* @package com.hoo.server.datasource.service

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

public interface MultipleDataSourceService {

public void execute4MySQL() throws Exception;

public void execute4Oracle() throws Exception;

}

接口实现

package com.hoo.server.datasource.service.impl;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.hoo.framework.dao.BaseDao;

import com.hoo.framework.service.impl.AbstractService;

import com.hoo.server.datasource.service.MultipleDataSourceService;

/**

* function: 多数据源测试服务接口实现

* @author hoojo

* @createDate 2013-10-10 上午11:09:54

* @file MultipleDataSourceServiceImpl.java

* @package com.hoo.server.datasource.service.impl

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

@Service

public class MultipleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

@Autowired

private BaseDao dao;

@Override

public void execute4MySQL() throws Exception {

info(dao.findBySql("select * from city limit 2").toString());

}

@Override

public void execute4Oracle() throws Exception {

info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

}

}

测试上面的服务层代码,看看能否利用拦截器实现数据源动态切换

在上面的MultipleDataSourceServiceImplTest中加入如下代码

@Autowired

@Qualifier("multipleDataSourceServiceImpl")

private MultipleDataSourceService service;

@Test

public void testService() {

try {

service.execute4MySQL();

service.execute4Oracle();

} catch (Exception e) {

e.printStackTrace();

}

}

运行上面的代码后可以看到能够成功查询到结果

5、测试实现类带Oracle或MySQL字符串的

package com.hoo.server.datasource.service.impl;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.hoo.framework.dao.BaseDao;

import com.hoo.framework.service.impl.AbstractService;

import com.hoo.server.datasource.service.MultipleDataSourceService;

/**

* function: 多数据源测试服务接口实现

* @author hoojo

* @createDate 2013-10-10 上午11:09:54

* @file MultipleDataSourceServiceImpl.java

* @package com.hoo.server.datasource.service.impl

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

@Service

public class MySQLDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

@Autowired

private BaseDao dao;

@Override

public void execute4MySQL() throws Exception {

info(dao.findBySql("select * from city limit 2").toString());

}

@Override

public void execute4Oracle() throws Exception {

info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

}

}

package com.hoo.server.datasource.service.impl;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.hoo.framework.dao.BaseDao;

import com.hoo.framework.service.impl.AbstractService;

import com.hoo.server.datasource.service.MultipleDataSourceService;

/**

* function: 多数据源测试服务接口实现

* @author hoojo

* @createDate 2013-10-10 上午11:09:54

* @file MultipleDataSourceServiceImpl.java

* @package com.hoo.server.datasource.service.impl

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

@Service

public class OracleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

@Autowired

private BaseDao dao;

@Override

public void execute4MySQL() throws Exception {

info(dao.findBySql("select * from city limit 2").toString());

}

@Override

public void execute4Oracle() throws Exception {

info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

}

}

这里的两个实现类的类名都含有不同规则的数据源标识符字符串,而且方法名也含有相关字符串,这些都匹配拦截器中的规则。

在MultipleDataSourceServiceImplTest 中加入测试代码

@Autowired

@Qualifier("oracleDataSourceServiceImpl")

private MultipleDataSourceService oracleService;

@Autowired

@Qualifier("mySQLDataSourceServiceImpl")

private MultipleDataSourceService mySQLService;

@Test

public void testOracleService() {

try {

oracleService.execute4MySQL();

} catch (Exception e1) {

e1.printStackTrace();

}

try {

oracleService.execute4Oracle();

} catch (Exception e) {

e.printStackTrace();

}

}

@Test

public void testMySQLService() {

try {

mySQLService.execute4MySQL();

} catch (Exception e1) {

e1.printStackTrace();

}

try {

mySQLService.execute4Oracle();

} catch (Exception e) {

e.printStackTrace();

}

}

执行上面的测试用例会发现有一个查询会失败,那是因为我们按照拦截器中的业务规则切换数据源就匹配到了其中一个,就是通过类名进行数据源切换,所以只定位到其中一个数据源。6、测试MyBatis的数据源切换方法

MyBatis的查询接口

package com.hoo.server.datasource.mapper;

import java.util.List;

import java.util.Map;

import com.hoo.framework.mybatis.SqlMapper;

/**

* function: MyBatis 多数据源测试查询接口

* @author hoojo

* @createDate 2013-10-10 下午04:18:08

* @file MultipleDataSourceMapper.java

* @package com.hoo.server.datasource.mapper

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

public interface MultipleDataSourceMapper extends SqlMapper {

public List> execute4MySQL() throws Exception;

public List> execute4Oracle() throws Exception;

}

multiple-datasource-mapper.xml

测试MyBatis的mapper查询接口,在MultipleDataSourceServiceImplTest加入以下代码

@Autowired

private MultipleDataSourceMapper mapper;

@Test

public void testMapper() {

try {

trace(mapper.execute4MySQL());

} catch (Exception e1) {

e1.printStackTrace();

}

try {

trace(mapper.execute4Oracle());

} catch (Exception e) {

e.printStackTrace();

}

}

运行以上测试代码也能发现可以正常的查询到Oracle和MySQL数据库中的数据。MyBatis的在这里只负责查询,而增删改是hibernate完成的任务,所以这里也就不再测试modified部分。

7、上面的拦截器是需要在配置文件中进行配置的,这里利用annotation的配置的拦截器进行业务拦截,也许有些人更喜欢用annotation

package com.hoo.framework.spring.interceptor;

import https://www.doczj.com/doc/9812252288.html,ng.reflect.Proxy;

import https://www.doczj.com/doc/9812252288.html,ng.ClassUtils;

import https://www.doczj.com/doc/9812252288.html,ng.JoinPoint;

import https://www.doczj.com/doc/9812252288.html,ng.annotation.Aspect;

import https://www.doczj.com/doc/9812252288.html,ng.annotation.Before;

import https://www.doczj.com/doc/9812252288.html,ponent;

import com.hoo.framework.log.ApplicationLogging;

import com.hoo.framework.spring.support.CustomerContextHolder;

/**

* function: 多数据源动态配置拦截器

* @author hoojo

* @createDate 2013-10-10 上午11:35:54

* @file MultipleDataSourceInterceptor.java

* @package com.hoo.framework.spring.interceptor

* @project SHMB

* @blog https://www.doczj.com/doc/9812252288.html,/IBM_hoojo

* @email hoojo_@https://www.doczj.com/doc/9812252288.html,

* @version 1.0

*/

@Component

@Aspect

public class MultipleDataSourceInterceptor extends ApplicationLogging {

/**

* function: 动态设置数据源

* @author hoojo

* @createDate 2013-10-10 上午11:38:45

* @throws Exception

*/

@Before("execution(* com.hoo..service.impl.*ServiceImpl.*(..)) || execution(* com.hoo..mapper.*Mapper.*(..))")

public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {

Class clazz = joinPoint.getTarget().getClass();

String className = clazz.getName();

if (ClassUtils.isAssignable(clazz, Proxy.class)) {

className = joinPoint.getSignature().getDeclaringTypeName();

}

String methodName = joinPoint.getSignature().getName();

Object[] arguments = joinPoint.getArgs();

trace("execute {}.{}({})", className, methodName, arguments);

if (className.contains("MySQL")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DA TA_SOURCE_MYSQL);

} else if (className.contains("Oracle")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else if (methodName.contains("MySQL")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

} else if (methodName.contains("Oracle")) {

CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else {

CustomerContextHolder.clearCustomerType();

}

/*

if (className.contains("MySQL") || methodName.contains("MySQL")) { CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

} else if (className.contains("Oracle") || methodName.contains("Oracle")) { CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

} else {

CustomerContextHolder.clearCustomerType();

}

*/

}

}

这种拦截器就是不需要在配置文件中加入任何配置进行拦截,算是一种扩展的方法。

相关主题
文本预览
相关文档 最新文档