spring mybatis 多数据源读写分离实际应用

通过继承spring AbstractRoutingDataSource父类来进行动态的切换数据源,结合注解和spring aop来实现。


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

/**
* 自定义的多数据源路由器.
*
*/
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
private final transient Logger log = LoggerFactory.getLogger(CustomerRoutingDataSource.class);

@Override
protected Object determineCurrentLookupKey() {
final String dbType = CustomerDBContextHolder.getDataSourceType();
if (log.isInfoEnabled()) {
log.info("获取了KEY={}的数据源", dbType);
}
return dbType;
}
}




/**
* 自定义的上下文储存器
*/
public class CustomerDBContextHolder {

//线程安全的ThreadLocal
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

public static void setDataSourceType(String dbType) {
Assert.notNull(dbType, "DBType cannot be null");
contextHolder.set(dbType);
}

public static String getDataSourceType() {
String str = (String) contextHolder.get();
if (StringUtils.isBlank(str))
str = DataSource.master;
return str;
}

public static void clearDataSourceType() {
contextHolder.remove();
}

}





import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

/**
*注解
*/
public @interface DataSource {
String name() default DataSource.master;

public static String master = "master";

public static String slave = "slave";


}




import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
*切点
*/
@Aspect
@Component
public class DataSourceAspect {

//定义切点
@Pointcut("@annotation(org.pos.manager.data.utils.spring.DataSource)")
public void daoAspect() {

}

@Before("daoAspect()")
public void doBefore(JoinPoint joinPoint){
try {
CustomerDBContextHolder.setDataSourceType(getDataSource(joinPoint));
} catch (Exception e) {
e.printStackTrace();
}
}

@SuppressWarnings("rawtypes")
public static String getDataSource(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String name = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
name = method.getAnnotation(DataSource.class).name();
break;
}
}
}
return name;
}
}


数据源配置

<bean id="dataSourceWrite" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${master.jdbc.driverClassName}" />
<property name="jdbcUrl" value="${master.jdbc.url}" />
<property name="user" value="${master.jdbc.userDS.username}" />
<property name="password" value="${master.jdbc.userDS.password}" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="1" />
<!-- 连接池中保留的最大连接数 -->
<property name="maxPoolSize" value="200" />
<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
<property name="maxIdleTime" value="1800" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
<property name="acquireIncrement" value="5" />
<!-- 最大的PreparedStatement的数量 -->
<property name="maxStatements" value="1000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean>

<bean id="dataSourceRead" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${slave.jdbc.driverClassName}" />
<property name="jdbcUrl" value="${slave.jdbc.url}" />
<property name="user" value="${slave.jdbc.userDS.username}" />
<property name="password" value="${slave.jdbc.userDS.password}" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="1" />
<!-- 连接池中保留的最大连接数 -->
<property name="maxPoolSize" value="200" />
<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
<property name="maxIdleTime" value="1800" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
<property name="acquireIncrement" value="5" />
<!-- 最大的PreparedStatement的数量 -->
<property name="maxStatements" value="1000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean>

<bean id="dataSource" class="org.pos.manager.data.utils.spring.CustomerRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="dataSourceWrite" />
<entry key="slave" value-ref="dataSourceRead" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceWrite"/>
</bean>

applicationContext.xml中加入aop代理配置
<aop:aspectj-autoproxy proxy-target-class="true" />


最后在service层实现动态设置


@Component(value = "accountBankService")
public class AccountBankServiceImpl implements AccountBankService {

@Autowired
@Qualifier(value = "accountBankDao")
private AccountBankDao accountBankDao;

@DataSource(name = DataSource.slave)
@Override
public List<AccountBank> getAccountBankList() throws Exception {
return accountBankDao.getAccountBankList();
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值