记一次Mybatis Update遇到的坑
背景
最近遇到一个线上问题,由于接手老项目不久,对代码不熟悉,导致这个坑排查了很久才发现。说他是坑吧,其实也不算坑,只能说是由于前人对这个东西不够了解导致的。
简介
问题是这样的,在A表中,每次业务操作都会根据条件在A表中查询id,如果查询到了,就不执行插入,反之会执行插入,并且返回id,代码如下:
private Integer getOrAddxxxByName(String name) {
Integer id = xxxDao.queryRCChannelId(name);
if (id == null) {
id = xxxDao.addRCChannel(name);
}
return id;
}
这段代码里最后返回的结果就是所谓的id,这个id又会在B表中做关联,乍眼一看,这段代码好像没什么问题,但是线上却产生了奇怪的现象,那就是新用户首次操作了和这段代码有关的功能时,在B表中都会出现一条关联id为1的数据,这就奇怪了,用户每次重新加入的数据,为啥给我返回个1,然后第二次操作又恢复正常了???
于是我点进了dao对应的addRCChannel方法,发现了问题,xml中对应方法实现是这样的
<insert id="addRCChannel">
INSERT channel_info (`channel_name`) VALUES (#{channelName});
</insert>
如果看到这儿你也没发现问题,那只能说明,你也被写代码的老哥带进去了!问题的原因就是因为insert方法每次返回的结果是成功的行数,也就是说,我们成功插入一条,那永远都是返回1,这就和上面方法的逻辑产生了冲突,他想要的是,每次返回成功之后的数据id
解决
文档
这时候自然就应该打开Mybatis的文档了,上面是这么说的:
插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式。
首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就 OK 了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:
可以看到,文档上对这两个字段进行了解释
按照我的理解,useGeneratedKeys这个属性默认为false,设置为true,他就会把插入的这条数据的id设置到keyProperty指定的列中,所以,上面xml只要改成下面这样
<insert id="addRCChannel" useGeneratedKeys="true" keyProperty="id">
INSERT channel_info (`channel_name`) VALUES (#{channelName});
</insert>
这时候,代码修改如下:
private Integer getOrAddxxxByName(String name) {
Integer id = xxxDao.queryRCChannelId(name);
if (id == null) {
Channel channel = new Channel();
channel.setChannelName(name);
xxxDao.addRCChannel(channel);
id = channel.getId();
}
return id;
}
这样,最后返回的结果就是数据的真实id了。
总结
这个问题看似简单,实际上需要对Mybatis很熟悉,至少要阅读过文档,而不是想当然的复制一段代码没有报错就是成功了,细节决定成败,每一个疏忽的小问题,到了线上都是不好的用户体验。