背景:一個項目中可能存在多數據源的情況,雖然微服務中,一般是單數據源,但是例如后臺管理這些管理接口則不適合使用微服務來 提供接口,所以業務庫也需要共存于后臺管理項目,而后臺管理項目中則有自己本身的一個權限數據庫,則就會存在多數據源的情況。 思路:Spring本身已經有實現數據源切換的功能類,可以實現在項目運行時根據相應key值切換到對應的數據源DataSource上。 我們只需擴展實現即可。 并結合數據源動態切換為需要切換數據源的方法增加注解,從而實現對帶有注解的攔截切換。 問題:事務控制,缺省數據源生效,而切換為第二數據源時,事務的數據源默認采用了缺省的。 網上有說更改切面和事務的執行順序,但是試驗后并未成功。
以下是為動態數據源切換,及缺省事務第二數據源的事務控制的實現方案,以springboot作為基礎框架。
使用druid做數據源監控與管理spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver druid: first: #數據源1 url: jdbc:mysql://127.0.0.01:63885/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 username: demo password: demo rongyuan: #數據源2 url: jdbc:mysql://127.0.0.01:63885/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 username: demo password: demo 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: true wall: config: multi-statement-allow: true構建數據源及注入到動態數據源中
package io.y.common.datasources; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; 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 com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; /** * @title * @author zengzp * @time 2018年7月25日 上午11:22:46 * @Description */ @Configuration // 加上此注解禁用數據源自動配置 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class DynamicDataSourceConfig { @Bean(name="first") @ConfigurationProperties("spring.datasource.druid.first") public DataSource firstDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean(name="rongyuan") @ConfigurationProperties("spring.datasource.druid.rongyuan") public DataSource secondDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dataSource(@Qualifier("first")DataSource firstDataSource, @Qualifier("rongyuan")DataSource secondDataSource) { Map繼承spring的動態實現,及重寫數據源的獲取方法targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceNames.FIRST, firstDataSource); targetDataSources.put(DataSourceNames.SECOND, secondDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
package io.y.common.datasources; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * @title * @author zengzp * @time 2018年7月25日 上午 10:20:31 * @Description */ public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal定義數據源切換注解contextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(new HashMap<>(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(); } }
package io.y.common.datasources.annotation; import java.lang.annotation.*; /** * @title 多數據源注解 * @author zengzp * @time 2018年7月25日 下午14:50:53 * @Description */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String name() default ""; }定義切面,用來攔截帶注解的方法,并在方法執行前實現數據源的切換
package io.y.common.datasources.aspect; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import io.y.common.datasources.DataSourceNames; import io.y.common.datasources.DynamicDataSource; import io.y.common.datasources.annotation.TargetDataSource; /** * @title 多數據源切面處理類 * @author zengzp * @time 2018年7月25日 下午11:56:43 * @Description */ @Aspect @Component @Order(0) public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(io.y.common.datasources.annotation.TargetDataSource)") public void dataSourcePointCut() { } @Before("dataSourcePointCut()") public void around(JoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); TargetDataSource ds = method.getAnnotation(TargetDataSource.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()); } } @AfterReturning("dataSourcePointCut()") public void after(){ DynamicDataSource.clearDataSource(); logger.debug("clean datasource"); } }數據源名稱常量類
package io.y.common.datasources; /** * @title 增加多數據源,在此配置 * @author zengzp * @time 2018年7月25日 下午4:55:20 * @Description */ public interface DataSourceNames { String FIRST = "first"; String SECOND = "rongyuan"; }
第二數據源事務控制處理以上已經完成了動態數據源的切換,只需在Service方法上加上@TargetDataScoure注解并且指定需要切換的數據源名稱,first數據源為缺省數據源。
如果使用@Transactional,缺省數據源的事務正常執行,如果使@TargetDataScoure切換為第二數據源并執行事務時,則數據源切換失敗。
問題分析:
大多數項目只需要一個事務管理器。如果存在多數據源的情況,事務管理器是否會生效,由于spingboot約定大于配置的理念, 默認事務管理器無需我們再聲明定義,而是默認加載時已經指定了其數據源,其數據源則為缺省數據源,如果執行事務時是第二數據源,則 還會以第一數據源做處理,這時則會異常。
定義事務管理器 并指定其對應管理的數據源和聲明name
package io.y.common.datasources; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; /** * @title 多事物管理器配置 * @author zengzp * @time 2018年7月25日 下午4:55:33 * @Description */ @Configuration public class TransactionConfig { public final static String DEFAULT_TX = "defaultTx"; public final static String RONGYUAN_TX = "rongyuanTx"; @Bean(name=TransactionConfig.DEFAULT_TX) public DataSourceTransactionManager transaction(@Qualifier(DataSourceNames.FIRST)DataSource firstDataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(firstDataSource); return dataSourceTransactionManager; } @Bean(name=TransactionConfig.RONGYUAN_TX) public DataSourceTransactionManager rongyuanTransaction(@Qualifier(DataSourceNames.SECOND) DataSource rongyuanDataScoure){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(rongyuanDataScoure); return dataSourceTransactionManager; } }
2.事務管理器使用在@Transactional上指定使用哪個名稱的事務管理器
@Override @Transactional(value=TransactionConfig.RONGYUAN_TX, rollbackFor=Exception.class) @TargetDataSource(name = "rongyuan") public void deleteBatch(Integer[] advertIds) { if (advertIds == null || advertIds.length <= 0) { throw new IllegalArgumentException("參數異常"); } advertDao.deleteBatch(advertIds); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76516.html
摘要:多數據源,一般用于對接多個業務上獨立的數據庫可能異構數據庫。這也就導致異構數據庫的檢查也是類似問題。內容略數據源多數據源,涉及到異構數據庫,必須明確指定,否則的轉換出錯取值內容可參考初始連接數最大連接池數量。 開篇之前,說一句題外話。多數據源和動態數據源的區別。 多數據源,一般用于對接多個業務上獨立的數據庫(可能異構數據庫)。 動態數據源,一般用于大型應用對數據切分。 配置參考 如...
摘要:配置想想,我們需要哪些數據庫要用到,數據庫連接池要用到橋接器要用到,因此要倉庫點我去倉庫中找到搜索這些加進去。 本文旨在用最通俗的語言講述最枯燥的基本知識 最近身邊的程序員掀起了學習springboot的熱潮,說什么學會了springboot在大街上就可以橫著走、什么有了springboot媽媽再也不擔心我的編程了、什么BAT都喜歡的框架...聽得作者那個心癢癢的,于是找了個時間,下載...
摘要:申請連接時執行檢測連接是否有效,做了這個配置會降低性能。作者在版本中使用,通過監控界面發現有緩存命中率記錄,該應該是支持。允許和不允許單條語句返回多個數據集取決于驅動需求使用列標簽代替列名稱。需要驅動器支持。將自動映射所有復雜的結果。 項目github地址:https://github.com/5-Ason/aso... 具體可看 ./db/db-mysql 模塊 本文主要實現的是對...
閱讀 1264·2021-09-23 11:51
閱讀 1369·2021-09-04 16:45
閱讀 626·2019-08-30 15:54
閱讀 2075·2019-08-30 15:52
閱讀 1593·2019-08-30 11:17
閱讀 3098·2019-08-29 13:59
閱讀 2010·2019-08-28 18:09
閱讀 380·2019-08-26 12:15