diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceConfiguration.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceConfiguration.java index 5b4e9b03..48099cdd 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceConfiguration.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceConfiguration.java @@ -1,16 +1,13 @@ package com.jmsoftware.maf.springcloudstarter.database; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; -import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import lombok.extern.slf4j.Slf4j; import lombok.val; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.quartz.QuartzDataSource; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -28,45 +25,32 @@ @ConditionalOnClass({MybatisPlusAutoConfiguration.class}) @AutoConfigureBefore({MybatisPlusAutoConfiguration.class}) public class DataSourceConfiguration { - @Bean("masterDataSource") - @ConfigurationProperties("spring.datasource.dynamic.datasource.master") - public DataSource masterDataSource() { - log.warn("Initial bean: masterDataSource"); - return DruidDataSourceBuilder.create().build(); - } - - @Bean("slave1DataSource") - @ConfigurationProperties("spring.datasource.dynamic.datasource.slave1") - public DataSource slave1DataSource() { - log.warn("Initial bean: slave1DataSource"); - return DruidDataSourceBuilder.create().build(); - } - - @Bean("quartzDataSource") @QuartzDataSource - @ConfigurationProperties("spring.datasource.dynamic.datasource.quartz") + @Bean("quartzDataSource") @ConditionalOnProperty(prefix = "spring.quartz", name = "job-store-type", havingValue = "jdbc") - public DataSource quartzDataSource() { + public DataSource quartzDataSource(DynamicRoutingDataSource dynamicRoutingDataSource) { log.warn("Initial bean: quartzDataSource"); - return DruidDataSourceBuilder.create().build(); + return dynamicRoutingDataSource.getDataSource(DataSourceEnum.QUARTZ.getDataSourceName()); } @Bean @Primary - public DynamicRoutingDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, - @Qualifier("slave1DataSource") DataSource slave1DataSource) { - log.warn("Loading masterDataSource and slave1DataSource as DynamicDataSource"); + public ReadWriteIsolationDynamicRoutingDataSource dynamicDataSource(DynamicRoutingDataSource dynamicRoutingDataSource) { val targetDataSources = new HashMap<>(4); - targetDataSources.put(DataSourceTypeEnum.MASTER, masterDataSource); - targetDataSources.put(DataSourceTypeEnum.SLAVE1, slave1DataSource); - val dynamicDataSource = new DynamicRoutingDataSource(); - dynamicDataSource.setDefaultTargetDataSource(masterDataSource); + targetDataSources.put(DataSourceEnum.MASTER, + dynamicRoutingDataSource.getDataSource(DataSourceEnum.MASTER.getDataSourceName())); + targetDataSources.put(DataSourceEnum.SLAVE1, + dynamicRoutingDataSource.getDataSource(DataSourceEnum.SLAVE1.getDataSourceName())); + val dynamicDataSource = new ReadWriteIsolationDynamicRoutingDataSource(); + dynamicDataSource.setDefaultTargetDataSource(dynamicRoutingDataSource.getDataSource("master")); dynamicDataSource.setTargetDataSources(targetDataSources); + log.warn("Set 'masterDataSource' and 'slave1DataSource' as {}", + ReadWriteIsolationDynamicRoutingDataSource.class.getSimpleName()); return dynamicDataSource; } @Bean - public PlatformTransactionManager platformTransactionManager(DynamicRoutingDataSource dynamicRoutingDataSource) { - return new DataSourceTransactionManager(dynamicRoutingDataSource); + public PlatformTransactionManager platformTransactionManager(ReadWriteIsolationDynamicRoutingDataSource readWriteIsolationDynamicRoutingDataSource) { + return new DataSourceTransactionManager(readWriteIsolationDynamicRoutingDataSource); } } diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceContextHolder.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceContextHolder.java index 055f0510..cd3f1955 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceContextHolder.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceContextHolder.java @@ -12,44 +12,44 @@ * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 6/27/2021 12:07 AM **/ @Slf4j -public class DataSourceContextHolder { - private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); +public final class DataSourceContextHolder { + protected static final ThreadLocal CONTEXT_HOLDER = + ThreadLocal.withInitial(() -> DataSourceEnum.MASTER); private static final AtomicInteger COUNTER = new AtomicInteger(-1); private static final int MAX_COUNT = 9999; - public static DataSourceTypeEnum get() { - return Optional.ofNullable(CONTEXT_HOLDER.get()).orElse(DataSourceTypeEnum.MASTER); + private DataSourceContextHolder() { } - public static void set(DataSourceTypeEnum dbType) { + public static DataSourceEnum get() { + return Optional.ofNullable(CONTEXT_HOLDER.get()).orElse(DataSourceEnum.MASTER); + } + + private static void set(DataSourceEnum dbType) { CONTEXT_HOLDER.set(dbType); } - static void master() { - set(DataSourceTypeEnum.MASTER); + public static void master() { + set(DataSourceEnum.MASTER); if (log.isDebugEnabled()) { - log.debug("Current data source -> {}", DataSourceTypeEnum.MASTER); + log.debug("Current data source -> {}", DataSourceEnum.MASTER); } } - static void slave() { - // Simple load-balance for more slave clusters + public static void slave() { + // Simple load-balance for more slave clusters, assumed we got 2 replicas val index = COUNTER.getAndIncrement() % 2; if (COUNTER.get() > MAX_COUNT) { COUNTER.set(-1); } - // if (index == 0) { - // set(DataSourceTypeEnum.SLAVE1); - // }else { - // set(DataSourceTypeEnum.SLAVE2); - // } - set(DataSourceTypeEnum.SLAVE1); + // if replica data sources are more then 1, the data source needs load balance by index + set(DataSourceEnum.SLAVE1); if (log.isDebugEnabled()) { - log.debug("Current data source -> {}, index: {}", DataSourceTypeEnum.MASTER, index); + log.debug("Current data source -> {}, index: {}", DataSourceEnum.MASTER, index); } } - static void clear() { + public static void clear() { CONTEXT_HOLDER.remove(); if (log.isDebugEnabled()) { log.debug("Cleared CONTEXT_HOLDER"); diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceEnum.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceEnum.java new file mode 100644 index 00000000..8422fdd9 --- /dev/null +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceEnum.java @@ -0,0 +1,31 @@ +package com.jmsoftware.maf.springcloudstarter.database; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +/** + * Description: DataSourceEnum, change description here. + * + * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 6/27/2021 12:03 AM + **/ +@Getter +@ToString +@RequiredArgsConstructor +public enum DataSourceEnum { + /** + * Master + */ + MASTER("master"), + /** + * Slave 1 + */ + SLAVE1("slave1"), + /** + * Quartz + */ + QUARTZ("quartz"), + ; + + private final String dataSourceName; +} diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceTypeEnum.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceTypeEnum.java deleted file mode 100644 index 17088808..00000000 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DataSourceTypeEnum.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.jmsoftware.maf.springcloudstarter.database; - -/** - * Description: DataSourceTypeEnum, change description here. - * - * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 6/27/2021 12:03 AM - **/ -public enum DataSourceTypeEnum { - /** - * Master - */ - MASTER, - /** - * Slave 1 - */ - SLAVE1 -} diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicDataSourceInterceptor.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicDataSourceInterceptor.java index 4fbf44b2..7bbbe455 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicDataSourceInterceptor.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicDataSourceInterceptor.java @@ -32,12 +32,6 @@ }) }) public class DynamicDataSourceInterceptor implements Interceptor { - /** - * The constant WRITE_OPERATION_SQL_REGEX. Preserved this constant for future use. - */ - @SuppressWarnings("unused") - private static final String WRITE_OPERATION_SQL_REGEX = ".*INSERT.*|.*UPDATE.*|.*DELETE.*"; - @Override public Object intercept(Invocation invocation) throws Throwable { // Check if the transaction synchronization is active. It it was, would use MASTER data source @@ -68,8 +62,12 @@ public Object intercept(Invocation invocation) throws Throwable { } log.warn("SQL statement [{}], SqlCommandType: [{}], using 🐬 [{}] data source", mappedStatement.getId(), mappedStatement.getSqlCommandType().name(), DataSourceContextHolder.get()); - val result = invocation.proceed(); - DataSourceContextHolder.clear(); + Object result; + try { + result = invocation.proceed(); + } finally { + DataSourceContextHolder.clear(); + } return result; } diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicRoutingDataSource.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/ReadWriteIsolationDynamicRoutingDataSource.java similarity index 66% rename from spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicRoutingDataSource.java rename to spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/ReadWriteIsolationDynamicRoutingDataSource.java index 89b5b4fb..7f0854c4 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/DynamicRoutingDataSource.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/database/ReadWriteIsolationDynamicRoutingDataSource.java @@ -3,11 +3,11 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** - * Description: DynamicDataSource, change description here. + * Description: ReadWriteIsolationDynamicRoutingDataSource, change description here. * * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 6/27/2021 12:06 AM **/ -public class DynamicRoutingDataSource extends AbstractRoutingDataSource { +public class ReadWriteIsolationDynamicRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.get();