MybatisPlus基础学习之自动填充策略

4.MybatisPlus自动填充策略

4.1 为什么要进行自动填充处理?

创建时间修改时间!这些操作一遍都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表中gmt_creategmt_modified几乎所有的表都要配置上,而且需要自动化

4.2 实现自动填充

实现自动填充我们可以在数据库级别实现,也可以在代码级别实现

4.2.1 在数据库级别实现

1.在数据表中新增创建时间和修改时间字段

1-1 在user表中增加create_time字段

create_time字段Type类型 选择 “datetime” , Deafault默认值 选择 “current_timestamp”

MybatisPlus基础学习之自动填充策略

1-2 在user表中增加update_time字段

update_time字段Type类型 也选择 “datetime” , Deafault默认值 也选择 “current_timestamp”,和create_time字段相同

MybatisPlus基础学习之自动填充策略

2.设置update_time字段自动更新

2-1 选择根据当前时间戳更新

由于在IDEA中并没有发现更新的选项,所以这里使用NaviCat进行设置,勾选 "update_time"字段下的 “根据当前时间戳更新”

MybatisPlus基础学习之自动填充策略

3-2 查看修改后的user数据表

MybatisPlus基础学习之自动填充策略
我们发现数据表中的create_time和update_time字段按照当前时间进行填充了!

3.修改User实体类

package com.kuang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data //导入无参构造,set和get方法,以及toString方法等
@AllArgsConstructor //导入有参构造
@NoArgsConstructor //再次导入无参构造,防止被有参构造覆盖掉
public class User {
    //对应数据库中的主键 (UUID, 自增Id)
    /**
     * 这里选择的是ASSIGN_ID(3.3.0版本后的推荐使用的,基于雪花算法)
     * 3.3.0之前的版本推荐使用的是ID_WORKER(全局唯一ID,基于雪花算法)
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id; //用户编号
    private String name; //用户名
    private Integer age; //年龄
    private String email; //邮箱
   /* *
      * 由于mybatis-plus会自动开启驼峰命名转换
      * 所以对于数据库字段create_time,
      * 在Java实体类中直接使用CreateTime即可
    */
    private Date createTime; //创建时间
    private Date updateTime; //修改时间
}

4.修改用户测试

package com.kuang.mybatis_plus;

import com.kuang.mybatis_plus.mapper.UserMapper;
import com.kuang.mybatis_plus.pojo.User;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
//扫描mapper接口所在包
@MapperScan("com.kuang.mybatis_plus.mapper")
class MybatisPlusApplicationTests {

    //通过类型自动装配UserMapper接口
    @Autowired
    private UserMapper userMapper;

    //测试更新
    @Test
    public void testUpdate() {
        //获取User对象
        User user = new User();
        //设置修改用户的Id
        user.setId("1405056510844735489");
        //设置用户要修改的信息(修改多个属性)
        user.setName("周杰伦");
        user.setAge(24);
        user.setEmail("zjl123@qq.com");
        //通过Id修改用户,并且返回影响行数
        //注意:虽然updateById是通过Id修改信息,但是参数其实是一个对象!
        int result = userMapper.updateById(user);
        //打印受影响行数
        System.out.println(result);
    }

}

5.查看测试结果

5-1 查看控制台输出

MybatisPlus基础学习之自动填充策略

结果我们发现,预编译处理中,并没有动态拼接修改update_time属性!

那么到底更新时间是否进行了修改呢?我们继续查看修改数据结果

5-2 查看修改数据结果

MybatisPlus基础学习之自动填充策略

结果我们发现update_time更新时间确实进行了修改!说明我们在数据库级别实现的自动填充确实有效!

除了可以在数据库级别实现,当然我们也可以通过Java代码级别来实现

4.2.2 在代码级别实现

1.删除默认值和取消更新

在Navicat中分别将 “create_time”"update_time"字段的默认值设为Null,并取消勾选 “根据当前时间戳更新”

MybatisPlus基础学习之自动填充策略

MybatisPlus基础学习之自动填充策略

2.编写User实体类

由于MybatisPlus中提供了使用 @TableField注解 来实现代码级别的自动填充,所以我们首先查看一下它的相关源码,大致了解一下它的基本实现

2-1 查看@TableField注解源码
package com.baomidou.mybatisplus.annotation;

import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

import java.lang.annotation.*;

/**
 * 表字段标识
 *
 * @author hubin sjy tantan
 * @since 2016-09-09
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TableField {
    
    .....
        
    /**
     * 字段自动填充策略
     */
    //我们发现,在@TableField注解中有一个枚举类 FieldFill,并且它默认值是DEFAULT
    FieldFill fill() default FieldFill.DEFAULT;
    
    .....

}

2-2 查看FieldFill枚举类源码
package com.baomidou.mybatisplus.annotation;

/**
 * 字段填充策略枚举类
 *
 * <p>
 * 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成
 * <if test="...">......</if>
 * 判断优先级比 {@link FieldStrategy} 高
 * </p>
 *
 * @author hubin
 * @since 2017-06-27
 */
public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入时填充字段
     */
    INSERT,
    /**
     * 更新时填充字段
     */
    UPDATE,
    /**
     * 插入和更新时填充字段
     */
    INSERT_UPDATE
}

FieldFill字段枚举类四种自动填充处理策略,分别是Default (默认不处理)、Insert (插入时填充)、Update (更新时填充)、Insert_Update (插入和更新时填充)

2-3 修改User实体类

由于要在代码级别实现自动填充,所以要在User实体类的createTime和updateTime字段前使用 @TableField注解来实现

package com.kuang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data //导入无参构造,set和get方法,以及toString方法等
@AllArgsConstructor //导入有参构造
@NoArgsConstructor //再次导入无参构造,防止被有参构造覆盖掉
public class User {
    //对应数据库中的主键 (UUID, 自增Id)
    /**
     * 这里选择的是ASSIGN_ID(3.3.0版本后的推荐使用的,基于雪花算法)
     * 3.3.0之前的版本推荐使用的是ID_WORKER(全局唯一ID,基于雪花算法)
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id; //用户编号
    private String name; //用户名
    private Integer age; //年龄
    private String email; //邮箱
   /* *
      * 由于mybatis-plus会自动开启驼峰命名转换
      * 所以对于数据库字段create_time,
      * 在Java实体类中直接使用CreateTime即可
    */
    /* *
     * 使用@TableField注解,设置字段自动填充策略
     * fill的值为FieldFill.INSERT时,表示插入时填充字段
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime; //创建时间
    /* *
     * 使用@TableField注解,设置字段自动填充策略
     * fill的值为FieldFill.INSERT_UPDATE时,表示插入或者更新时填充字段
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime; //修改时间
}

3.编写处理器处理注解

对于自动填充策略的处理,MybatisPlus还提供了专门的数据源对象处理器,我们只需实现MetaObjectHandler接口便可进行一些填充策略的设置

3-1 查看MetaObjectHandler接口源码
package com.baomidou.mybatisplus.core.handlers;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.ibatis.reflection.MetaObject;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * 元对象字段填充控制器抽象类,实现公共字段自动写入<p>
 * <p>
 * 所有入参的 MetaObject 必定是 entity 或其子类的 MetaObject
 *
 * @author hubin
 * @since 2016-08-28
 */
public interface MetaObjectHandler {

    /**
     * 是否开启了插入填充
     */
    default boolean openInsertFill() {
        return true;
    }
    
    .....
    
    /**
     * 通用填充
     *
     * @param fieldName  Java实体类的属性名称
     * @param fieldVal   Java实体类的属性值
     * @param metaObject 数据源对象参数
     */
    default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
        if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
        return this;
    }
    
    ......
        
    
   /**
     * @param metaObject 数据源对象参数
     * @param fieldName Java实体类的属性名称
     * @param fieldType Java实体类的属性类型
     * @param fieldVal Java实体类的属性值
     * @since 3.3.0
     */
     //严格插入填充
    default <T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, Object fieldVal) {
        return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }    
    
    ......
        
   /**
     * @param metaObject 数据源对象参数
     * @param fieldName Java实体类的属性名称
     * @param fieldType Java实体类的属性类型
     * @param fieldVal Java实体类的属性值
     * @since 3.3.0
     */   
     //严格更新填充
   default <T> MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Class<T> fieldType, Supplier<T> fieldVal) {
        return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }
}

通过查看源码,我们发现MetaObjectHandler接口,主要有三个填充策略方法,分别是setFieldValByName (通用填充)、strictInsertFill (严格插入填充)、strictUpdateFill(严格更新填充)

3-2 编写自定义的MyMetaObjectHandler实现类

在自定义实现类中,我们只需实现MetaObjectHandler接口,然后重写 insertFill方法 (插入填充),updateFill方法 (更新填充)和 strictFillStrategy方法(严格填充策略)

package com.kuang.mybatis_plus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

//使用Slf4j注解,设置Slf4j日志输出
@Slf4j
//将MyMetaObjectHandler注册为Spring的IOC容器中的组件
@Component
//自定义实现类MyMetaObjectHandler,实现MetaObjectHandler(数据源对象处理器)接口
public class MyMetaObjectHandler implements MetaObjectHandler {

    //插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        //打印日志信息
        log.info("start insert fill...");
        //是否开启了插入填充
//        this.openInsertFill();
        /* *
         * setFieldValByName方法有三个参数:
         * String fieldName, Object fieldVal, MetaObject metaObject
         * @param fieldName  Java实体类的属性名称
         * @param fieldVal   Java实体类的属性值
         * @param metaObject 数据源对象参数
         */
        //设置创建时间属性和值
        //3.3.0之前使用setFieldValByName方法
//        this.setFieldValByName("createTime",new Date(),metaObject);
        //3.3.0之后推荐使用strictInsertFill方法
        this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());
        //设置修改时间属性和值
        //3.3.0之前使用setFieldValByName方法
//        this.setFieldValByName("updateTime",new Date(),metaObject);
        //3.3.0之后推荐使用strictInsertFill方法
        this.strictInsertFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
    }

    //更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        //打印日志信息
        log.info("start update fill...");
        //设置修改时间属性和值
        //3.3.0之前使用setFieldValByName方法
//        this.setFieldValByName("updateTime",new Date(),metaObject);
        //3.3.0之后推荐使用strictUpdateFill方法
        this.strictInsertFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
    }

     //修改严格填充策略
    //默认策略是:如果属性有值,则不覆盖;如果填充值为null,则不填充
    @Override
    public MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
        //每次填充,直接使用填充值
        //获取字段值,封装到Object对象中
        Object obj = fieldVal.get();
        //判断值是否为空
        if(Objects.nonNull(obj)) {
            metaObject.setValue(fieldName, obj);
        }
        return this;
    }

}
3-3 修改User实体类属性类

这里还需要将User实体类的createTimeupdateTime字段属性类型修改为LocalDateTime,否则插入数据时无法自动填充创建时间和修改时间

package com.kuang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.Date;

@Data //导入无参构造,set和get方法,以及toString方法等
@AllArgsConstructor //导入有参构造
@NoArgsConstructor //再次导入无参构造,防止被有参构造覆盖掉
public class User {
    //对应数据库中的主键 (UUID, 自增Id)
    /**
     * 这里选择的是ASSIGN_ID(3.3.0版本后的推荐使用的,基于雪花算法)
     * 3.3.0之前的版本推荐使用的是ID_WORKER(全局唯一ID,基于雪花算法)
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id; //用户编号
    private String name; //用户名
    private Integer age; //年龄
    private String email; //邮箱
   /* *
      * 由于mybatis-plus会自动开启驼峰命名转换
      * 所以对于数据库字段create_time,
      * 在Java实体类中直接使用CreateTime即可
    */
    /* *
     * 使用@TableField注解,设置字段自动填充策略
     * fill的值为FieldFill.INSERT时,表示插入时填充字段
     */
    @TableField(fill = FieldFill.INSERT)
    //将createTime修改为LocalDateTime类型
    private LocalDateTime createTime; //创建时间
    /* *
     * 使用@TableField注解,设置字段自动填充策略
     * fill的值为FieldFill.INSERT_UPDATE时,表示插入或者更新时填充字段
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime; //修改时间
}

设置完了严格填充策略,接下来我们就来编写测试类,查看严格填充策略是否生效!

4.编写插入数据测试类

4-1 编写插入数据测试类
package com.kuang.mybatis_plus;

import com.kuang.mybatis_plus.mapper.UserMapper;
import com.kuang.mybatis_plus.pojo.User;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
//扫描mapper接口所在包
@MapperScan("com.kuang.mybatis_plus.mapper")
class MybatisPlusApplicationTests {

    //继承了BaseMapper,所有的方法都来自父类
    //我们也可以编写自己的扩展方法
    //通过类型自动装配UserMapper接口
    @Autowired
    private UserMapper userMapper;

    //测试插入用户数据
    @Test
    public void testInsert() {
       //获取User对象
       User user = new User();
       //设置用户名和年龄以及邮箱等属性
        //使用ASSign_UUID主键策略
        user.setName("杨宗纬");
        user.setAge(44);
        user.setEmail("yzw123456@qq.com");

        //返回受影响的行数(自动帮我们生成Id)
       int result = userMapper.insert(user);
       //打印受影响行数
        System.out.println(result);
       //打印插入的用户信息
       System.out.println(user);
    }

}
4-2 查看控制台输出

MybatisPlus基础学习之自动填充策略

4-3 查看插入数据结果

MybatisPlus基础学习之自动填充策略

结果插入数据成功,插入和修改时间也自动填充了!

5.编写修改数据测试类

5-1 编写修改数据测试类
package com.kuang.mybatis_plus;

import com.kuang.mybatis_plus.mapper.UserMapper;
import com.kuang.mybatis_plus.pojo.User;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
//扫描mapper接口所在包
@MapperScan("com.kuang.mybatis_plus.mapper")
class MybatisPlusApplicationTests {

    //继承了BaseMapper,所有的方法都来自父类
    //我们也可以编写自己的扩展方法
    //通过类型自动装配UserMapper接口
    @Autowired
    private UserMapper userMapper;

    //测试更新
    @Test
    public void testUpdate() {
        //获取User对象
        User user = new User();
        //设置修改用户的Id
        user.setId("1405056510844735489");
        //设置用户要修改的信息(修改多个属性)
        user.setName("周杰伦");
        user.setAge(26);
        user.setEmail("zjl123456@qq.com");
        //通过Id修改用户,并且返回影响行数
        //注意:虽然updateById是通过Id修改信息,但是参数其实是一个对象!
        int result = userMapper.updateById(user);
        //打印受影响行数
        System.out.println(result);
    }

}
5-2 查看控制台输出

MybatisPlus基础学习之自动填充策略

5-3 查看修改数据结果

MybatisPlus基础学习之自动填充策略

结果修改数据成功,修改时间也自动更新!

到这里,MybatisPlus的自动填充策略的基础学习就结束了,欢迎大家学习和讨论!
参考视频链接:https://www.bilibili.com/video/BV17E411N7KN (B站UP主遇见狂神说的MybatisPlus快速入门)

上一篇:mybatis plus CU自动填充 和 软删除自动填充


下一篇:Java for LeetCode 025 Reverse Nodes in k-Group