Mybatis配置多数据源

        在大多数情况下,一个功能全面的系统只使用单一数据源几乎是不太可能的,所以配置多数据源是十分必要的,记录一下去年某个项目配置多数据源的方式~

单一数据源的配置如下:

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<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {

		@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配置文件


以上,多数据源配置完成,下面讲讲在实际进行数据操作是如何使用。

  • 第一种方法属于比较笨的方法,就是在操作数据之前,手动设置DataSourceTypeManager.set(),执行完数据库操作之后重置;
public void insertData () {
    // 设置数据源,枚举值
    DataSourceTypeManager.set(DataSources.SYS);
    //....数据库操作

    // 清除设置的值
    DataSourceTypeManager.reset();

}
  • 第二种是之前项目使用的,通过扩展SqlSessionDaoSupport实现。

(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> T _selectOne(String statement, Object parameter) {
		this.showSql(statement, parameter);
		return (T) getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
	}

	protected <T> List<T> _selectList(String statement, Object parameter) {
		this.showSql(statement, parameter);
		return getSqlSession().selectList(getClass().getName() + "." + statement, parameter);
	}

//	protected <T> Pagination<T> _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<Map<String, Object>> queryYieldPgAttention(String reportName, String site) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("site", site);
		params.put("reportName", reportName);
		return this._selectList("queryYieldPgAttention", params);
	}
}

        个人认为这种方法还挺方便的,同一个数据库的查询写在同一个Mapper文件中即可~代码侵入性不高,但是在数据源很多的时候需要编写很多的扩展类,也是很费体力的……

  • 第三种:使用Spring AOP实现注解配置数据源

        说实话,在开始学习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)在需要切换数据源的service方法上加上注解就OK了~

//使用此注解来切换到想切换的数据源
    @TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
    @Override
    public int insert(UserInfo userInfo) {
        return userInfoMapper.insert(userInfo);
    }

以上就是关于Mybatis实现多数据源的记录~

好好学习,天天向上~^ ^

猜你喜欢

转载自blog.csdn.net/lvdou1120101985/article/details/81184871
今日推荐