updateById方法

MyBatis-Plus提供了updateById方法来更新数据,它是根据id来更改数据的,但是需要的参数是一个实体类对象,而且这个对象的id不能为null,否则无法完成update操作(当然被逻辑删除的数据不能执行该方法),如:

  1. @Test
  2. public void test() {
  3. User user=new User();
  4. user.setId(1422397337881661442L);
  5. user.setName("阿离1314");
  6. userMapper.updateById(user);
  7. }

image.png

自动填充时间

在实际开发中,每张表几乎都需要具有表示“该行数据创建时间”和“该行数据被更改时间”两个字段,下面是阿里巴巴的开发手册的一点:
【强制】表必备三字段:id, gmt_create, gmt_modified。
说明:其中 id 必为主键,类型为 unsigned bigint、单表时自增、步长为 1。gmt_create,
gmt_modified 的类型均为 date_time 类型,前者现在时表示主动创建,后者过去分词表示被
动更新。

一般来说,数据的创建时间、更新时间都希望自动生成,而不是手动采用代码生成时间,自动生成时间通常有两个级别的方法:“数据库级别”和“代码级别”。

数据库级别

新创建字段(实体类字段对应增加即可)
image.png
刚创建时的数据:
image.png
执行更改后的数据:

  1. @Test
  2. public void test() {
  3. User user=new User();
  4. user.setId(1422397337881661442L);
  5. user.setName("阿离1314520");
  6. userMapper.updateById(user);
  7. }

image.png
可以看到gmt_modified时间自动更新了,不过数据库一般是不允许随意被直接操作的,所以通常采用代码级别来自动填充时间

代码级别

MyBatis-Plus也提供了自动填充时间的插件,首先需要编写这个处理器(处理器通常放在handler包下)并注入Spring:

  1. @Component
  2. public class MyMetaObjectHandler implements MetaObjectHandler {
  3. @Override
  4. public void insertFill(MetaObject metaObject) {
  5. // setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
  6. this.setFieldValByName("gmtCreate", new Date(), metaObject);
  7. this.setFieldValByName("gmtModified", new Date(), metaObject);
  8. }
  9. @Override
  10. public void updateFill(MetaObject metaObject) {
  11. // setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
  12. this.setFieldValByName("gmtCreate", new Date(), metaObject);
  13. this.setFieldValByName("gmtModified", new Date(), metaObject);
  14. }
  15. }

然后需要给这两个字段添加注解:

public class User {
    @TableId(value = "id", type = IdType.ID_WORKER)
    private Long id;
    private String name;
    private String pwd;
    @TableLogic
    private Integer deleted;
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;
    @TableField(fill = FieldFill.UPDATE)
    private Date gmtModified;

    //构造器和setXxx、getXxx
}

字段填充策略 FieldFill:

描述
DEFAULT 默认不处理
INSERT 插入填充字段
UPDATE 更新填充字段
INSERT_UPDATE 插入和更新填充字段

这些自动填充策略不止适用于时间的自动填充,其它字段有需要字段填充也都可以使用,当前也需要对应的处理器。

数据库实现乐观锁的机制

乐观锁 : 故名思意就是十分乐观,它总是认为不会出现问题,无论干什么不去上锁,如果出现了问题, 再次更新值测试 。
悲观锁:故名思意就是十分悲观,它总是认为总是出现问题,无论干什么都会上锁,再去操作。

而数据库实现乐观锁的机制之一就是添加version字段了:

  • 取出记录时,获取当前 version
  • 更新时,带上这个version 执行
  • 更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

注册乐观锁插件(新版的注册方式有变化,参考官方文档):

@Configuration
public class MyBatisPlusConfig {

   /**
     * 注册乐观锁插件
     * @return new OptimisticLockerInterceptor()
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

}

实例如下:
image.png
模拟多线程:

    @Test
    public void test() {
        User user=userMapper.selectById(1422397337881661442L);

        User user1 = new User();
        user1.setId(1422397337881661442L);
        user1.setName("阿离小可爱");
        user1.setVersion(user.getVersion());

        User user2 = new User();
        user2.setId(1422397337881661442L);
        user2.setName("小阿离呀,我爱了");
        user2.setVersion(user.getVersion());

        userMapper.updateById(user2);
        userMapper.updateById(user1);
    }

运行结果:
image.png
可以看到user1的更新是失败的,因为user1携带的version是1,但user2先更新了,这就导致数据库中的version由1变成了2,因此user1携带的version=1就不能完成更新了。

特别注意:
在使用乐观锁前,一定要获取该条数据的version是多少,否则无法实现乐观锁;
支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime;
仅支持 updateById 与 update 方法;