使用mybatis +spring 插件实现读写分离

     最近的项目中在数据库优化的时候需要用到读写分离,由于代码已经写好了,所以最优的方式就是在代码端不做任何修改,由于mybatis的灵活性,所以考虑用mybatis执行的时候通过插件的形式进行动态设置数据源。又由于spring支持数据源的懒加载和路由数据源的功能,所以最终解决方案是mybatis+spring数据源懒加载+spring动态数据源

   话不多说,直接上代码

   首先根据写一个动态数据源类去继承spring的AbstractRoutingDataSource类,重写他的determineCurrentLookupKey方法,从AbstractRoutingDataSource类中的

/**
	 * Retrieve the current target DataSource. Determines the
	 * {@link #determineCurrentLookupKey() current lookup key}, performs
	 * a lookup in the {@link #setTargetDataSources targetDataSources} map,
	 * falls back to the specified
	 * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
	 * @see #determineCurrentLookupKey()
	 */
	protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

这个方法主要用于通过key来获取对应的数据源

所以这个方法主要是获取数据源的key

public class DynamicDataSource extends AbstractRoutingDataSource {

	/*
	 * (non-Javadoc)
	 * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
	 */
	@Override
	protected Object determineCurrentLookupKey() {
		return DBContextHolder.getDbType();
	}
}

又由于多个线程可能都会使用到这个key所以为了避免各个线程之间相互干扰,所以借助一个DBContextHolder类来处理

public class DBContextHolder {

	private static Logger logger = Logger.getLogger(DBContextHolder.class);

	/**
	 * 线程ThreadLocal
	 */
	private static ThreadLocal<String> contextHolder = new ThreadLocal<>();

	public static final String DB_TYPE_WR = "dbTypeWR";

	public static final String DB_TYPE_RD = "dbTypeRD";

	public static String getDbType() {
		String db = contextHolder.get();
		if(db == null) {
			db = DB_TYPE_WR;// 默认是读写库
		}
		return db;
	}

	/**
	 * @Title: setDbType
	 * @Description: 设置本线程的dbtype
	 * @author YiXiaoGang
	 * @date 2015年12月25日 下午3:04:49
	 * @param str
	 */
	public static void setDbType(String str) {
		logger.debug("所使用数据源为:" + str);
		contextHolder.set(str);
	}

	/**
	 * @Title: clearDBType
	 * @Description: 清理连接类型
	 * @author YiXiaoGang
	 * @date 2015年12月25日 下午3:05:00
	 */
	public static void clearDBType() {
		contextHolder.remove();
	}
}

到此,spring部分就做完了,最后,就是mybatis插件部分,对所有的update方法读取主数据源,对所有的query方法读取从数据源

@Intercepts({
		@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
		@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,
				ResultHandler.class})}
		)
public class DatasouceInterceptor implements Interceptor {

		/**
	 * 插件方法更新
	 */
	private static final String UPDATE = "update";

	/**
	 * 插件方法查询
	 */
	private static final String QUERY = "query";

	Logger logger = Logger.getLogger(PagingInterceptor.class);

	/**
	 * SQL方言 可选MySql,Oracle
	 */
	private String diacect;

	/**
	 * mybatis插件拦截方法,用于分页,加解密
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		String methodName = invocation.getMethod().getName();
		MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
	
		if(QUERY.equals(methodName)) {
			DBContextHolder.setDbType(DBContextHolder.DB_TYPE_RD);
			return doQuery(invocation, fields, coditions);
		}else if(UPDATE.equals(methodName)) {
			DBContextHolder.setDbType(DBContextHolder.DB_TYPE_WR);
			return doUpdate(invocation, fields);
		}
		return null;
	}


   最后配置多数据源spring数据源部分如下

  

<!-- 配置主数据源(可读写)-->
	<bean id="dataSourceWR" class="org.apache.commons.dbcp2.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="test1" />
		<property name="password" value="test1" />
		<!-- 初始化连接数 -->
		<property name="initialSize" value="1" />
		<!-- 最大活动连接数 <property name="maxActive" value="50" /> -->
		<!-- 最大空闲连接数 -->
		<property name="maxIdle" value="10" />
		<!-- 最小空闲连接数 -->
		<property name="minIdle" value="1" />
		<!-- 获取连接超时等待时间(毫秒) <property name="maxWait" value="10000" /> -->
		<!-- 空闲池空闲连接激活线程的运行间隔时间(毫秒) -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 空闲池中空闲连接能够被激活前的最小空闲时间(毫秒) -->
		<property name="minEvictableIdleTimeMillis" value="10000" />
	</bean>
	
	<!--配置从数据源,只用于读 -->
	<bean id="dataSourceRD" class="org.apache.commons.dbcp2.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcal" />
		<property name="username" value="test2" />
		<property name="password" value="test2" />
		<!-- 初始化连接数 -->
		<property name="initialSize" value="1" />
		<!-- 最大活动连接数 <property name="maxActive" value="50" /> -->
		<!-- 最大空闲连接数 -->
		<property name="maxIdle" value="10" />
		<!-- 最小空闲连接数 -->
		<property name="minIdle" value="1" />
		<!-- 获取连接超时等待时间(毫秒) <property name="maxWait" value="10000" /> -->
		<!-- 空闲池空闲连接激活线程的运行间隔时间(毫秒) -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 空闲池中空闲连接能够被激活前的最小空闲时间(毫秒) -->
		<property name="minEvictableIdleTimeMillis" value="10000" />
	</bean>

	<!-- 定义sqlSessionFactory 并自动注册实体别名 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath*:mapping/*/*.xml" />
        <property name="plugins">
            <ref bean="pagingInterceptor" />
        </property>
    </bean>
    <!--配置动态数据源,这儿targetDataSources就是路由数据源所对应的名称-->
    <bean id="dynamicDataSource" class="com.test.DynamicDataSource">
          <!-- 通过key-value关联数据源 -->  
       <property name="targetDataSources">  
           <map>  
               <entry value-ref="dataSourceWR" key="dbTypeWR"></entry>  
               <entry value-ref="dataSourceRD" key="dbTypeRD"></entry>  
           </map>  
       </property>  
     
    </bean>
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref local="dynamicDataSource"/>
        </property>
    </bean>


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值