【方向盘】MyBatis封装结果集时,Integer类型的id字段被赋值成了Long类型---读源码找原因(下)

简介: 【方向盘】MyBatis封装结果集时,Integer类型的id字段被赋值成了Long类型---读源码找原因(下)

看看我们关心的id属性:


image.png


oh my god。元数据里面保存的根本就不是我们以为的setId(Integer id)这种,而是保留有父类自己的东西。所以我们自然就好理解了,为什么set进去一个BigInteger值竟然也不抱错的原因了(它也继承了Number类)。


到此,我们就算把出现这种现象的原因完全给弄明白了。


but,but,but。这其实并没有彻底的让我“心服口服”,至少有两大问题一直困扰着我,没有找到根本原因。


疑问提出


(此处暂时只提出两个问题做出解答,更加详细的,可以关注后续我的撸管MyBatis源码专题)


1、为何getValue匹配类型转换器的时候,找到的是UnknownTypeHandler?


(本问题此处大概讲一下,更加详细的,MyBatis的类型转换器模式,完全需要拉一个专题出来讲解)


MyBatis内部注册和维护了几乎所有的类型转换器,所以我们平时使用的时候根本就不用管,它自动就能跟我们匹配上,转换成我们需要的结果。在初始化的时候,有个转换器注册类:TypeHandlerRegistry:(列出部分)


image.png


我们会发现3.4版本的MyBatis对 JSR 310标准的日期时间也提供了支持

顺带我们可以看一下,框架升级给我们带来的优雅体验:


image.png


我们发现3.4.6版本处理起来,就优雅很多,大赞。


UnknownTypeHandler对应的类型:


register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);


我们会发现,它对应的都是Object类型。


MyBatis在进行初始化的时候,会把所有的xml文件里的ResultMap进行注册,并且提供全局访问。而当注册到此处的继承情况的时候,在获取xml继承的id类型的时候,因为是继承的,所以拿不到实际类型,从而注册不到对应的处理器,最终只能交给UnknownTypeHandler处理


下面一个简单的例子,大家可以感受一下MyBatis为啥注册时候找不到了:


 public static void main(String[] args) throws NoSuchFieldException {
        //Field id = Son.class.getField("id");
        //System.out.println(id); //若id是从父类继承来的,传入的泛型  java.lang.Number com.sayabc.boot2demo1.api.TestController$BaseEntity.id
        //System.out.println(id); //id是本类自己的属性 public java.lang.Integer com.sayabc.boot2demo1.api.TestController$Son.id
        //另外一种方式(属性都是private的都ok)
        Field id = Son.class.getSuperclass().getDeclaredField("id");
        System.out.println(id); //我们会发现获取的SuperClass的  类型直接是java.lang.Number 根本没得商量
    }
    class Son extends BaseEntity<Integer> {
        //public Integer id;
        private String name;
    }
    //此处为了方便反射 属性用public的
    class BaseEntity<PK extends Number> {
        public PK id;
    }


我们能够得出结论。当属性是从父类继承过来的,反射去获取这个字段的类型,它的类型是父类类型。(本例如果没有继承自Number,那返回的就是Object类型)


2、为何刚看到的元数据metaClass对象保存的是父类的setId方法呢?作何考虑?这个值又是什么时候被赋值放进去的呢?


这几个问题其实相对来说比较简单些,如果熟悉流行开源框架的这方面的设计思想,发现都是通的,大家都这么“玩”。因此这个问题我这里就不做解答了,留给读者自己思考一番吧


MyBatis结果集如果是Map遇上泛型的话,也是可能遇上同样问题的。


说到最后


  框架能极大提高我们的开发效率,甚至我们可以基于开源本身定制出更符合我们业务的框架。


一件事本身的复杂度不会减少,它只是从一个地方转移到了另外一个地方而已,总的复杂度是恒定不变的,这是一个定理。


(比如这次的撸源码调试找问题就非常耗时,从开始到搞明白花了整4个小时左右,耗时的原因关键是MyBatis自己存在我上面指出的软病,加大了定位问题的难度)



相关文章
|
1月前
|
SQL Java 数据库连接
MyBatis的配置文件中定义类型别名(type aliases)的技巧。
类型别名提供了一种便捷的方式来引用复杂的全限定类名。通过使用 `<package>`标签进行自动扫描或使用 `<typeAlias>`标签手动指定,可以在整个MyBatis配置中提高清晰度和维护性。无论是简化mapper文件中的配置,还是提高整体的配置可读性,类型别名都是一个非常有用的配置工具。
105 0
|
10月前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
1620 5
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
10月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
116 3
|
10月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
215 2
|
11月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
310 10
|
10月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
93 0
|
11月前
|
SQL Java 数据库连接
Mybatis中传入不同类型的值处理方案
这篇文章讲述了在Mybatis中如何处理传入不同类型参数的情况,包括单个值、列表及Map等,并提供了相应的XML映射和Java代码示例。
394 0
|
前端开发 Java 数据库
Java系列之 Long类型返回前端精度丢失
这篇文章讨论了Java后端实体类中Long类型数据在传递给前端时出现的精度丢失问题,并提供了通过在实体类字段上添加`@JsonSerialize(using = ToStringSerializer.class)`注解来确保精度的解决方法。
|
安全 Java 编译器
long类型在32位操作系统上的安全问题
long类型在32位操作系统上的安全问题
337 1
springboot解决jackson序列化Long类型精度失效问题
springboot解决jackson序列化Long类型精度失效问题
330 0