Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法(四)
L就设置数据源为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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public interface MultipleDataSourceMapper extends SqlMapper {
public List
public List
}
multiple-datasource-mapper.xml
< xml version="1.0" encoding="UTF-8" >
SELECT
*
FROM
deviceInfo_tab t where rownum < 10
]]>
SELECT
*
FROM
city limit 2
]]>
测试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 java.lang.reflect.Proxy;
import org.apache.commons.lang.ClassUtils;
import org.
aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
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 http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @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.DATA_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();
}
*/
}
}
这种拦截器就是不需要在配置文件中加入任何配置进行拦截,算是一种扩展的方法。
三、总结
多数据源动态切换的主要地方在于我们要定义一个自己的数据源来实现AbstractRoutingDataSource中的determineCurrentLookupKey方法,然后通过CustomerContextHolder来实现数据源的切换工作。而数据源的动态切换也就在于我们利用了Spring的Aop中的拦截器Interceptor进行业务类的方法进行拦截,通过类名或方法名中的有效字符串来动态切换到我们定义好的规则对应的数据源。