通信网站模板,网站建设备案,网站分几种类型,一个网站的建设步骤背景
随着互联网应用的快速发展#xff0c;多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能#xff0c;成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例#xff0c;详细介绍如何在 Spring Boot 应用中实现动态数据源切换#xff0c;帮…背景
随着互联网应用的快速发展多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例详细介绍如何在 Spring Boot 应用中实现动态数据源切换帮助开发者高效应对不同业务场景下的数据管理需求。无论是读写分离还是数据隔离都能轻松搞定。
AOP动态代理
AOP注解
Target({ElementType.METHOD, ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
public interface TargetDataSource {String name();
}
AOP切面类
Aspect
Component
public class DataSourceAspect {Pointcut(annotation(com.example.aliyunai.db.TargetDataSource))public void dataSourcePointcut() {}Before(dataSourcePointcut())public void changeDataSource(JoinPoint point) {MethodSignature signature (MethodSignature) point.getSignature();Method method signature.getMethod();TargetDataSource targetDataSource method.getAnnotation(TargetDataSource.class);if (targetDataSource ! null) {String dataSourceName targetDataSource.name();DataSourceContextHolder.setDataSourceKey(dataSourceName);}}After(dataSourcePointcut())public void clearDataSource(JoinPoint point) {DataSourceContextHolder.clearDataSourceKey();}
}
数据源配置
Configuration
public class DataSourceConfig {Bean(name master)public DataSource primaryDataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).driverClassName().url().username().password().build();}Bean(name slave)public DataSource secondaryDataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).driverClassName().url().username().password().build();}Bean(name dynamicDataSource)Primarypublic DynamicDataSource dynamicDataSource(Qualifier(master) DataSource master,Qualifier(slave) DataSource slave) {MapObject, Object targetDataSources new HashMap();targetDataSources.put(master, master);targetDataSources.put(slave, slave);DynamicDataSource dynamicDataSource new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(master);return dynamicDataSource;}}
线程上下文 public class DataSourceContextHolder {private static final ThreadLocalString contextHolder new ThreadLocal();public static void setDataSourceKey(String key) {contextHolder.set(key);}public static String getDataSourceKey() {return contextHolder.get();}public static void clearDataSourceKey() {contextHolder.remove();}
}
动态数据源设置
public class DynamicDataSource extends AbstractRoutingDataSource {private static final Logger logger LoggerFactory.getLogger(DynamicDataSource.class);Overrideprotected Object determineCurrentLookupKey() {String dataSourceKey DataSourceContextHolder.getDataSourceKey();logger.info(Determining current data source: {}, dataSourceKey);return dataSourceKey;}
}service类
Service
public class UserService {Resourceprivate UserMapper userMapper;TargetDataSource(name master)public User queryFromPrimary() {User user userMapper.selectById(1);return user;}TargetDataSource(name slave)public User queryFromSecondary() {User user userMapper.selectById(1L);return user;}
}
Mybatis 拦截器 Component
Intercepts({Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),Signature(type Executor.class, method update, args {MappedStatement.class,Object.class})})
public class DynamicDataSourceInterceptor implements Interceptor {private static final Logger logger LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);// 假设这里有一个获取当前数据源标识的方法你需要根据实际项目中的实现来替换private String getCurrentDataSourceKey() {// 示例从某个上下文持有者中获取数据源标识这里只是示意实际要替换return DataSourceContextHolder.getDataSourceKey();}Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement (MappedStatement) invocation.getArgs()[0];Object parameter invocation.getArgs()[1];BoundSql boundSql mappedStatement.getBoundSql(parameter);String sql boundSql.getSql();//对单个表进行处理logger.info(sql:sql);String mappedStatementId mappedStatement.getId();//对所有查询进行处理if (mappedStatementId.contains(query)) {String table getTable(sql);if (table.equals(user)){DataSourceContextHolder.setDataSourceKey(getCurrentDataSourceKey());}//增删改}else if (mappedStatementId.contains(update)) {DataSourceContextHolder.setDataSourceKey(getCurrentDataSourceKey());}return invocation.proceed();}Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}private static String getTable(String sql) {// 定义正则表达式模式用于匹配 from 和 where 之间的表名Pattern pattern Pattern.compile(FROM\\s(\\w)\\sWHERE);Matcher matcher pattern.matcher(sql);if (matcher.find()) {return matcher.group(1);}return null;}
}