赞
踩
提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!!
package com.gateway.admin.datasources.annotation;
import java.lang.annotation.*;
/**
* 多数据源注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
package com.gateway.admin.datasources; import com.gateway.admin.datasources.annotation.DataSource; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 多数据源,切面处理类 */ @Aspect @Component public class DataSourceAspect implements Ordered { protected Logger logger = LoggerFactory.getLogger(getClass()); /** * 针对上面注解做切面拦截 */ @Pointcut("@annotation(com.gateway.admin.datasources.annotation.DataSource)") public void dataSourcePointCut() {} @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource ds = method.getAnnotation(DataSource.class); if(ds == null){ //如果没有注解,使用默认数据源 DynamicDataSource.setDataSource(DataSourceNames.FIRST); }else { //根据注解中设置的数据源名称,选择对应的数据源 DynamicDataSource.setDataSource(ds.name()); logger.debug("set datasource is " + ds.name()); } try { return point.proceed(); } finally { //清除数据源配置 DynamicDataSource.clearDataSource(); } } @Override public int getOrder() { return 1; } }
package com.gateway.admin.datasources; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 动态数据源 */ public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
4 .数据源名称配置
package com.gateway.admin.datasources;
/**
* 多数据源配置数据源
*/
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
String THREE = "three";
String FOUR = "four";
}
5 .多数据源配置类
package com.gateway.admin.datasources; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 多数据源配置类 */ @Configuration public class DynamicDataSourceConfig { //如果ioc容器中,同一个类型有多个bean,则bean的名称为方法的名称 @Bean @ConfigurationProperties("spring.datasource.druid.first") public DataSource firstDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.second") public DataSource secondDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.three") public DataSource threeDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.four") public DataSource fourDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource, DataSource threeDataSource, DataSource fourDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceNames.FIRST, firstDataSource); targetDataSources.put(DataSourceNames.SECOND, secondDataSource); targetDataSources.put(DataSourceNames.THREE, threeDataSource); targetDataSources.put(DataSourceNames.FOUR, fourDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver druid: first: #db1 url: jdbc:mysql://127.0.0.1:3306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root second: #db2 url: jdbc:mysql://127.0.0.1:3306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root three: #db3 url: jdbc:mysql://127.0.0.1:3306/db3?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root four: #db4 url: jdbc:mysql://127.0.0.1:3306/db4?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root
//通过@DataSource以及name,动态指定对应的数据源
@DataSource(name = DataSourceNames.SECOND)
public void test(String param) {
testMapper.test(param);
}
部分源码分析
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { @Nullable//目标数据源 private Map<Object, Object> targetDataSources; @Nullable//默认数据源 private Object defaultTargetDataSource; @Nullable//解析的数据源 private Map<Object, DataSource> resolvedDataSources; @Nullable//解析默认的数据源 private DataSource resolvedDefaultDataSource; @Override public void afterPropertiesSet() { if (this.targetDataSources == null) { throw new IllegalArgumentException("Property 'targetDataSources' is required"); } this.resolvedDataSources = new HashMap<>(this.targetDataSources.size()); this.targetDataSources.forEach((key, value) -> { Object lookupKey = resolveSpecifiedLookupKey(key); DataSource dataSource = resolveSpecifiedDataSource(value); this.resolvedDataSources.put(lookupKey, dataSource); }); if (this.defaultTargetDataSource != null) { //设置解析的默认数据源 this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource); } } //数据源对应的key,默认和目标数据源map中的key一致 protected Object resolveSpecifiedLookupKey(Object lookupKey) { return lookupKey; } //将指定的数据源对象解析为 DataSource 实例 protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException { if (dataSource instanceof DataSource) { return (DataSource) dataSource; } else if (dataSource instanceof String) { return this.dataSourceLookup.getDataSource((String) dataSource); } else { throw new IllegalArgumentException( "Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource); } } //==========================================获取数据库连接========================================= @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { //从特定的数据源中获取对应的数据库连接对象 return determineTargetDataSource().getConnection(username, password); } protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); //DynamicDataSource 中对 determineCurrentLookupKey()方法进行了重写 Object lookupKey = determineCurrentLookupKey(); //resolvedDataSources 对应的map中获取对应的数据源 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; } @Nullable protected abstract Object determineCurrentLookupKey(); }
现有需求: 在多数据源能够动态切换的前提下,其中某个库中的表需要实现分表操作,例如:上述多数据源配置中的second 数据库中某个表需要实现分表操作,我们采用ShardingJDBC实现。此时second 数据源的配置需要更改为ShardingJDBC对应的数据源,如下面更改后的多数据源配置:
原来多数据源配置类
package com.gateway.admin.datasources; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 多数据源配置类 */ @Configuration public class DynamicDataSourceConfig { //如果ioc容器中,同一个类型有多个bean,则bean的名称为方法的名称 @Bean @ConfigurationProperties("spring.datasource.druid.first") public DataSource firstDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.second") public DataSource secondDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.three") public DataSource threeDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.four") public DataSource fourDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource, DataSource threeDataSource, DataSource fourDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceNames.FIRST, firstDataSource); targetDataSources.put(DataSourceNames.SECOND, secondDataSource); targetDataSources.put(DataSourceNames.THREE, threeDataSource); targetDataSources.put(DataSourceNames.FOUR, fourDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
整合ShardingJDBC 后,多数据源配置类
package com.gateway.admin.datasources; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.apache.shardingsphere.shardingjdbc.jdbc.adapter.AbstractDataSourceAdapter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 多数据源配置类 */ @Configuration public class DynamicDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.druid.first") public DataSource firstDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.three") public DataSource threeDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.four") public DataSource fourDataSource() { return DruidDataSourceBuilder.create().build(); } //该数据源为shardingjdbc对应的数据源,可通过java代码配置,也可通过配置文件配置,此处我们采用yml配置,配置文件下面会给出 /** * shardingjdbc有四种数据源,需要根据业务注入不同的数据源 * * <p>1. 未使用分片, 脱敏的名称(默认): shardingDataSource; * <p>2. 主从数据源: masterSlaveDataSource; * <p>3. 脱敏数据源:encryptDataSource; * <p>4. 影子数据源:shadowDataSource * */ @Lazy @Resource(name = "shardingDataSource") private AbstractDataSourceAdapter shardingDataSource; @Bean @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource threeDataSource, DataSource fourDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceNames.FIRST, firstDataSource); targetDataSources.put(DataSourceNames.SECOND, shardingDataSource); targetDataSources.put(DataSourceNames.THREE, threeDataSource); targetDataSources.put(DataSourceNames.FOUR, fourDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
对应的yml配置文件为(我们此处只是针对单个库做分表操作):
# Tomcat server: tomcat: uri-encoding: UTF-8 max-threads: 1000 min-spare-threads: 30 port: 8888 spring: #shardingjdbc-------------------------------------------------------------------------------------------------------- main: allow-bean-definition-overriding: true shardingsphere: props: sql: show: true dataSource: names: ds0 ds0: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/gateway_stable?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root sharding: tables: t_order: logicTable: t_order actualDataNodes: ds0.t_order$->{1..2} tableStrategy: # standard: # shardingColumn: id # preciseAlgorithmClassName: com.personal.datasources.MonthPreciseShardingAlgorithm # rangeAlgorithmClassName: inline: shardingColumn: id algorithmExpression: t_order$->{id % 2 + 1} keyGenerator: type: SNOWFLAKE column: id worker: id: 1 datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver druid: first: #数据源1 url: jdbc:mysql://127.0.0.1:3306/wanshu_admin?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 # second: #数据源2 # url: jdbc:mysql://127.0.0.1:3306/gateway_stable?useUnicode=true&characterEncoding=utf-8&useSSL=false # username: root # password: root three: #数据源3 url: jdbc:mysql://127.0.0.1:3306/datacenter_stable?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 four: #数据源4 url: jdbc:mysql://127.0.0.1:3306/chuanglan_risk_control?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 initial-size: 10 max-active: 100 min-idle: 10 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin filter: stat: log-slow-sql: true slow-sql-millis: 1000 merge-sql: false wall: config: multi-statement-allow: true mybatis: mapper-locations: classpath:mapper/**/*.xml
关于shardingJDBC的相关知识,可参考:shardingJDBC分库分表
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。