通过继承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();
}
}
分享到:
相关推荐
在实际应用中,多数据源读写分离是非常常见的场景,今天我们就来讨论如何使用MyBatis实现多数据源读写分离。 什么是读写分离? 读写分离是指将数据库的读取和写入操作分离到不同的数据库服务器上,以提高系统的...
通过学习和理解这些代码,你可以掌握如何在实际项目中应用多数据源读写分离。 8. **注意事项**:在实现读写分离时,需要注意数据一致性问题,比如分布式事务的处理。此外,还需要考虑监控和维护多个数据源的复杂性...
当我们需要处理多个数据源时,例如在分布式系统或读写分离的场景下,Spring整合MyBatis的多数据源切换就显得尤为重要。这个"spring整合mybatis多数据源"的示例提供了可运行的代码,帮助开发者理解和实践这一功能。 ...
当这些技术结合在一起处理多数据源和动态切换时,可以实现读写分离,提高系统的性能和稳定性。 本案例中,"springboot+druid+mybatis多数据源动态切换"主要涉及以下几个核心知识点: 1. **SpringBoot多数据源配置*...
在实际应用中,可以根据业务场景灵活地切换数据源,实现读写分离、负载均衡等高级功能。需要注意的是,数据源切换应在事务边界内完成,以确保数据的一致性。 在 `thc-datasources` 压缩包中,可能包含了更详细的...
多数据源配置常见于以下场景:读写分离、微服务架构中各服务独立的数据库、历史数据归档、异构数据库系统等。 总结,Spring Boot结合MyBatis的多数据源配置为企业级应用提供了强大的数据库管理能力。通过灵活的...
总结来说,"Spring+SpringMVC+Mybatis多数据源"的整合涉及了Spring的数据源管理、SpringMVC的请求路由、Mybatis的数据源配置以及事务管理。具体实现时,可以根据项目需求选择合适的方式进行数据源切换,以达到优化...
在IT行业中,数据库的读写分离是提升系统性能和稳定性的重要策略之一,特别是在高并发的Web应用中。SpringMVC和MyBatis是常见的Java Web开发框架,而Druid则是一个优秀的数据库连接池组件,提供了丰富的监控和扩展...
当我们的项目涉及到多数据源时,即需要连接并操作多个数据库,Spring Boot和MyBatis的整合就显得尤为重要。本文将深入探讨如何实现Spring Boot结合MyBatis的多数据源最简解决方案。 首先,我们来理解多数据源的需求...
当项目中存在多个数据库需求时,例如主从分离、读写分离或者不同环境的数据隔离,就需要实现多数据源配置。本篇将深入探讨如何在Spring和MyBatis环境中配置多个数据源。 首先,我们需要理解数据源(DataSource)的...
标题 "mybatis spring 多数据源" 涉及到的...总的来说,"mybatis spring 多数据源"的主题涵盖了如何在Java应用中利用MyBatis和Spring进行多数据库管理,包括数据源配置、动态数据源路由、事务管理以及相关工具的使用。
在企业级应用开发中,经常需要访问和操作多个数据库,这种需求促使了多数据源配置与管理在Spring框架和MyBatis持久层框架中的重要性。下面对这个主题的知识点进行详细说明。 1. **多数据源场景介绍** 在处理多数据...
本项目“Spring+SpringMVC+Mybatis动态链接多数据源”旨在实现一个灵活、可扩展的数据源切换机制,以适应复杂的业务场景。 Spring框架作为Java领域中最广泛使用的轻量级框架,它提供了强大的依赖注入和AOP(面向切...
在Spring和MyBatis的配置中,可以设置多个数据源,一个用于写操作(主库),另一个或多个用于读操作(从库)。在业务逻辑中,根据操作类型选择合适的数据源进行连接,通常通过事务管理器来实现。 在本项目中,...
以上就是关于"springboot整合mybatis多数据源动态配置 swagger"这一主题的知识点详解,涵盖了Spring Boot与MyBatis的整合、多数据源读写分离的实现,以及Swagger在API文档和测试中的应用。理解并掌握这些内容,对于...
- 对于读写分离的实现,主要是在 `DataSource` 层面上进行配置,可以定义多个数据源(一个是写库,其他是读库),并通过特定策略(如轮询、随机等)来选择读库。 4. **源码修改**: - 修改 `...
在企业级应用开发中,动态数据源是一种常见需求,它允许程序在运行时根据不同的业务逻辑切换到不同的数据库。...在实际应用中,还需要考虑数据源的切换性能、事务的一致性以及在分布式环境下的协调等问题。
spring +springboot+mybatis+maven 读写分离,数据库采用mysql, 采用springboot 采用项目框架搭建,继承spring 中的AbstractRoutingDataSource,实现 determineCurrentLookupKey 进行数据源的动态切换,采用Spring ...
5. **配置实现**:在SpringMVC和MyBatis的环境中,我们通常会通过配置数据源(DataSource)来实现读写分离。需要至少两个数据源:一个是主库数据源,另一个是从库数据源。在MyBatis的SqlSessionFactory配置中,我们...