https://www.cnblogs.com/linyb-geek/p/13647327.html
前言
最近使用RuoYi-Vue來做后臺管理腳手架蚕涤。RuoYi-Vue 是一個 Java EE 企業(yè)級快速開發(fā)平臺滔驾,基于經(jīng)典技術(shù)組合(Spring Boot俺抽、Spring Security凑保、MyBatis、Jwt、Vue)增淹,內(nèi)置模塊如:部門管理、角色用戶舶衬、菜單及按鈕授權(quán)埠通、數(shù)據(jù)權(quán)限、系統(tǒng)參數(shù)逛犹、日志管理端辱、代碼生成等。在線定時任務配置虽画;支持集群舞蔽,支持多數(shù)據(jù)源。其官方文檔如下
感興趣的朋友码撰,可以點鏈接查看渗柿。這個平臺目前的orm框架是mybatis,而項目組的orm框架是mybatis-plus脖岛。為了統(tǒng)一技術(shù)棧朵栖,項目組就決定把若依的orm框架升級為mybatis-plus。因為之前就有過把mybatis升級為mybatis-plus的經(jīng)驗柴梆,就感覺這個升級是很簡單陨溅。但是在改造后,運行程序卻報了形如下異常
Invalid boundstatement(not found): com.lybgeek.admin.file.mapper.FileMapper.insert
排查
從異常的字面意思是說绍在,F(xiàn)IleMapper中的insert方法沒有綁定门扇。查看FileMapper.xml配置,確實沒有發(fā)現(xiàn)綁定insert這個sql語句塊偿渡。那是否加上insert的sql語句塊臼寄,就能解決問題?加上確實是能解決問題溜宽。
但如果用過mybatis-plus的朋友吉拳,應該會知道,mybatis-plus中BaseMapper已經(jīng)幫我們封裝好了一系列的單表增刪改查适揉,我們無需寫配置留攒,就可以實現(xiàn)單表增刪改查。所以在xml配置insert是治標不治本涡扼。
那要如何排查呢?
1盟庞、方向一:是否是包沖突引起吃沪?
利用maven helper插件包沖突
從圖可以看出不是包沖突引起的。
注:?因為之前吃過包沖突的虧什猖,因此在把若依的orm改成mybatis-plus之前票彪,就已經(jīng)去除跟mybatis相關(guān)的 jar沖突了
方向二:是不是引入不同類包的BaseMapper
我們引入的必須是
importcom.baomidou.mybatisplus.core.mapper.BaseMapper;
而不是
importcom.baomidou.mybatisplus.mapper.BaseMapper;
不過出現(xiàn)這個問題红淡,通常也是引入不同版本的mybatis-plus jar才會出現(xiàn)。如果你是只用3+以上版本降铸,他引入就只有
importcom.baomidou.mybatisplus.core.mapper.BaseMapper;
方向三:通用方法(斷點調(diào)試)
其實代碼排查最怕就是異常棧被吃了在旱,如果有異常信息,排查方向相對比較好找推掸。比如這個異常桶蝎,其異常棧信息為
Caused by: org.apache.ibatis.binding.BindingException:Invalid boundstatement(not found): com.lybgeek.admin.file.mapper.FileMapper.insertat org.apache.ibatis.binding.MapperMethod$SqlCommand.(MapperMethod.java:235)at org.apache.ibatis.binding.MapperMethod.(MapperMethod.java:53)at org.apache.ibatis.binding.MapperProxy.lambda$cachedInvoker$0(MapperProxy.java:107)at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)at org.apache.ibatis.binding.MapperProxy.cachedInvoker(MapperProxy.java:94)at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:85)at com.sun.proxy.$Proxy129.insert(Unknown Source)at com.baomidou.mybatisplus.extension.service.IService.save(IService.java:59)at com.baomidou.mybatisplus.extension.service.IService$$FastClassBySpringCGLIB$$f8525d18.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
我們從異常棧信息,我們可以知道這個異常從
org.apache.ibatis.binding.MapperMethod
這個類拋出谅畅,于是我們可以把斷點先設置到這邊登渣。通過源碼我們可以得知org.apache.ibatis.mapping.MappedStatement空了,導致報了如上異常毡泻,而MappedStatement又是由
org.apache.ibatis.session.Configuration
提供胜茧。而Configuration是通過
org.apache.ibatis.session.SqlSessionFactory
進行設置。然后繼續(xù)排查仇味,就會發(fā)現(xiàn)
com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
這個自動裝配類呻顽。里面有這么一段代碼
@Bean@ConditionalOnMissingBeanpublicSqlSessionFactorysqlSessionFactory(DataSource dataSource)throwsException{// TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBeanMybatisSqlSessionFactoryBean factory =newMybatisSqlSessionFactoryBean();? ? ? ? factory.setDataSource(dataSource);? ? ? ? factory.setVfs(SpringBootVFS.class);if(StringUtils.hasText(this.properties.getConfigLocation())) {? ? ? ? ? ? factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));? ? ? ? }? ? ? ? applyConfiguration(factory);if(this.properties.getConfigurationProperties() !=null) {? ? ? ? ? ? factory.setConfigurationProperties(this.properties.getConfigurationProperties());? ? ? ? }if(!ObjectUtils.isEmpty(this.interceptors)) {? ? ? ? ? ? factory.setPlugins(this.interceptors);? ? ? ? }if(this.databaseIdProvider !=null) {? ? ? ? ? ? factory.setDatabaseIdProvider(this.databaseIdProvider);? ? ? ? }if(StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {? ? ? ? ? ? factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());? ? ? ? }if(this.properties.getTypeAliasesSuperType() !=null) {? ? ? ? ? ? factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());? ? ? ? }if(StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {? ? ? ? ? ? factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());? ? ? ? }if(!ObjectUtils.isEmpty(this.typeHandlers)) {? ? ? ? ? ? factory.setTypeHandlers(this.typeHandlers);? ? ? ? }? ? ? ? Resource[] mapperLocations =this.properties.resolveMapperLocations();if(!ObjectUtils.isEmpty(mapperLocations)) {? ? ? ? ? ? factory.setMapperLocations(mapperLocations);? ? ? ? }// TODO 對源碼做了一定的修改(因為源碼適配了老舊的mybatis版本,但我們不需要適配)Class defaultLanguageDriver =this.properties.getDefaultScriptingLanguageDriver();if(!ObjectUtils.isEmpty(this.languageDrivers)) {? ? ? ? ? ? factory.setScriptingLanguageDrivers(this.languageDrivers);? ? ? ? }? ? ? ? Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);// TODO 自定義枚舉包if(StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {? ? ? ? ? ? factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());? ? ? ? }// TODO 此處必為非 NULLGlobalConfig globalConfig =this.properties.getGlobalConfig();// TODO 注入填充器this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);// TODO 注入主鍵生成器this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));// TODO 注入sql注入器this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);// TODO 注入ID生成器this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);// TODO 設置 GlobalConfig 到 MybatisSqlSessionFactoryBeanfactory.setGlobalConfig(globalConfig);returnfactory.getObject();? ? }
作者在注釋上都寫了,要用
MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
于是查看若依代碼丹墨,發(fā)現(xiàn)在若依中的mybatis配置類中有配置如下代碼片段
@BeanpublicSqlSessionFactorysqlSessionFactory(DataSource dataSource)throwsException{? ? ? ? String typeAliasesPackage = env.getProperty("mybatis.type-aliases-package");? ? ? ? String mapperLocations = env.getProperty("mybatis.mapper-locations");? ? ? ? String configLocation = env.getProperty("mybatis.config-location");? ? ? ? typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);? ? ? ? VFS.addImplClass(SpringBootVFS.class);finalSqlSessionFactoryBean sessionFactory =newSqlSessionFactoryBean();? ? ? ? sessionFactory.setDataSource(dataSource);? ? ? ? sessionFactory.setTypeAliasesPackage(typeAliasesPackage);? ? ? ? sessionFactory.setMapperLocations(newPathMatchingResourcePatternResolver().getResources(mapperLocations));? ? ? ? sessionFactory.setConfigLocation(newDefaultResourceLoader().getResource(configLocation));returnsessionFactory.getObject();? ? }
從MybatisPlusAutoConfiguration的源碼中廊遍,我們可以得知,當項目已經(jīng)有配置SqlSessionFactory带到。mybatis-plus將不會自動幫我們注入SqlSessionFactory昧碉,而使用我們自己定義的SqlSessionFactory。而若依項目配置的SqlSessionFactory不是MybatisSqlSessionFactoryBean
修復
1揽惹、方法一
把mybatis的SqlSessionFactoryBean替換成mybatis-plus的MybatisSqlSessionFactoryBean
2被饿、方法二
去掉項目中sqlSessionFactory。這樣mybatis-plus就會自動幫我們注入sqlSessionFactory
總結(jié)
可能有朋友會覺得遇到異常問題搪搏,直接通過搜索引擎找答案不就可以了狭握。這確實是一個挺好的方法,但有時候可能搜索半天都沒找到答案疯溺,我們就可以通過異常信息棧论颅、以及調(diào)試線程棧,就可以得出一些比較有用的信息囱嫩。出現(xiàn)異常并不可怕恃疯,可怕的是出了問題,異常日志信息被吞墨闲,都不知道從何排查今妄。最后安利一下若依這個腳手架,管理后臺開發(fā)神器,誰用誰知道