【实现案例】应用层面基于 MyBatis-Plus 实现数据表记录创建和修改时间自动同步
需求描述
实际开发过程中,很多时候数据表结构是不方便修改的。
任何良好的数据表设计都应包含 主键id
、创建时间gmt_create
以及 修改时间gmt_midofied
这三个字段。
其中,主键应当是自增的无符号大整型,而另外两个时间相关的字段,应当是基于实际的记录创建和修改时间自动同步的日期时间类型。
在系统中集成了 Mybatis-Plus 的情况下,我们可以通过设置主键的自增策略来完成主键的自动更新。
本文要介绍的是,如何实现创建时间gmt_create
以及 修改时间gmt_midofied
这两个字段,根据实际系统中的操作自动同步的功能。
技术选型
这里列举实现过程中用到的相关技术,及其对应的版本:
- JDK 17
- Spring Boot 3.4.1
- Lombok 1.18.36(可选,用于简化实现)
- Mybatis-Plus 3.5.9
- MySQL 8.0
以下给出相关配置的 XML 文件,要注意 MySQL 驱动与选用的数据库版本对应,Mybatis-Plus 要引入适应于 Spring Boot 版本的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.grobsr</groupId>
<artifactId>learning</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>learning</name>
<description>learning</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 注意这里的 artifactId 是 mybatis-plus-spring-boot3-starter -->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-spring-boot3-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
相关配置
建议显式地指定应用名称,这样在作为微服务被调用时也许可以避免一些不必要的麻烦。
基本的数据库相关配置是必不可少的,还需要额外配置驼峰和下划线命名的自动映射。
开启 Mybatis-Plus 的日志功能,方便从框架所执行的数据库语句来排查问题。
spring.application.name=learning
spring.datasource.url=jdbc:mysql://[domain / ip]/[table name]?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.datasource.username=[username]
spring.datasource.password=[password]
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl
具体实现
测试用数据表
为了简化场景,测试用数据表 entity
仅包含涉及到的三个字段,以下给出建表的脚本。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for entity
-- ----------------------------
DROP TABLE IF EXISTS `entity`;
CREATE TABLE `entity` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`gmt_create` datetime NULL DEFAULT NULL COMMENT '记录创建时间',
`gmt_modified` datetime NULL DEFAULT NULL COMMENT '记录修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '实体类 Entity 对应的测试用表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
测试用实体类
测试用实体类中的属性应与数据表中的字段一一对应,要添加 MyBatis-Plus 的相关注解。
/**
* 测试用实体类
* @since 1.0
* @author Grobsr
*/
@Data
@NoArgsConstructor
@TableName("entity")
public class Entity {
/**
* 对应数据表中的主键 id,设置生成策略为自增
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 对应数据表中的记录创建时间 gmt_create,设置创建时同步填充
*/
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
/**
* 对应数据表中的记录修改时间 gmt_modified,设置更新时同步修改
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
public Entity(Long id) {
this.id = id;
}
}
测试用 Mapper 接口
@Mapper
public interface EntityMapper extends BaseMapper<Entity> {}
测试用处理类
/**
* 应用层面基于 MyBatis-Plus 实现数据表记录创建和修改时间自动同步
* @since 1.0
* @author Grobsr
*/
@Component
public class TestMetaObjectHandler implements MetaObjectHandler {
/**
* 重写的 insertFill 方法,它会在 MyBatis-Plus 新增记录时被触发,填充新增和修改时间。
* @param metaObject 管理实体类属性的元数据对象
*/
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
/**
* 重写的 updateFill 方法,它会在 MyBatis-Plus 修改记录时被触发,更新修改时间。
* @param metaObject 管理实体类属性的元数据对象
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
测试类
@SpringBootTest
class LearningApplicationTests {
@Autowired
EntityMapper entityMapper;
@Test
void testInsert() {
entityMapper.insert(new Entity());
}
@Test
void testUpdate() {
entityMapper.updateById(new Entity(1L));
}
}
步骤总结
- 搭建 MyBatis-Plus 的整体框架,用注解
@TableName("[table name]")
做好实体映射,创建好对应的 Mapper 接口。 - 在实体类的主键字段上添加主键标记并设置生成策略
@TableId(type = IdType.AUTO)
,在需要自动填充的字段上设置同步时机@TableField(fill = [FieldFill.INSERT / FieldFill.UPDATE / FieldFill.INSERT_UPDATE])
。 - 创建处理类并实现
MetaObjectHandler
接口,完成具体的更新操作。