1. Implementation principle

AbstractRoutingDataSource determineTargetDataSource method in the form of data source identification for current data sources; DetermineCurrentLookupKey method is to obtain data source identification, dynamic data source switch, you need to implement determineCurrentLookupKey method, providing dynamic data source identification. Annotations on AOP recognition methods are used to switch data sources. Use default data sources without annotations.

2. Required classes

Implement AbstractRoutingDataSource method, using DataSourceContextHolder unified data management

DynamicDataSource

package com.hsshy.beam.common.mutidatasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; Dynamic data source / * * * * * * @ @ author hs date on February 12, 2019 * / public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); }}Copy the code

DataSourceContextHolder

package com.hsshy.beam.common.mutidatasource; /** * datasource context ** @author hs * @date 2019年2月12日 */ public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); Public static void setDataSourceType(String dataSourceType) {public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() {return contextholder.get (); } /** * clearDataSourceType */ public static void clearDataSourceType() {contextholder.remove (); }}Copy the code

Database Data Source Configuration

A. Default data source HikariProperties configuration

package com.hsshy.beam.common.config; import com.zaxxer.hikari.HikariDataSource; /** * <p> database data source configuration </p> * @author hs * @date 12 February 2019 */ public class HikariProperties {private String URL = "JDBC: mysql: / / 127.0.0.1:3306 / beam? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.cj.jdbc.Driver"; private long connectionTimeout = 60000L; private long idleTimeout = 60000L; private long validationTimeout = 3000L; private long maxLifetime = 60000L; private int maximumPoolSize = 60; private int minimumIdle = 10; public void config(HikariDataSource dataSource) { dataSource.setJdbcUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setDriverClassName(driverClassName); dataSource.setConnectionTimeout(connectionTimeout); dataSource.setIdleTimeout(idleTimeout); dataSource.setValidationTimeout(validationTimeout); dataSource.setMaxLifetime(maxLifetime); dataSource.setMaximumPoolSize(maximumPoolSize); dataSource.setMinimumIdle(minimumIdle); dataSource.setReadOnly(false); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public long getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(long connectionTimeout) { this.connectionTimeout = connectionTimeout; } public long getIdleTimeout() { return idleTimeout; } public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; } public long getValidationTimeout() { return validationTimeout; } public void setValidationTimeout(long validationTimeout) { this.validationTimeout = validationTimeout; } public long getMaxLifetime() { return maxLifetime; } public void setMaxLifetime(long maxLifetime) { this.maxLifetime = maxLifetime; } public int getMaximumPoolSize() { return maximumPoolSize; } public void setMaximumPoolSize(int maximumPoolSize) { this.maximumPoolSize = maximumPoolSize; } public int getMinimumIdle() { return minimumIdle; } public void setMinimumIdle(int minimumIdle) { this.minimumIdle = minimumIdle; }}Copy the code

B. Configure MutiDataSourceProperties for the second data source

package com.hsshy.beam.common.config; import com.zaxxer.hikari.HikariDataSource; /** * Default multi-data source configuration ** @author fengshuonan * @date 2017-08-16 10:02 */ public class MutiDataSourceProperties {private The String url = "JDBC: mysql: / / 127.0.0.1:3306 / biz? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"; private String username = "root"; private String password = "root"; private long connectionTimeout = 60000L; private long idleTimeout = 60000L; private long validationTimeout = 3000L; private long maxLifetime = 60000L; private int maximumPoolSize = 60; private int minimumIdle = 10; private String filters = "log4j,wall,mergeStat"; private String driverClassName = "com.mysql.cj.jdbc.Driver"; private String[] dataSourceNames = {"dataSourceBeam", "dataSourceBiz"}; public void config(HikariDataSource dataSource) { dataSource.setJdbcUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setConnectionTimeout(connectionTimeout); dataSource.setIdleTimeout(idleTimeout); dataSource.setValidationTimeout(validationTimeout); dataSource.setMaxLifetime(maxLifetime); dataSource.setMaximumPoolSize(maximumPoolSize); dataSource.setMinimumIdle(minimumIdle); dataSource.setReadOnly(false); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public long getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(long connectionTimeout) { this.connectionTimeout = connectionTimeout; } public long getIdleTimeout() { return idleTimeout; } public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; } public long getValidationTimeout() { return validationTimeout; } public void setValidationTimeout(long validationTimeout) { this.validationTimeout = validationTimeout; } public long getMaxLifetime() { return maxLifetime; } public void setMaxLifetime(long maxLifetime) { this.maxLifetime = maxLifetime; } public int getMaximumPoolSize() { return maximumPoolSize; } public void setMaximumPoolSize(int maximumPoolSize) { this.maximumPoolSize = maximumPoolSize; } public int getMinimumIdle() { return minimumIdle; } public void setMinimumIdle(int minimumIdle) { this.minimumIdle = minimumIdle; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } public String[] getDataSourceNames() { return dataSourceNames; } public void setDataSourceNames(String[] dataSourceNames) { this.dataSourceNames = dataSourceNames; }}Copy the code

Configure multidata source MultiDataSourceConfig

package com.hsshy.beam.common.config; import com.hsshy.beam.common.mutidatasource.DynamicDataSource; import com.hsshy.beam.common.mutidatasource.aop.MultiSourceExAop; import com.zaxxer.hikari.HikariDataSource; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; import java.sql.SQLException; import java.util.HashMap; /** * multiple data source configuration <br/> * <p> * With the introduction of multiple data sources, So let spring transaction AOP after switching to multi-source AOP * * @author hs * @date 2019/2/1221:58 */ @configuration @conditionalonProperty (prefix = "beam.muti-datasource", name = "open", havingValue = "true") @EnableTransactionManagement(order = 2) public class MultiDataSourceConfig { @Bean @ConfigurationProperties(prefix = "beam.muti-datasource") public MutiDataSourceProperties mutiDataSourceProperties() { return new MutiDataSourceProperties(); } @Bean @ConfigurationProperties(prefix = "spring.datasource") public HikariProperties hikariProperties() { return new HikariProperties(); } @Bean public MultiSourceExAop multiSourceExAop() { return new MultiSourceExAop(); } /** ** private HikariDataSource dataSource(HikariProperties druidProperties) {HikariDataSource dataSource = new HikariDataSource(); druidProperties.config(dataSource); return dataSource; } /** * multiple data sources, */ Private HikariDataSource bizDataSource(HikariProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) { HikariDataSource dataSource = new HikariDataSource(); druidProperties.config(dataSource); mutiDataSourceProperties.config(dataSource); return dataSource; } /** * @bean public DynamicDataSource mutiDataSource(HikariProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) { HikariDataSource dataSourceBeam = dataSource(druidProperties); HikariDataSource bizDataSource = bizDataSource(druidProperties, mutiDataSourceProperties); try { dataSourceBeam.getConnection(); bizDataSource.getConnection(); } catch (SQLException sql) { sql.printStackTrace(); } DynamicDataSource dynamicDataSource = new DynamicDataSource(); HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(mutiDataSourceProperties.getDataSourceNames()[0], dataSourceBeam); hashMap.put(mutiDataSourceProperties.getDataSourceNames()[1], bizDataSource); dynamicDataSource.setTargetDataSources(hashMap); dynamicDataSource.setDefaultTargetDataSource(dataSourceBeam); return dynamicDataSource; }}Copy the code

DataSource identity DataSource

package com.hsshy.beam.common.mutidatasource.annotion; import java.lang.annotation.*; /** ** Multi-data source identifier ** @author hs * @date 12 February 2019 */ @inherited @Retention(retentionPolicy. RUNTIME) @target ({ ElementType.METHOD }) public @interface DataSource { String name() default ""; }Copy the code

AOP dynamically switch data source MultiSourceExAop

package com.hsshy.beam.common.mutidatasource.aop; import com.hsshy.beam.common.config.MutiDataSourceProperties; import com.hsshy.beam.common.mutidatasource.DataSourceContextHolder; import com.hsshy.beam.common.mutidatasource.annotion.DataSource; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; 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.beans.factory.annotation.Autowired; import org.springframework.core.Ordered; import java.lang.reflect.Method; /** * aop ** @author hs * @date 2019年2月12日 */ @aspect public class MultiSourceExAop implements Ordered {private  Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired MutiDataSourceProperties mutiDataSourceProperties; @Pointcut(value = "@annotation(com.hsshy.beam.common.mutidatasource.annotion.DataSource)") private void cut() { } @Around("cut()") public Object around(ProceedingJoinPoint point) throws Throwable { Signature signature = point.getSignature(); MethodSignature methodSignature = null; if (! (Signature Instanceof MethodSignature)) {throw new IllegalArgumentException(" This annotation only works with methods "); } methodSignature = (MethodSignature) signature; Object target = point.getTarget(); Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); DataSource datasource = currentMethod.getAnnotation(DataSource.class); if (datasource ! = null) { DataSourceContextHolder.setDataSourceType(datasource.name()); Log.debug (" set datasource to: "+ datasource.name()); } else { DataSourceContextHolder.setDataSourceType(mutiDataSourceProperties.getDataSourceNames()[0]); Log.debug (" Set data source to: dataSourceCurrent"); } try { return point.proceed(); } finally {log.debug(" Empty data source information!" ); DataSourceContextHolder.clearDataSourceType(); Override public int getOrder() {return 1; }}Copy the code

Enumerating DatasourceEnum for multiple data sources

package com.hsshy.beam.common.constant; /** ** Enumeration of multiple data sources ** @author hs * @date Feb. 12, 2019 */ public interface DatasourceEnum {String DATA_SOURCE_GUNS = "dataSourceBeam"; //beam data source String DATA_SOURCE_BIZ = "dataSourceBiz"; // Other business data source}Copy the code

Configuration file application.yml

Server: port: 8081 spring: profiles: local datasource: url: JDBC: mysql: / / 127.0.0.1:3306 / beam? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezo ne=GMT%2B8 username: root password: 123456 filters: log4j,wall,mergeStat hikari: readOnly: false connectionTimeout: 60000 idleTimeout: 60000 validationTimeout: 3000 maxLifetime: 60000 loginTimeout: 5 maximumPoolSize: 60 minimumIdle: 10 redis: database: 0 host: 127.0.0.1 port: 6379 Password: # Password (default empty) timeout: 6000ms # connection timeout duration (ms) ## beam: Muti - a datasource: open: true url: JDBC: mysql: / / 127.0.0.1:3306 / test? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezo ne=GMT%2B8 username: root password: 123456 dataSourceNames: - dataSourceBeam - dataSourceBizCopy the code

Only one data source can use the following configuration to use the Hikari database connection pool

Spring: profiles: local datasource: url: JDBC: mysql: / / 127.0.0.1:3306 / beam? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezo ne=GMT%2B8 username: root password: root filters: log4j,wall,mergeStat hikari: readOnly: false connectionTimeout: 60000 idleTimeout: 60000 validationTimeout: 3000 maxLifetime: 60000 loginTimeout: 5 maximumPoolSize: 60 minimumIdle: 10Copy the code