现在老板的需求是那么的讲究,需要读写分离,那么我们如何配置多个数据源的,下面用springboot搭框架,配置多个数据源。
参考链接:https://blog.csdn.net/xuSir_1/article/details/78986972;
1.springboot配置多数据源 首先看一下application-dev.yml 配置
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver druid: first: #数据源1 url: jdbc:mysql://192.168.1.252:3306/b2q?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 username: root password: b2q123456 second: #数据源2 url: jdbc:mysql://localhost:3306/b2q?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8 username: root password: root # password: Quali-tyCz87897013! 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 lower_case_table_names: 0 #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
上面的方式实现就已经配置了两个 数据源了,下面来看下代码的实现
2、配置一个注解,方便使用,直接在需要配置的方法上面加上数据源即可
@Target({ ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String name() default ""; }
2-1:配置管理多数据源的名称,方便管理。
/** * 增加多数据源,在此配置 */ public interface DataSourceNames { String FIRST = "first"; String SECOND = "second"; }
3、动态数据源加载
package com.wsh.b2q.admin.datasources; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 动态数据源 * @author huojg */ 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(); } }
这里有必要说一下AbstractRoutingDataSource这个类,加载一个图片:
可以看到AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。
因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。
3-1:重要一点:把上面的代码加载到配置中
package com.wsh.b2q.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; /** * 配置多数据源 * @author huojg */ @Configuration public class DynamicDataSourceConfig { @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 @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceNames.FIRST, firstDataSource); targetDataSources.put(DataSourceNames.SECOND, secondDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
4、最最重要的一步,就是使用spring的aop原理,切面方式加载数据源
package com.wsh.b2q.admin.datasources.aspect; import com.wsh.b2q.admin.datasources.DataSourceNames; import com.wsh.b2q.admin.datasources.DynamicDataSource; import com.wsh.b2q.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; /** * 多数据源,切面处理类 * @author huojg */ @Aspect @Component public class DataSourceAspect implements Ordered { protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(com.wsh.b2q.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); logger.debug("set datasource is " + DataSourceNames.FIRST); }else { DynamicDataSource.setDataSource(ds.name()); logger.debug("set datasource is " + ds.name()); } try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); logger.debug("clean datasource"); } } @Override public int getOrder() { return 1; } }
5、最后一步就是使用了在你的service的实现类 serviceImpl上面进行注解,这里是重点(我刚开一直放在dao上面,因为我用的是mybatis,以为就是要放在这个上面,结果一直出不来,最后才知道应该放在serviceImpl上面)
@Override @DataSource(name="second") public List<IntegralExchangeRule> list(Map<String,Object> map) { return dao.list(map); }
OK,现在已经全部配置完成,可以使用了
有必要说一下:我上面的DataSourceAspect这个类里面around方法里面,已经默认是数据源1,如果你不配置@DaeSource(name=""),它默认会使用第一个数据源,否则的话,按照你的数据源名称去使用的。