震惊: MyBatis使用时: integer返回Long型的坑

背景

NEYdb.jpeg

最近基于MyBatis(3.4.5)写了几个通用抽象类用以继承, 为了更通用些, 参数使用了泛型.
大致如下

抽象基类-BaseEntity

@Data
public abstract class BaseEntity<ID> implements Serializable {
    private static final long serialVersionUID = 1L;
    protected ID id;
}

抽象基类-CommonEntity

@Data
public abstract class CommonEntity<ID> extends BaseEntity<ID> {
    private static final long serialVersionUID = 1L;
    //省略其他通用属性
}

抽象基类-DataEntity

@Data
public abstract class DataEntity<ID> extends CommonEntity<ID> {
    private static final long serialVersionUID = 1L;
    //省略其他通用属性
}

然后我们有个表

CREATE TABLE `person` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

对应的实体Person, 继承了以上3个类
Person

@Data
public class Person extends DataEntity<Integer> {
    //private Integer Id; 无需显式声明, 继承自基类
    private String name;
    private Integer age;
}

样使用, 按道理没什么问题, 咸鱼就写了几个查询, 结果一运行

Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: java.lang.Long cannot be cast to java.lang.Integer; nested exception is com.fasterxml.jackson.databind.JsonMappingException: java.lang.Long cannot be cast to java.lang.Integer (through reference chain: java.util.ArrayList[0]->com.mrcoder.sbmannotations.domain.Person["id"])]

总儿言之,Long型无法转为Integer

当时 ,咸鱼就纳闷了, 不敢置信的从数据库定义到实体的属性统统检查了一遍, 从头到尾都没有发现有Long的定义, 那么这个Long从何而来? 又怎么会报这样的错??

带着疑惑, 咸鱼开始了Debug之旅.

究竟哪里开始报的错?

从错误的提示上, 我们根本没法找到哪一步开始错的, 没法子, 断点大法走起.


image.png

断点看出, sql查询出来id值的类型就是Long了.
这就诡异了,根据上面继承结构, Person这个类Id明明应该是Integer类型才对.

难道getPersonById方法有问题?

但我们的getPersonById方法实现很简单,就是直接mybatis执行了查询


image.png

无奈之下, 咸鱼尝试了各种方式(折腾), 发现直接显示在Person类声明

private Integer id;

就不会报错.

难道MyBatis结果集封装时对泛型类型支持有问题??

为了搞清这个问题, 不得不去扒一扒MyBatis的结果集封装实现了.

MyBatis 结果集封装

翻了翻MyBatis源码, 很快找到了


image.png

显然, ResultSetHandler就是专门处理结果集封装的接口类, IDEA跳转实现, 发现DefaultResultSetHandler是它的唯一实现类.

接下来, 我们继续断点大法来验证
直接在handleResultSets断点


image.png

发现跳转到DefaultResultSetHandler.handleResultSets


image.png

继续往下,发现到了DefaultResultSetHandler.getFirstResultSet
image.png

继续到了ResultSetWrapper.ResultSetWrapper(ResultSet rs, Configuration configuration) ,在这个构造方法里我们看到了希望
image.png

循环里就是在对每个字段进行类型、值的填充.
通过断点,我们验证了在这一步就赋值类型错误的事实.


image.png

那么问题来了, 为什么此处赋给id的class是Long?

仔细分析代码

final ResultSetMetaData metaData = rs.getMetaData();

这段获取数据库中的源数据,包含类型、值等信息, 接下来在一个for循环里把源数据进行了处理赋值给实体.
其中,以下这段完成了class的赋值

classNames.add(metaData.getColumnClassName(i));

我们继续断点进入metaData.getColumnClassName(i)方法


image.png

此时f.getMysqlType()拿到了数据库中id的声明类型为“INT UNSIGNED”,所以直接走到了switch的default分支


image.png

也就是f.getMysqlType().getClassName(),此时去MysqlType的枚举中获取className, 发现
image.png

到了这一步, 已经真相大白了!

根本原因

其实, 踩坑的的原因有两点.

  • 我们使用了泛型抽象基类去指定了ID的类型
  • 我们的数据库ID字段设置的类型为无符号的Int型

或许是Mybatis的“锅”, 又或者是表设计的问题, 总之, 避开以上任意一点, 就不会踩到此坑.

请关注我的订阅号

订阅号.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容