在大多数情况下,一个功能全面的系统只使用单一数据源几乎是不太可能的,所以配置多数据源是十分必要的,记录一下去年某个项目配置多数据源的方式~
1. 配置datasource
2. 配置sessionFactory,属性为上一步配置的datasource和mapperLocation(xml文件所在的目录)
要实现多数据源切换,重点在于扩展AbstractRoutingDataSource抽象类,这个类是Spring2.0之后增加的。
这个类相当于数据源DataSourcer的路由中介,可以实现在项目运行时根据设置的key值切换到对应的数据源DataSource上。
实现过程如下:
1. 编写枚举类,表示数据源的key
public enum DataSources {
SYS, API, APP, APWH, BICD
}
2. 编写线程安全的数据源切换类DataSourceTypeManager
public class DataSourceTypeManager {
// ThreadLocal类是实现线程安全的关键,因为数据操作大部分都是并发执行,所以必须要考虑线程安全
private static final ThreadLocal dataSourceTypes = new ThreadLocal() {
@Override
protected DataSources initialValue() {
return DataSources.SYS;
}
};
public static DataSources get() {
return dataSourceTypes.get();
}
public static void set(DataSources dataSourceType) {
dataSourceTypes.set(dataSourceType);
}
public static void reset() {
dataSourceTypes.set(DataSources.SYS);
}
}
3. 扩展类AbstractRoutingDataSource
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
// 关键,重写获取数据源key的方法
@Override
protected Object determineCurrentLookupKey() {
return DataSourceTypeManager.get();
}
}
4. mybatis配置文件
以上,多数据源配置完成,下面讲讲在实际进行数据操作是如何使用。
public void insertData () {
// 设置数据源,枚举值
DataSourceTypeManager.set(DataSources.SYS);
//....数据库操作
// 清除设置的值
DataSourceTypeManager.reset();
}
(1)新建SqlSessionDaoSupport的扩展类BaseDaoSupport,封装一些CRUD方法。
public abstract class BaseDaoSupport extends SqlSessionDaoSupport {
private static boolean isShowSql;
public Connection getConnection() {
return getSqlSession().getConnection();
}
public Configuration getConfiguration() {
return getSqlSession().getConfiguration();
}
@Resource
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
public boolean isShowSql() {
if (isShowSql) {
}
return isShowSql;
}
public void showSql(String statement, Object parameter) {
if (isShowSql()) {
getConfiguration().getMappedStatement(getClass().getName() + "." + statement).getBoundSql(parameter)
.getSql();
}
}
/*********************** 封装方法分割线 ***********************/
protected int _getInt(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected String _getString(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected double _getDouble(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
@SuppressWarnings("unchecked")
protected T _selectOne(String statement, Object parameter) {
this.showSql(statement, parameter);
return (T) getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected List _selectList(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectList(getClass().getName() + "." + statement, parameter);
}
// protected Pagination _selectForPage(String statement, Object parameter) {
// this.showSql(statement, parameter);
// return null;
// }
protected int _insert(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().insert(getClass().getName() + "." + statement, parameter);
}
protected int _update(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().update(getClass().getName() + "." + statement, parameter);
}
protected int _delete(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().delete(getClass().getName() + "." + statement, parameter);
}
}
(2)每个数据源新建扩展类,重写getSqlSession方法,设置数据源。
public class ApiDaoSupport extends BaseDaoSupport{
public SqlSession getSqlSession() {
// 设置相应的数据源
DataSourceTypeManager.set(DataSources.API);
return super.getSqlSession();
}
}
(3)数据库操作类,对应Mapper文件的方法。
@Repository("bicdDao")
public class BicdDao extends ApiDaoSupport{
public List
个人认为这种方法还挺方便的,同一个数据库的查询写在同一个Mapper文件中即可~代码侵入性不高,但是在数据源很多的时候需要编写很多的扩展类,也是很费体力的……
说实话,在开始学习Spring的时候并没有理解AOP的意义,当结合实际业务使用的时候就发现了它的巧妙了,一个被公认为伟大的东西一定是有它让人信服的理由,Spring就是这样一个存在,向这些框架作者致敬~
回到正题~
以下关于自定义注解的实现摘抄整理自:https://blog.csdn.net/twomr/article/details/79137056
(1)编写自定义注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
DataSourceKey dataSourceKey() default DataSourceKey.DB_MASTER;
}
(2)编写数据源切换切面类:DynamicDataSourceAspect
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
private static final Logger LOG = Logger.getLogger(DynamicDataSourceAspect.class);
@Pointcut("execution(* com.apedad.example.service.*.list*(..))")
public void pointCut() {
}
/**
* 执行方法前更换数据源
*
* @param joinPoint 切点
* @param targetDataSource 动态数据源
*/
@Before("@annotation(targetDataSource)")
public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
if (dataSourceKey == DataSourceKey.DB_OTHER) {
LOG.info(String.format("设置数据源为 %s", DataSourceKey.DB_OTHER));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_OTHER);
} else {
LOG.info(String.format("使用默认数据源 %s", DataSourceKey.DB_MASTER));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);
}
}
/**
* 执行方法后清除数据源设置
*
* @param joinPoint 切点
* @param targetDataSource 动态数据源
*/
@After("@annotation(targetDataSource)")
public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
LOG.info(String.format("当前数据源 %s 执行清理方法", targetDataSource.dataSourceKey()));
DynamicDataSourceContextHolder.clear();
}
@Before(value = "pointCut()")
public void doBeforeWithSlave(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取当前切点方法对象
Method method = methodSignature.getMethod();
if (method.getDeclaringClass().isInterface()) {//判断是否为借口方法
try {
//获取实际类型的方法对象
method = joinPoint.getTarget().getClass()
.getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
LOG.error("方法不存在!", e);
}
}
if (null == method.getAnnotation(TargetDataSource.class)) {
DynamicDataSourceContextHolder.setSlave();
}
}
}
(3)开启AOP
在spring配置文件中增加:
(4)在需要切换数据源的service方法上加上注解就OK了~
//使用此注解来切换到想切换的数据源
@TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
@Override
public int insert(UserInfo userInfo) {
return userInfoMapper.insert(userInfo);
}
以上就是关于Mybatis实现多数据源的记录~
好好学习,天天向上~^ ^