日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

谈一谈Spring-Mybatis在多数据源配置上的坑

發(fā)布時(shí)間:2025/3/18 javascript 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谈一谈Spring-Mybatis在多数据源配置上的坑 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
  • 蘇格團(tuán)隊(duì)
  • 作者:JayceKon
  • 交流QQ群:855833773
  • 歡迎加入我們的團(tuán)隊(duì),微信聯(lián)系方式:foreverpx_cjl

概述

先聊一聊業(yè)務(wù)背景,隨著系統(tǒng)服務(wù)的不斷開發(fā),我們的系統(tǒng)會(huì)充斥著各種個(gè)樣的業(yè)務(wù).這種時(shí)候,我們應(yīng)該要開始考慮一下如何將系統(tǒng)的粒度細(xì)化.舉個(gè)常見的例子: 電商系統(tǒng)可以拆分為 商品模塊,訂單模塊,地址模塊等等.這些模塊都可以獨(dú)立抽取出來(lái),形成一個(gè)單獨(dú)的服務(wù).這就會(huì)涉及到各個(gè)模塊之間的通信問題,一些簡(jiǎn)單的服務(wù),我們可以通過(guò) rpc 接口 直接進(jìn)行通信,但是有些服務(wù)卻不適用這種模式.本文主要講一下在多數(shù)據(jù)源路上遇到的一些坑.

多數(shù)據(jù)源

項(xiàng)目結(jié)構(gòu)

源碼地址: github.com/jaycekon/Sp…

配置文件: DataSourceConfig

@Bean(name = "masterDataSource")@Qualifier("masterDataSource")@ConfigurationProperties(prefix = "spring.datasource")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "slaveDataSource")@Qualifier("slaveDataSource")@ConfigurationProperties(prefix = "spring.datasource.db2")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}@Bean@Primarypublic DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource master,@Qualifier("slaveDataSource") DataSource slave) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DatabaseType.db1, master);targetDataSources.put(DatabaseType.db2, slave);DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSources);// 該方法是AbstractRoutingDataSource的方法dataSource.setDefaultTargetDataSource(master);// 默認(rèn)的datasource設(shè)置為myTestDbDataSourcereturn dataSource;}@Beanpublic SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource,@Qualifier("slaveDataSource") DataSource myTestDb2DataSource) throws Exception {SqlSessionFactoryBean fb = new SqlSessionFactoryBean();fb.setDataSource(this.dataSource(myTestDbDataSource, myTestDb2DataSource));fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));return fb.getObject();} 復(fù)制代碼

項(xiàng)目創(chuàng)建流程可以參: 《Spring-Mybatis 讀寫分離》

數(shù)據(jù)庫(kù)

test_1:

CREATE TABLE `school` (`id` int(11) NOT NULL AUTO_INCREMENT,`school_name` varchar(255) DEFAULT NULL,`province` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 復(fù)制代碼

test_2:

CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 復(fù)制代碼

1、數(shù)據(jù)庫(kù)鏈接異常

此數(shù)據(jù)庫(kù)鏈接異常,指的是在 切換數(shù)據(jù)源 時(shí),數(shù)據(jù)庫(kù)鏈接異常

啟動(dòng)我們的服務(wù):

說(shuō)明我們的服務(wù)配置是沒有什么問題的,那么所謂的數(shù)據(jù)庫(kù)鏈接異常又是什么回事呢?

Test:

@Autowiredprivate SchoolService schoolService;@Autowiredprivate UserService userService;@Testpublic void addUser() {userService.inserUser("root2","root2");}@Testpublic void addSchool() {schoolService.addSchool("ceshi1", "ceshi1");} 復(fù)制代碼

通過(guò)注解設(shè)置數(shù)據(jù)源:

@Service @DataSource("db2") public class UserService@Service @DataSource("db1") public class SchoolService 復(fù)制代碼

我們創(chuàng)建了一個(gè)測(cè)試類,來(lái)檢測(cè)兩個(gè)數(shù)據(jù)源處理情況

從結(jié)果來(lái)看:

1、schoolService 成功了 (db:test_1)

2、UserService 失敗了( db:test_2)

errorMessage:

org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist ### The error may involve com.jaycekon.mybatis.multi.mapper.UserMapper.insert-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO `user`(`username`, `password`) VALUES ( ?, ?); ### Cause: java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist ; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist 復(fù)制代碼

上述異常,即我們可能會(huì)遇到的第一個(gè)坑: UserService 中的數(shù)據(jù)源鏈接異常

異常分析

1、數(shù)據(jù)源鏈接的是 test_1 說(shuō)明沒有成功切換數(shù)據(jù)源

2、觀察切面方法,監(jiān)聽的是 dataSource

@Before("@annotation(com.jaycekon.mybatis.multi.config.DataSource)") 復(fù)制代碼

3、@DataSource

@Retention(RetentionPolicy.CLASS) @Target({ElementType.TYPE}) public @interface DataSource 復(fù)制代碼

通過(guò)上述注解可以發(fā)現(xiàn),我們注解對(duì)象為 TYPE(類),而在 AspectJ 中的注解監(jiān)聽,只支持方法注解監(jiān)聽,并不能監(jiān)聽類的注解.因此,在上述我們通過(guò)注解整個(gè)類的方式,并不能做到數(shù)據(jù)源動(dòng)態(tài)切換:

@Service @DataSource("db2") public class UserService@Service @DataSource("db1") public class SchoolService 復(fù)制代碼

解決辦法

1、修改 DataSource 為方法注解,對(duì)每個(gè)需要切換數(shù)據(jù)源的方法進(jìn)行監(jiān)聽.該方法 比較.

2、通過(guò)@Pointcut("execution(* com.jaycekon.demo.mapper.*.*(..))") 通過(guò)Pointcut 的形式,可以監(jiān)聽到某個(gè)包下面的所有類,所有方法.這個(gè)方法還行,但是每次如果創(chuàng)建了新的類,有可能需要修改配置.

3、目前采用的方式為,將不同數(shù)據(jù)源的mapper,type-aliases,config 分開 配置方式可參考: 傳送門

修改后目錄(配置文件只需保留兩項(xiàng)即可):

2、Mapper 映射異常

在我們修改新的配置文件后,可以參考下面代碼(db2 類似):

@Configuration @MapperScan(value = "com.jaycekon.mybatis.multi.mapper.db1") @EnableTransactionManagement public class DataSourceConfig {private static final String MAPPER_LOCATION = "mybatis.mapper-locations.db1";@Autowiredprivate Environment env;@Bean(name = "masterDataSource")@Qualifier("masterDataSource")@ConfigurationProperties(prefix = "spring.datasource")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "db1SqlSessionFactory")@Primarypublic SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource) throws Exception {SqlSessionFactoryBean fb = new SqlSessionFactoryBean();fb.setDataSource(myTestDbDataSource);fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty(MAPPER_LOCATION)));return fb.getObject();}@Beanpublic DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource myTestDbDataSource) {return new DataSourceTransactionManager(myTestDbDataSource);} } 復(fù)制代碼

其實(shí)這里的配置文件隱藏了一個(gè)坑,在我們啟動(dòng)編譯時(shí),并不會(huì)出現(xiàn)什么問題,但是當(dāng)我們?cè)L問 (db2) 的時(shí)候,問題就來(lái)了:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.jaycekon.mybatis.multi.mapper.db2.UserMapper.insert 復(fù)制代碼

我們可以看到,db1(school) 的單元測(cè)試沒有問題,但是 db2(user) 卻出了問題.

異常分析

1、Mapper 掃描沒有找到對(duì)應(yīng)的 XML 文件

2、多數(shù)據(jù)源存在多個(gè) SqlSessionFactory ,需要將 Mapper文件綁定到對(duì)應(yīng)的 SqlSessionFactory

3、解決辦法,在掃描 Mapper 時(shí),將其綁定到對(duì)應(yīng)的 SqlSessionFactory :

@MapperScan(value = "com.jaycekon.mybatis.multi.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory") 復(fù)制代碼

在 @MapperScan 中可以看到對(duì)應(yīng)的解釋:

* Specifies which {@code SqlSessionFactory} to use in the case that there is* more than one in the spring context. Usually this is only needed when you* have more than one datasource. 復(fù)制代碼

啟動(dòng)測(cè)試類--pass ,啟動(dòng)程序-- pass

如果你覺得這個(gè)坑到這里就結(jié)束了,你就太小看我了~

2.1 TypeAliases 映射

正常來(lái)說(shuō),我們單元測(cè)試 & 服務(wù)都沒有問題,講道理是能夠正常進(jìn)行接下來(lái)的開發(fā)了.但是,我們?nèi)绻褂玫氖?Spring-Boot 進(jìn)行開發(fā),那我們?cè)诎l(fā)布前就還需要做一個(gè)操作 打包 Jar包 ,隨后用命令行啟動(dòng)服務(wù):

java -jar target/spring-boot-mybatis-multi.jar

And Then,然后就會(huì)出現(xiàn)下述問題:

Failed to parse mapping resource: 'class path resource [mybatis-mappers/db2/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'. Cause: java.lang.ClassNotFoundException: Cannot find class: User復(fù)制代碼

在配置 SqlSessionFactory 我們已經(jīng)設(shè)置了 TypeAliasesPackage 的掃描路徑:

@Bean(name = "db1SqlSessionFactory")@Primarypublic SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource) throws Exception {...fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));...} 復(fù)制代碼

但是他并沒有起任何作用,這是為什么呢?

異常分析

1、別名掃描沒有起作用

2、到Github 查找相關(guān)內(nèi)容,會(huì)發(fā)現(xiàn)有相同的經(jīng)歷: 傳送門

解決辦法

1、不使用別名(不是個(gè)好辦法)

2、在mybatis/spring-boot-starter 這個(gè)項(xiàng)目中,提出了一個(gè)官方的 Demo

我們截取中間比較關(guān)鍵的一部分代碼:

SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dataSource);factory.setVfs(SpringBootVFS.class); 復(fù)制代碼

我們采用方法2 嘗試一下,看看能不能解決問題:

關(guān)于 VFS 的一些解釋:

虛擬文件系統(tǒng)(VFS),用來(lái)讀取服務(wù)器里的資源 復(fù)制代碼

個(gè)人理解為,新創(chuàng)建的 SqlSessionFactory 沒有能夠加載配置文件,導(dǎo)致除 @Primary 外的所有 SqlSessionFactory 都沒辦法加載相關(guān)配置文件.

3、Config 異常

一路配置下來(lái),單元測(cè)試跑通了,服務(wù)啟動(dòng)也成功了,接下來(lái)就是一頓騷操作,各種功能開發(fā)~ 在開發(fā)完成后,進(jìn)入測(cè)試階段.一看數(shù)據(jù)返回,坑爹啊~~

怎么返回了個(gè)空數(shù)據(jù)?

異常分析

1、數(shù)據(jù)有返回,服務(wù)沒有問題

2、schoolName 對(duì)應(yīng) 數(shù)據(jù)庫(kù) school_name,中間轉(zhuǎn)換需要使用駝峰命名轉(zhuǎn)換

駝峰命名轉(zhuǎn)換 mybatis.configuration.map-underscore-to-camel-case 出問題了.

解決辦法

1、添加配置 mybatis.configuration.map-underscore-to-camel-case=true

2、創(chuàng)建 MybatisConfig 配置類(db2 類似):

@Bean@ConfigurationProperties(prefix = "mybatis.configuration")@Scope("prototype")public org.apache.ibatis.session.Configuration globalConfiguration() {return new org.apache.ibatis.session.Configuration();}@Bean(name = "db1SqlSessionFactory")@Primarypublic SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource,org.apache.ibatis.session.Configuration config) throws Exception {...fb.setConfiguration(config);...} 復(fù)制代碼

3、@Scope("prototype") 這里配置類使用的是多實(shí)例作用域,主要是為了解決單例模式會(huì)影響到數(shù)據(jù)源的鏈接.

數(shù)據(jù)庫(kù)連接超時(shí)

當(dāng)你屁顛屁顛的把項(xiàng)目發(fā)布到服務(wù)器,接口調(diào)試都沒有問題.過(guò)了一晚突然發(fā)現(xiàn),服務(wù)掛了,what happen?

{"msg": "\n### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.\n### SQL: ******\n###Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.\n; SQL [];No operations allowed after connection closed.; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.","code": 500 } 復(fù)制代碼

MySQL5.0以后針對(duì)超長(zhǎng)時(shí)間DB連接做了一個(gè)處理,如果一個(gè)DB連接在無(wú)任何操作情況下過(guò)了8個(gè)小時(shí)后(Mysql 服務(wù)器默認(rèn)的“wait_timeout”是8小時(shí)),Mysql會(huì)自動(dòng)把這個(gè)連接關(guān)閉。這就是問題的所在,在連接池中的connections如果空閑超過(guò)8小時(shí),mysql將其斷開,而連接池自己并不知道該connection已經(jīng)失效,如果這時(shí)有 Client請(qǐng)求connection,連接池將該失效的Connection提供給Client,將會(huì)造成上面的異常。 所以配置datasource時(shí)需要配置相應(yīng)的連接池參數(shù),定時(shí)去檢查連接的有效性,定時(shí)清理無(wú)效的連接。引用

解決辦法-完善相關(guān)配置:

spring.datasource.jdbcUrl=jdbc:mysql://localhost:3306/test_1 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.default-auto-commit = false spring.datasource.default-read-only = true spring.datasource.max-idle = 10 spring.datasource.max-wait = 10000 spring.datasource.min-idle = 5 spring.datasource.initial-size = 5 spring.datasource.validation-query = SELECT 1 spring.datasource.test-on-borrow = false spring.datasource.test-while-idle = true spring.datasource.time-between-eviction-runs-millis = 18800spring.datasource.db2.jdbcUrl=jdbc:mysql://localhost:3306/test_2 spring.datasource.db2.username=root spring.datasource.db2.password=123456 spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver spring.datasource.db2.default-auto-commit = false spring.datasource.db2.default-read-only = true spring.datasource.db2.max-idle = 10 spring.datasource.db2.max-wait = 10000 spring.datasource.db2.min-idle = 5 spring.datasource.db2.initial-size = 5 spring.datasource.db2.validation-query = SELECT 1 spring.datasource.db2.test-on-borrow = false spring.datasource.db2.test-while-idle = true spring.datasource.db2.time-between-eviction-runs-millis = 18800復(fù)制代碼

4、事務(wù)異常

由于我們?cè)诙鄶?shù)據(jù)源中,采用了多 sqlSessionFactory 方式,因此在事務(wù)管理這塊,會(huì)出現(xiàn)事務(wù)管理異常相關(guān)問題,有興趣的童鞋可以參考:www.atomikos.com/Main/WebHom… ,推薦一個(gè)整合的 Demo

總結(jié)

Mybatis 多數(shù)據(jù)源配置主要分兩種,一種動(dòng)態(tài)配置數(shù)據(jù)源 & 一種配置多 sqlsessionFactory,本文的一些坑,主要基于 多 sqlSessionFactory. 上述的所有問題,都是在開發(fā)過(guò)程中所遇到,可能各位或多或少有遇到過(guò),希望能給各位相關(guān)幫助.

如對(duì)個(gè)人見解有所異議,歡迎指正.

Demo地址: github.com/jaycekon/Sp…

總結(jié)

以上是生活随笔為你收集整理的谈一谈Spring-Mybatis在多数据源配置上的坑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 粉嫩av在线 | 六月色婷| av免费不卡| 日韩影视一区 | 亚洲乱轮 | 他揉捏她两乳不停呻吟动态图 | 国产不卡视频一区二区三区 | 欧州一区二区三区 | 碧蓝之海动漫在线观看免费高清 | 亚洲第一视频网 | 密桃成熟时在线观看 | 欧洲亚洲天堂 | 成人午夜免费在线观看 | 日本少妇毛茸茸 | 六月丁香综合网 | 精品一卡二卡 | 成人免费视频免费观看 | 日韩av电影手机在线观看 | 免费黄色在线网址 | 国产精品区一区二 | 天天操夜夜欢 | 秋霞在线观看秋 | 可以直接在线观看的av | 国产人成精品 | 丰满人妻一区二区三区53号 | 视频免费1区二区三区 | 97avcc| 精品久久91 | 国产女人18毛片水真多1 | 在线日韩一区二区 | 中国1级毛片 | 国产成人av一区二区三区 | 欧美日日 | 九九在线视频 | 国产人妻精品久久久久野外 | 色原网| 久久精品中文闷骚内射 | 狠狠爱欧美 | 色汉综合 | 日韩永久免费视频 | 亚洲激情av | 国产日韩欧美综合 | 中文字幕亚洲乱码熟女一区二区 | 免费的毛片| 日韩欧美视频在线免费观看 | 97超碰人 | 亚洲国产免费av | 涩涩视频在线看 | www.色香蕉 | 日本韩国欧美一区二区 | 国产成人亚洲综合a∨婷婷 台湾a级片 | 亚洲成人精品 | 五月激情婷婷丁香 | 日本人妻不卡一区二区三区中文字幕 | 二级黄色片| 91av官网| 日韩精品电影在线 | 女人黄色片 | 一区二区不卡 | 欧美精品 在线观看 | 国产精品自拍片 | 可以免费看av | 午夜影院美女 | 成人久久精品 | 免费的三级网站 | 日本高清免费aaaaa大片视频 | 天天搞天天 | 一本一道久久综合 | 欧美日韩中文国产 | 日韩av成人 | av在线不卡免费 | 日日摸天天添天天添破 | 亚洲午夜片 | 国产亚洲一区二区三区 | 97在线免费公开视频 | 六月色丁香 | 露出调教羞耻91九色 | 国产精品久久久久影院色老大 | www亚洲精品 | 操久久| 一区二区传媒有限公司 | 日韩精品在线观看AV | 91导航| 国产热视频 | 激情五月视频 | 免费av在线网 | 一区二区成人免费视频 | 极品尤物一区二区三区 | 国产破处视频 | 伊人久久五月 | 午夜影院免费版 | 色视频国产 | 日韩国产欧美一区 | 日韩精品一区二区亚洲av性色 | 欧美久久久久久久久久久久久久 | 色福利网 | 日本女人性视频 | 亚洲一级片在线播放 | 日韩欧美高清一区 |