百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

mybatis升级为mybatis-plus踩到的坑

yuyutoo 2024-10-12 00:03 3 浏览 0 评论

前言

最近使用RuoYi-Vue来做后台管理脚手架。RuoYi-Vue 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源。其官方文档如下

http://doc.ruoyi.vip/

感兴趣的朋友,可以点链接查看。这个平台目前的orm框架是mybatis,而项目组的orm框架是mybatis-plus。为了统一技术栈,项目组就决定把若依的orm框架升级为mybatis-plus。因为之前就有过把mybatis升级为mybatis-plus的经验,就感觉这个升级是很简单。但是在改造后,运行程序却报了形如下异常

Invalid bound statement (not found): com.lybgeek.admin.file.mapper.FileMapper.insert

排查

从异常的字面意思是说,FIleMapper中的insert方法没有绑定。查看FileMapper.xml配置,确实没有发现绑定insert这个sql语句块。那是否加上insert的sql语句块,就能解决问题?加上确实是能解决问题。

但如果用过mybatis-plus的朋友,应该会知道,mybatis-plus中BaseMapper已经帮我们封装好了一系列的单表增删改查,我们无需写配置,就可以实现单表增删改查。所以在xml配置insert是治标不治本。

那要如何排查呢?

1、方向一:是否是包冲突引起?

利用maven helper插件包冲突



从图可以看出不是包冲突引起的。

注: 因为之前吃过包冲突的亏,因此在把若依的orm改成mybatis-plus之前,就已经去除跟mybatis相关的 jar冲突了

方向二:是不是引入不同类包的BaseMapper

我们引入的必须是

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

而不是

import com.baomidou.mybatisplus.mapper.BaseMapper;

不过出现这个问题,通常也是引入不同版本的mybatis-plus jar才会出现。如果你是只用3+以上版本,他引入就只有

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

方向三:通用方法(断点调试)

其实代码排查最怕就是异常栈被吃了,如果有异常信息,排查方向相对比较好找。比如这个异常,其异常栈信息为

Caused by: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.lybgeek.admin.file.mapper.FileMapper.insert
	at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235)
	at org.apache.ibatis.binding.MapperMethod.<init>(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

进行设置。然后继续排查,就会发现

com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

这个自动装配类。里面有这么一段代码

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        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<? extends LanguageDriver> 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 此处必为非 NULL
        GlobalConfig 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 到 MybatisSqlSessionFactoryBean
        factory.setGlobalConfig(globalConfig);
        return factory.getObject();
    }

作者在注释上都写了,要用

 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean

于是查看若依代码,发现在若依中的mybatis配置类中有配置如下代码片段

 @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
    {
        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);

        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        return sessionFactory.getObject();
    }

从MybatisPlusAutoConfiguration的源码中,我们可以得知,当项目已经有配置SqlSessionFactory。mybatis-plus将不会自动帮我们注入SqlSessionFactory,而使用我们自己定义的SqlSessionFactory。而若依项目配置的SqlSessionFactory不是MybatisSqlSessionFactoryBean

修复

1、方法一

把mybatis的SqlSessionFactoryBean替换成mybatis-plus的MybatisSqlSessionFactoryBean

2、方法二

去掉项目中sqlSessionFactory。这样mybatis-plus就会自动帮我们注入sqlSessionFactory

总结

可能有朋友会觉得遇到异常问题,直接通过搜索引擎找答案不就可以了。这确实是一个挺好的方法,但有时候可能搜索半天都没找到答案,我们就可以通过异常信息栈、以及调试线程栈,就可以得出一些比较有用的信息。出现异常并不可怕,可怕的是出了问题,异常日志信息被吞,都不知道从何排查。最后安利一下若依这个脚手架,管理后台开发神器,谁用谁知道

相关推荐

jQuery VS AngularJS 你更钟爱哪个?

在这一次的Web开发教程中,我会尽力解答有关于jQuery和AngularJS的两个非常常见的问题,即jQuery和AngularJS之间的区别是什么?也就是说jQueryVSAngularJS?...

Jquery实时校验,指定长度的「负小数」,小数位未满末尾补0

在可以输入【负小数】的输入框获取到焦点时,移除千位分隔符,在输入数据时,实时校验输入内容是否正确,失去焦点后,添加千位分隔符格式化数字。同时小数位未满时末尾补0。HTML代码...

如何在pbootCMS前台调用自定义表单?pbootCMS自定义调用代码示例

要在pbootCMS前台调用自定义表单,您需要在后台创建表单并为其添加字段,然后在前台模板文件中添加相关代码,如提交按钮和表单验证代码。您还可以自定义表单数据的存储位置、添加文件上传字段、日期选择器、...

编程技巧:Jquery实时验证,指定长度的「负小数」

为了保障【负小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【负小数】的方法。HTML代码<inputtype="text"class="forc...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

编程技巧:Jquery实时验证,指定长度的「正小数」

为了保障【正小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【正小数】的方法。HTML做成方法<inputtype="text"class="fo...

jquery.validate检查数组全部验证

问题:html中有多个name[],每个参数都要进行验证是否为空,这个时候直接用required:true话,不能全部验证,只要这个数组中有一个有值就可以通过的。解决方法使用addmethod...

Vue进阶(幺叁肆):npm查看包版本信息

第一种方式npmviewjqueryversions这种方式可以查看npm服务器上所有的...

layui中使用lay-verify进行条件校验

一、layui的校验很简单,主要有以下步骤:1.在form表单内加上class="layui-form"2.在提交按钮上加上lay-submit3.在想要校验的标签,加上lay-...

jQuery是什么?如何使用? jquery是什么功能组件

jQuery于2006年1月由JohnResig在BarCampNYC首次发布。它目前由TimmyWilson领导,并由一组开发人员维护。jQuery是一个JavaScript库,它简化了客户...

django框架的表单form的理解和用法-9

表单呈现...

jquery对上传文件的检测判断 jquery实现文件上传

总体思路:在前端使用jquery对上传文件做部分初步的判断,验证通过的文件利用ajaxFileUpload上传到服务器端,并将文件的存储路径保存到数据库。<asp:FileUploadI...

Nodejs之MEAN栈开发(四)-- form验证及图片上传

这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R...

大数据开发基础之JAVA jquery 大数据java实战

上一篇我们讲解了JAVAscript的基础知识、特点及基本语法以及组成及基本用途,本期就给大家带来了JAVAweb的第二个知识点jquery,大数据开发基础之JAVAjquery,这是本篇文章的主要...

推荐四个开源的jQuery可视化表单设计器

jquery开源在线表单拖拉设计器formBuilder(推荐)jQueryformBuilder是一个开源的WEB在线html表单设计器,开发人员可以通过拖拉实现一个可视化的表单。支持表单常用控件...

取消回复欢迎 发表评论: