创建时间、修改时间!这些操作一遍都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表中gmt_create、gmt_modified几乎所有的表都要配置上,而且需要自动化!
实现自动填充我们可以在数据库级别实现,也可以在代码级别实现
create_time字段的Type类型 选择 “datetime” , Deafault默认值 选择 “current_timestamp”
update_time字段的Type类型 也选择 “datetime” , Deafault默认值 也选择 “current_timestamp”,和create_time字段相同
由于在IDEA中并没有发现更新的选项,所以这里使用NaviCat进行设置,勾选 "update_time"字段下的 “根据当前时间戳更新”
我们发现数据表中的create_time和update_time字段按照当前时间进行填充了!
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; //修改时间 }
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); } }
结果:我们发现,预编译处理中,并没有动态拼接修改update_time属性!
那么到底更新时间是否进行了修改呢?我们继续查看修改数据结果
结果:我们发现update_time更新时间确实进行了修改!说明我们在数据库级别实现的自动填充确实有效!
除了可以在数据库级别实现,当然我们也可以通过Java代码级别来实现!
在Navicat中分别将 “create_time” 和 "update_time"字段的默认值设为Null,并取消勾选 “根据当前时间戳更新”
由于MybatisPlus中提供了使用 @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; ..... }
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 (插入和更新时填充)
由于要在代码级别实现自动填充,所以要在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; //修改时间 }
对于自动填充策略的处理,MybatisPlus还提供了专门的数据源对象处理器,我们只需实现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(严格更新填充)
在自定义实现类中,我们只需实现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; } }
这里还需要将User实体类的createTime和updateTime字段的属性类型修改为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; //修改时间 }
设置完了严格填充策略,接下来我们就来编写测试类,查看严格填充策略是否生效!
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); } }
结果:插入数据成功,插入和修改时间也自动填充了!
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); } }
结果:修改数据成功,修改时间也自动更新!
到这里,MybatisPlus的自动填充策略的基础学习就结束了,欢迎大家学习和讨论!
参考视频链接:https://www.bilibili.com/video/BV17E411N7KN (B站UP主遇见狂神说的MybatisPlus快速入门)