MyBatis自动转型tinyint(1)为boolean及Integer=0处理成空字符问题
背景
数据表定义:
create table timed_task (
id bigint unsigned auto_increment comment 'PK' primary key,
task_status tinyint(1) default 0 not null comment '任务状态:1启用,2禁用',
mq_switch tinyint(1) default 0 not null comment '是否发送消息至MQ:1发送,0不发送',
isactive tinyint(1) default 1 not null comment '逻辑删除',
inserttime datetime default CURRENT_TIMESTAMP not null comment '插入时间',
insertby varchar(100) null comment '创建人',
updatetime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
updateby varchar(100) null comment '更新人'
) comment '定时任务配置表' collate = utf8mb4_unicode_ci;
其中有三个tinyint(1)
字段。
Java接口定义为:
List<Map> selectListBySelective(Map<String, Object> map);
MyBatis的mapper.xml
文件定义:
<select id="selectListBySelective" parameterType="Map" resultType="Map">
select
tt.id,
ifnull(tt.task_status, 0) as taskStatus,
tt.mq_switch as mqSwitch,
tt.inserttime,
tt.insertby,
date_format(tt.updatetime,'%Y-%m-%d %H:%i:%s') as updatetime,
tt.updateby,
ifnull(tt.isactive, 0)
from timed_task tt
where tt.isactive = 1
order by tt.updatetime desc
</select>
可见,对于三个不同的tinyint(1)
字段的处理方式不一样。
对于Spring Boot + MyBatis 应用,在配置文件application.properties
里面新增一条配置信息:logging.level.com.aaa.mapper=debug
,即可实现打印输出SQL语句到日志控制台。
SQL语句如下:
select tt.id,
ifnull(tt.task_status, 0) as taskStatus,
tt.mq_switch as mqSwitch,
tt.inserttime,
tt.insertby,
date_format(tt.updatetime, '%Y-%m-%d %H:%i:%s') as updatetime,
tt.updateby,
ifnull(tt.isactive, 0)
from timed_task tt
where tt.isactive = 1
order by tt.updatetime desc
拿到SQL语句去DataGrip执行,没有问题:
id | taskStatus | mqSwitch | inserttime | insertby | updatetime | updateby | ifnull(tt.isactive, 0) |
---|---|---|---|---|---|---|---|
3 | 1 | 1 | 2020-08-26 10:49:52 | awesome | 2020-08-26 10:49:52 | awesome | 1 |
但postman调用接口得到的返回数据是:
{
"list": [
{
"id": 3,
"ifnull(tt": {
"isactive, 0)": 1
},
"inserttime": "1598410192000",
"mqSwitch": true,
"taskStatus": 1,
"updateby": "awesome",
"insertby": "awesome",
"updatetime": "2020-08-26 10:49:52",
}
]
}
分析
问题1
inserttime
字段是datetime类型,取值变成timestamp,除非如date_format(tt.updatetime, '%Y-%m-%d %H:%i:%s') as updatetime
一样处理一下。
问题2
在返回值为Map类型(即resultType="Map"
)时,数据表里的tinyint(1)
类型的数据(即[1, 0]
),被mybatis会自动把转换成boolean类型数据(即[true/false]
),参考Mybatis中tinyint(1)数据自动转化为boolean处理。
解决方案:
- 使用
ifnull(column, 0)
处理该字段 - 在jdbcUrl添加参数:
tinyInt1isBit=false
(默认为true) - 避免使用长度为1的tinyint类型字段存储数字格式的数据。
在笔者的问题场景下,只推荐第一种解决方案。即通过ifnull处理。因此,可以看到taskStatus
如期返回1,而mqSwitch
还是返回true。
问题2
isactive
字段,也采用ifnull(tt.isactive, 0)加以处理
,但是没有后面的as
表达式部分。接口返回居然是:
"ifnull(tt": {
"isactive, 0)": 1
},
备注:本文使用的MyBatis为mybatis-spring-boot-starter,版本:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
<!--升级到这个版本,还是有问题-->
<!--<version>2.1.3</version>-->
</dependency>
对应的mybatis版本:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
<!-- mybatis-spring-boot-starter 2.1.3 版本依赖的mybatis版本 -->
<!--<version>3.5.5</version>-->
</dependency>
所以,在MyBatis看来,这不是问题!!??
在实体类PO里面有定义字段applyStatus,查询条件也返回t1.applyStatus
,SQL执行没有问题,但是MyBatis就是不返回该数据;
ifnull(t1.applyStatus, 0) as applyStatus
问题
数据表有个表示状态的字段,类型定义为tinyint(1)
,1表示状态开启,0表示状态关闭,前端查询条件里有个下拉框,选择开启或者关闭,根据此状态来查询数据:
<if test="status != null and status != ''">
AND status = #{status}
</if>
但是始终不生效,看前端传参0和1,1时查询正常,0时查询失败,说明问题不在前端。
打印SQL执行日志,发现根本没有带上AND status = 0
这个条件。
此时才发现,MySQL字段定义为tinyint(1)
时,MyBatis会把Integer = 0
当作空字符串来处理。
因此上面的if条件不成立,解决方案:
<if test="status != null">
AND status = #{status}
</if>
非tinyint入库失败
有如下数据表定义:
create table channel_advertiser_id (
id int auto_increment primary key,
category_id int null comment '广告主分类id',
use_id int null comment '广告主用途id',
isagentaccount int(5) default 2 not null comment '1:代理账号 0:非代理账号 2:空',
status int(5) default 1 not null comment '投放状态 1:投放中 2:投放停止'
) charset = utf8;
create index id on kraken_pre.channel_advertiser_id (id);
可以发现这些字段都不是tinyint
类型,而是int
类型。
有如下MyBatis的mapper.xml
<update id="updateAdvertiserIdByParams">
UPDATE channel_advertiser_id
<set>
<if test="categoryId != null and categoryId !=''">
category_id = #{categoryId},
</if>
<if test="useId != null and useId !=''">
use_id = #{useId},
</if>
<if test="isAgentAccount != null and isAgentAccount !=''">
isagentaccount = #{isAgentAccount},
</if>
<if test="status != null and status !=''">
status = #{status},
</if>
</set>
where id = #{id}
</update>
省略数据库PO定义,用如下单元测试类来验证:
/**
* @author johnny
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WebApplication.class)
public class MapperTest {
@Resource
private ChannelAdvertiserIdMapper advertiserIdMapper;
@Test
public void test() {
ChannelAdvertiserId advertiser = new ChannelAdvertiserId();
advertiser.setId(9159L);
advertiser.setCategoryId(0);
advertiser.setUseId(0);
advertiser.setStatus(0);
advertiser.setIsAgentAccount(0);
advertiserIdMapper.updateAdvertiserIdByParams(advertiser);
}
}
断点调试时的截图:
但是执行报错:
### SQL: UPDATE channel_advertiser_id where id = ?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where id = 9159' at line 3
也就是说,MyBatis的and categoryId !=''
条件不满足,即MyBatis将Integer
类型的0处理成空字符串?!!!
解决方法:去掉if
条件判断的后半部分。