mybatis plus相同Id与xml配置错误时,mybatis plus解决逻辑

51 篇文章 7 订阅
42 篇文章 5 订阅

前言

处理做项目的问题,其中不乏奇奇怪怪的问题,其中mybatis plus的问题感觉有点隐蔽,有些是运行时出现,有些是运行到具体的逻辑触发,对于应用的状态监控提出了极大的挑战,应用的状态由健康检查接口提供,或者TCP或者HTTP,那么健康检查要怎么写呢,比如K8S,一般使用HTTP GET方式要定时监控POD状态。

准备

准备SpringBoot与mybatis的demo,这个参考SpringBoot官网即可,数据库使用MySQL docker部署,毕竟docker部署方便:Basic Steps for MySQL Server Deployment with Docker

docker run --name mysql_8.0 -e MYSQL_ROOT_PASSWORD=123456i -d -i -p 3306:3306  container-registry.oracle.com/mysql/community-server:8.0

就可以使用client连接上去,然后建库表和demo数据,需要修改非本地登录mysql的能力

更新root登录限制开放

确认OK

造数据

mybatis plus的Id相同

依赖如下

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
    </dependencies>

dao与配置、main类

@SpringBootApplication
@MapperScan(basePackages = "com.feng.boot.mybatis.demo.dao")
public class MybatisMain {

    public static void main(String[] args) {
        SpringApplication.run(MybatisMain.class, args);
    }
}

@Mapper
public interface DemoDao {

    @Select("select * from user")
    List<User> selectUser();

    @Select("select * from user where name = #{name}")
    List<User> selectUser(String name);

}

@RestController
public class DemoController {

    @Lazy
    @Autowired
    private DemoDao demoDao;

    @RequestMapping("/user")
    public List<User> listUser(String name){
        return demoDao.selectUser(name);
    }

SpringBoot数据源配置自行处理,启动后日志

 _ _   |_  _ _|_. ___ _ |    _ 
| | |\/|_)(_| | |_\  |_)||_|_\ 
     /               |         
                        3.3.2 
2024-01-09 21:27:34.043 ERROR 3340 --- [           main] c.b.m.core.MybatisConfiguration          : mapper[com.feng.boot.mybatis.demo.dao.DemoDao.selectUser] is ignored, because it exists, maybe from xml file

但是 启动不会保错,如果是mybatis的starter则启动直接保错

让我们访问http://localhost:8080/user?name=demo

理论上只会有一条结果,然而:

 

2条,什么原因呢,开启trace日志

为啥不是我方法的执行结果,SQL都不对,这其实是mybatis plus留下的坑

 源码分析

启动过程就埋了坑,在

com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder

通过generateResultMapName生成mybatis的Id时,mybatis plus做了定制

    private String generateResultMapName(Method method) {
        Results results = method.getAnnotation(Results.class);
        if (results != null && !results.id().isEmpty()) {
            return type.getName() + "." + results.id();
        }
        StringBuilder suffix = new StringBuilder();
        //参数拼接
        for (Class<?> c : method.getParameterTypes()) {
            suffix.append("-");
            suffix.append(c.getSimpleName());
        }
        //如果没有参数,使用-void
        if (suffix.length() < 1) {
            suffix.append("-void");
        }
        //类名+方法名+参数类型拼接
        return type.getName() + "." + method.getName() + suffix;
    }

所以对于mybatis plus,对于方法重载不存在id冲突的可能性,在

com.baomidou.mybatisplus.core.MybatisConfiguration

 所以出现启动时的error日志,然后直接return了,所以下面的第2条是没有载入mappedstatement

所以SQL永远是第1条,无论我们怎么调用

mybatis-starter

原生的mybatis-starter的org.apache.ibatis.builder.annotation.MapperAnnotationBuilder

实际上id也差不多,也是通过-void方式实现无参数id

 

但是在org.apache.ibatis.builder.MapperBuilderAssistant

会使用不带参数的Id来做校验,在org.apache.ibatis.session.Configuration

而不是像mybatis plus那样直接return了。

解决方法

无非是id冲突,只要安装方法名唯一的情况即可,不使用方法重载

结果OK

mybatis的xml配置错误的情况

如果使用xml配置,那么也会存在出现错误的情况

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.feng.boot.mybatis.demo.dao.DemoDao">
    <!--<select id="selectUser" parameterType="java.lang.String" resultType="com.feng.boot.mybatis.demo.entity.User">
        select * from user where name = #{name}
    </select>-->

    <select id="selectUser" parameterType="java.lang.String" resultMap="com.feng.boot.mybatis.demo.entity.User">
        select * from user where name = #{name}
    </select>

    <select id="selectUsers" resultType="com.feng.boot.mybatis.demo.entity.User">
        select * from user
    </select>

</mapper>

笔者故意写错selectUser的xml,但是笔者在调用

    @RequestMapping("/users")
    public List<User> listUser(){
        return demoDao.selectUsers();
    }

跟第1个xml毫无关系,但是调用报错

java.lang.IllegalArgumentException: Result Maps collection does not contain value for com.feng.boot.mybatis.demo.entity.User
	at org.apache.ibatis.session.Configuration$StrictMap.get(Configuration.java:1031) ~[mybatis-3.5.6.jar:3.5.6]

 其实这个都还好,关键是启动毫无错误,然而执行任意一条正确的SQL却报错了,莫非mybatis绝得还可以抢救一下。而且错误信息可以跟正确执行的逻辑毫无相关,如果启动后仅仅看启动日志,就会埋下极大隐患,需要健康检查覆盖mybatis才行。

源码分析

在mybatis的源码中,org.apache.ibatis.session.Configuration

mybatis的设计是,在任意statement执行前,执行未完成态的statement,执行快速失败,既然知道未完成,为什么启动不报错,也不任何日志提示呢

 根源来自org.apache.ibatis.builder.xml.XMLMapperBuilder

 xml解析失败,并不是报错,也没有日志,直接放在了未完成的statement,这里好歹给个日志啊,估计mybatis是准备支持抢救一下,不对,是支持statement在运行态注入一些数据实现完成态。

总结

mybatis plus这个是直接丢弃相同Id的statement,安装先后顺序,会造成执行过程的误解,不过有日志可以查看,而且mybatis原生的starter是有校验的,直接报错了。mybatis的xml如果写错,那么启动居然不报错,执行任意statement前需要执行未完成的statement,来达到快速失败的情况,这种情况对健康检查提出了新挑战,否则可能出现启动OK,但是mybatis失败的情况。

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值