mybatis plus 自动更新数据库时间的小坑

3,251 阅读2分钟

描述问题

昨天测试又给我提了一个bug(我怎么觉得自己是个被bug驱动的渣渣程序员),说他更新了某条数据, 但是这调数据的修改时间没有变化. 我的第一反映是怎么可能,我在创建数据库表的时候明明设置了修改时间会自动更新呀? 但是人家就是测试这么个bug呀, 我也只能认怂, 修吧. 经过一番查找, 最后确定, 是我的代码写的有问题, 这里我重现一下犯罪现场, 对自己以兹鼓励哈.

犯罪数据准备

假设我们的数据库表如下所示, 注意这里的create_time和modified_time的设置, 二者都会在插入数据时自动填充, 并且modified_time会在更新数据时自动更新. 不行你可以在数据库中执行插入和更新试试就一目了然了.

CREATE TABLE `user_info`  (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID自增',
  `username` varchar(64) NOT NULL COMMENT '用户名',       
  `mobile` varchar(128) NOT NULL COMMENT '手机号',
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '0:未删除 1:已删除',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '用户信息表' ROW_FORMAT = Dynamic;

犯罪代码准备

// doamin
@Data
public class UserInfo {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String username;

    private String mobile;

    private Integer deleted;

    private LocalDateTime createdTime;

    private LocalDateTime modifiedTime;
}

// controller
@RestController
@RequestMapping("/date/user")
public class UserInfoController {


    @Resource
    private UserInfoService userInfoService;
    
    // 保存一条数据
    @RequestMapping("/save")
    public boolean createUserInfo(@RequestBody @Validated CreatUserInfoRequest request) {

        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(request, userInfo);

        boolean sucess = userInfoService.save(userInfo);
        return sucess;
    }

    // 更新一条数据
    @RequestMapping("/update")
    public boolean updateUserInfo(@RequestBody @Validated UpdateUserInfoRequest request) {

        UserInfo userInfo = userInfoService.getById(request.getId());
        Assert.notNull(userInfo, "编辑的信息不存在");

        BeanUtils.copyProperties(request, userInfo);
        boolean success = userInfoService.updateById(userInfo);

        return success;
    }
}

犯罪过程重现

执行插入操作: 数据库结果:

插入时执行的sql:

执行更新操作:

数据库结果:

更新时执行的sql:

看到这里, 我相信眼睛犀利的你应该已经发现我哪里错了, 是的, 就是你想的那样, 我在执行更新操作之前, 首先把这个对象查询出来了, 然后把属性值赋予他之后,我才去执行了更新, 此时我使用的对象正是我查询出来的, 而它的modifiedTime是有值的, 我每次给人家更新成最开始的时间,还埋怨人家咋不给我自动更新, 做人难, 做代码也挺难. 既然知道了原因, 那就改吧.

毁尸灭迹啦

直接更新, 不要先查询----核心是保证modifiedTime 没有值

    @RequestMapping("/update")
    public boolean updateUserInfo(@RequestBody @Validated UpdateUserInfoRequest request) {

        UserInfo userInfo = new UserInfo();

        BeanUtils.copyProperties(request, userInfo);
        boolean success = userInfoService.updateById(userInfo);

        return success;
    }

    @RequestMapping("/update")
    public boolean updateUserInfo(@RequestBody @Validated UpdateUserInfoRequest request) {

        UserInfo userInfo = userInfoService.getById(request.getId());
        Assert.notNull(userInfo, "编辑的信息不存在");

        BeanUtils.copyProperties(request, userInfo);
        userInfo.setModifiedTime(null);

        boolean success = userInfoService.updateById(userInfo);

        return success;
    }

使用@TableField + update注解


    @TableField(update = "now()")
    private LocalDateTime modifiedTime;

使用@TableFiled + fill 注解


 @TableField(fill = FieldFill.INSERT_UPDATE)
 private LocalDateTime modifiedTime;
    
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert field....");
        this.setFieldValByName("createTime", LocalDateTime.now(),metaObject);
        this.setFieldValByName("modifiedTime", LocalDateTime.now(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("modifiedTime", LocalDateTime.now(),metaObject);
    }
}