您的位置:首页 > 编程语言 > Java开发

Spring+mybatis 使用注解配置多数据库源,支持读写分离

2018-01-20 09:36 489 查看
第一步:创建一个DynamicDataSource的类,继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,代码如下:

package spring;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {

return DynamicDataSourceHolder.getDataSource();
}
}

第二步:创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识

package spring;

public class DynamicDataSourceHolder {

private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();

public static String getDataSource() {
return THREAD_DATA_SOURCE.get();
}

public static void setDataSource(String dataSource) {
THREAD_DATA_SOURCE.set(dataSource);
}

public static void clearDataSource() {
THREAD_DATA_SOURCE.remove();
}

}

第三步:配置多个数据源和第一步里创建的DynamicDataSource的bean,简化的配置如下:

<bean id="readServerDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.mysql.driver}"/>
<property name="url" value="${jdbc.mysql.url}"/>
<property name="username" value="${jdbc.mysql.username}"/>
<property name="password" value="${jdbc.mysql.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxIdle" value="${jdbc.maxIdle}"/>
<!--<property name="maxActive" value="${jdbc.maxActive}"/>-->
<!--<property name="maxWait" value="${jdbc.maxWait}"/>-->
<property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>
<!--<property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>-->
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
<property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
<property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerE
e4d4
victionRun}"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
</bean>

<bean id="writeServerDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.mysql.driver}"/>
<property name="url" value="${jdbc.mysql.url}"/>
<property name="username" value="${jdbc.mysql.username}"/>
<property name="password" value="${jdbc.mysql.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxIdle" value="${jdbc.maxIdle}"/>
<!--<property name="maxActive" value="${jdbc.maxActive}"/>-->
<!--<property name="maxWait" value="${jdbc.maxWait}"/>-->
<property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>
<!--<property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>-->
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
<property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
<property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
</bean>

<bean id="dataSource" class="spring.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 指定lookupKey和与之对应的数据源 -->
<entry key="readServerDataSource" value-ref="readServerDataSource"></entry>
<entry key="writeServerDataSource" value-ref="writeServerDataSource"></entry>
</map>
</property>
<!-- 这里可以指定默认的数据源 -->
<property name="defaultTargetDataSource" ref="readServerDataSource"/>
</bean>

<!-- MyBatis配置 -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描domain目录, 省掉Configuration.xml里的手工配置 -->
<property name="typeAliasesPackage" value="order.dao.domain"/>
<!-- 显式指定Mapper文件位置 -->
<property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

到这里已经可以使用多数据源了,在操作数据库之前只要DynamicDataSourceHolder.setDataSource("readServerDataSource")即可切换到数据源对其只进行读的操作,需要使用只要DynamicDataSourceHolder.setDataSource("writeServerDataSource")

接下来配置自定义注解用来切换数据源,这样操作起来更加的简便,不用每次都去set

首先,我们得定义一个名为DataSource的注解

package spring;

import org.springframework.stereotype.Component;

import java.lang.annotation.*;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value();
}

然后,定义AOP切面以便拦截所有带有注解@DataSource的方法,取出注解的值作为数据源标识放到DynamicDataSourceHolder的线程变量中

package spring;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

@Component
@Aspect
public class DataSourceAspect {
/**
* 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
*
* @param point
* @throws Exception
*/
public void intercept(JoinPoint point) throws Exception {
Class<?> target = point.getTarget().getClass();
MethodSignature signature = (MethodSignature) point.getSignature();
// 默认使用目标类型的注解,如果没有则使用其实现接口的注解
for (Class<?> clazz : target.getInterfaces()) {
resolveDataSource(clazz, signature.getMethod());
}
resolveDataSource(target, signature.getMethod());
}

/**
* 提取目标对象方法注解和类型注解中的数据源标识
*
* @param clazz
* @param method
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
// 默认使用类型注解
if (clazz.isAnnotationPresent((Class<? extends Annotation>) DataSource.class)) {
DataSource source = clazz.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
// 方法注解可以覆盖类型注解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource source = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
} catch (Exception e) {
System.out.println(clazz + ":" + e.getMessage());
}
}
}

OK,这样就可以直接在类或者方法上使用注解@DataSource来指定数据源,不需要每次都手动设置了。

提示:注解@DataSource既可以加在方法上,也可以加在接口或者接口的实现类上,优先级别:方法>实现类>接口。也就是说如果接口、接口实现类以及方法上分别加了@DataSource注解来指定数据源,则优先以方法上指定的为准。

例子用读的数据源:

@Service("ProductDAOImpl")
@DataSource("readServerDataSource")
public class ProductDAOImpl extends MyAbstractPageService<IProductDAO, Product> {

@Autowired
private IProductDAO dao;

@Override
public IProductDAO getDao() {
return dao;
}

public IProductDAO querySum() {
return dao;
}

public IProductDAO queryId(String id) {
return dao;
}
}

然后自己写个数据库的操作测试一下是否成功
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐