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