Heim >Java >javaLernprogramm >Wie SpringBoot das dynamische Umschalten mehrerer Datenquellen basierend auf AbstractRoutingDataSource implementiert
Im Produktionsgeschäft führen einige Aufgaben Langzeitabfragevorgänge aus. Wenn die Echtzeitanforderungen nicht hoch sind, hoffen wir, diese Abfrage-SQLs zu trennen und zur Slave-Datenbank zu wechseln abzufragen, um den Anwendungsdruck auf die Hauptdatenbank zu verringern.
Eine Lösung besteht darin, mehrere Datenquellen in der Konfigurationsdatei zu konfigurieren und dann die Datenquelle und die Mapper-bezogene Scankonfiguration über die Konfigurationsklasse abzurufen. Verschiedene Datenquellen konfigurieren unterschiedliche Mapper-Scanpositionen und dann Fügen Sie die von Ihnen benötigte Datenquelle in die Mapper-Schnittstelle ein. Diese Methode ist relativ einfach. Die Funktion besteht darin, Datenquellen durch Mapper-Scan-Standorte zu unterscheiden.
Eine praktikable Lösung besteht darin, im Voraus eine Standarddatenquelle festzulegen, mehrere andere Datenquellen gleichzeitig zu definieren und aop zum Implementieren von Anmerkungen zum Wechseln der Datenquellen zu verwenden. Das Erben der AbstractRoutingDataSource-Klasse ist der Schlüssel zur Implementierung dieser Lösung. Dies ist der Schwerpunkt dieses Artikels.
Die Kernlogik des dynamischen Umschaltens mehrerer Datenquellen von AbstractRoutingDataSource lautet: Wenn das Programm ausgeführt wird, wird die Datenquelle über AbstractRoutingDataSource dynamisch in das Programm eingebunden . Datenquellen flexibel wechseln.
Durch das dynamische Umschalten mehrerer Datenquellen basierend auf AbstractRoutingDataSource kann eine Trennung von Lesen und Schreiben erreicht werden. Die Logik lautet wie folgt:
/** * Retrieve the current target DataSource. Determines the * {@link #determineCurrentLookupKey() current lookup key}, performs * a lookup in the {@link #setTargetDataSources targetDataSources} map, * falls back to the specified * {@link #setDefaultTargetDataSource default target DataSource} if necessary. * @see #determineCurrentLookupKey() */ protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); 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; } /** * Determine the current lookup key. This will typically be * implemented to check a thread-bound transaction context. * <p>Allows for arbitrary keys. The returned key needs * to match the stored lookup key type, as resolved by the * {@link #resolveSpecifiedLookupKey} method. */ @Nullable protected abstract Object determineCurrentLookupKey();
Geben Sie die Datenquelle an, die umgeschaltet werden muss, indem Sie die abstrakte Methode discoverCurrentLookupKey
Das Beispiel basiert hauptsächlich auf #🎜🎜 #
com.alibaba.druid;tk.mybatisDefinieren Sie eine Klasse zum Zuordnen von Datenquellen. Verwenden Sie TheadLocal, um das Flag (den Schlüssel) zu speichern, welche Datenquelle jeder Thread auswählt Slave Datenbank-Slave (weggelassen). Die Konfiguration der Datenquelle kann vereinfacht werden.@Slf4j public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static List<String> dataSourceIds = new ArrayList<String>(); public static void setDataSourceType(String dataSourceType) { log.info("设置当前数据源为{}",dataSourceType); contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get() ; } public static void clearDataSourceType() { contextHolder.remove(); } public static boolean containsDataSource(String dataSourceId){ log.info("list = {},dataId={}", JSON.toJSON(dataSourceIds),dataSourceId); return dataSourceIds.contains(dataSourceId); } }Achten Sie auf den Bean-Namen der Master-Slave-Datenbank. 🎜🎜#targetDataSources speichert Daten. Das k-v-Paar der Quelle. defaultTargetDataSource speichert die Standarddatenquelle. Konfigurieren des Transaktionsmanagers und der SqlSessionFactoryBean#🎜 🎜 #
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
Definieren Sie eine Annotation, um die Geschäftslogik der Datenquelle
@Configuration @tk.mybatis.spring.annotation.MapperScan(value = {"com.server.dal.dao"}) @ConditionalOnProperty(name = "java.druid.datasource.master.url") public class JavaDruidDataSourceConfiguration { private static final Logger logger = LoggerFactory.getLogger(JavaDruidDataSourceConfiguration.class); @Resource private JavaDruidDataSourceProperties druidDataSourceProperties; @Primary @Bean(name = "masterDataSource", initMethod = "init", destroyMethod = "close") @ConditionalOnMissingBean(name = "masterDataSource") public DruidDataSource javaReadDruidDataSource() { DruidDataSource result = new DruidDataSource(); try { // result.setName(druidDataSourceProperties.getName()); result.setUrl(druidDataSourceProperties.getUrl()); result.setUsername(druidDataSourceProperties.getUsername()); result.setPassword(druidDataSourceProperties.getPassword()); result.setConnectionProperties( "config.decrypt=false;config.decrypt.key=" + druidDataSourceProperties.getPwdPublicKey()); result.setFilters("config"); result.setMaxActive(druidDataSourceProperties.getMaxActive()); result.setInitialSize(druidDataSourceProperties.getInitialSize()); result.setMaxWait(druidDataSourceProperties.getMaxWait()); result.setMinIdle(druidDataSourceProperties.getMinIdle()); result.setTimeBetweenEvictionRunsMillis(druidDataSourceProperties.getTimeBetweenEvictionRunsMillis()); result.setMinEvictableIdleTimeMillis(druidDataSourceProperties.getMinEvictableIdleTimeMillis()); result.setValidationQuery(druidDataSourceProperties.getValidationQuery()); result.setTestWhileIdle(druidDataSourceProperties.isTestWhileIdle()); result.setTestOnBorrow(druidDataSourceProperties.isTestOnBorrow()); result.setTestOnReturn(druidDataSourceProperties.isTestOnReturn()); result.setPoolPreparedStatements(druidDataSourceProperties.isPoolPreparedStatements()); result.setMaxOpenPreparedStatements(druidDataSourceProperties.getMaxOpenPreparedStatements()); if (druidDataSourceProperties.isEnableMonitor()) { StatFilter filter = new StatFilter(); filter.setLogSlowSql(druidDataSourceProperties.isLogSlowSql()); filter.setMergeSql(druidDataSourceProperties.isMergeSql()); filter.setSlowSqlMillis(druidDataSourceProperties.getSlowSqlMillis()); List<Filter> list = new ArrayList<>(); list.add(filter); result.setProxyFilters(list); } } catch (Exception e) { logger.error("数据源加载失败:", e); } finally { result.close(); } return result; } }
Aspekt anzugeben. Achten Sie darauf, die Reihenfolge anzugeben, um die Ausführung sicherzustellen, bevor Sie mit der Transaktion beginnen.
@Configuration public class DynamicDataSourceConfig { private static final String MAPPER_LOCATION = "classpath*:sqlmap/dao/*Mapper.xml"; @Bean(name = "dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DruidDataSource masterDataSource, @Qualifier("slaveDataSource") DruidDataSource slaveDataSource) { Map<Object, Object> targetDataSource = new HashMap<>(); DynamicDataSourceContextHolder.dataSourceIds.add("masterDataSource"); targetDataSource.put("masterDataSource", masterDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("slaveDataSource"); targetDataSource.put("slaveDataSource", slaveDataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSource); dataSource.setDefaultTargetDataSource(masterDataSource); return dataSource; } @Primary @Bean(name = "javaTransactionManager") @ConditionalOnMissingBean(name = "javaTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DynamicDataSource druidDataSource) { return new DataSourceTransactionManager(druidDataSource); } @Bean(name = "sqlSessionFactoryBean") public SqlSessionFactoryBean myGetSqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { sqlSessionFactoryBean.setMapperLocations(resolver.getResources(MAPPER_LOCATION)); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } }
pom.xml und application.yml werden oben weggelassen
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
Wenn unterschiedliche Ergebnisse zurückgegeben werden, ist es erfolgreich!
Das obige ist der detaillierte Inhalt vonWie SpringBoot das dynamische Umschalten mehrerer Datenquellen basierend auf AbstractRoutingDataSource implementiert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!