当前位置: 首页 > news >正文

数据库公共字段自动填充的三种实现方案

背景介绍

在实际项目开发中,我们经常需要处理一些公共字段自动填充,比如:

  • createTime (创建时间)
  • updateTime (更新时间)
  • createUser (创建人)
  • updateUser (更新人)
    这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。

方案一:MyBatis + AOP方式

这种方式通过自定义注解和切面来实现自动填充。

代码实现

  1. 添加AOPMyBatis依赖

SpringMVC

<!-- mybatis-spring -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version>
</dependency>
<!-- AOP-spring -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.12</version></dependency>

SpringBoot

<!--mybatis起步依赖-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency>
<!-- AOP -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 自定义枚举类
/*** 数据库操作类型*/
public enum OperationType {UPDATE,  //更新操作INSERT   //插入操作}
  1. 自定义注解
@Target(ElementType.METHOD)  // 作用于方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {OperationType value();  // INSERT/UPDATE
}
  1. 切面类实现
@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointcut() {}/*** 前置通知,在通知中进行公共字段的赋值*/@Before("autoFillPointcut()")public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException {log.info("开始公共字段自动填充...");//获取当前被拦截的方法上的数据库操作字段MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获取方法上的注解对象OperationType value = autoFill.value(); // 获取数据库的操作类型值//获取当前被拦截的方法上的参数 -- 实体对象 (默认约定需要自动填充的方法,将实体对象放在第一个)Object[] args = joinPoint.getArgs();if (args == null || args.length == 0) {return;}Object entity = args[0];// 准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();// 根据当前不同的操作类型,为对应的属性通过反射来赋值if (value == OperationType.INSERT){// 为4个公共字段赋值try {Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser", Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);// 通过反射为对象属性赋值setCreateTime.invoke(entity, now);setCreateUser.invoke(entity, currentId);setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {e.printStackTrace();}} else if (value == OperationType.UPDATE) {// 为4个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);// 通过反射为对象属性赋值setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {e.printStackTrace();}}}}
  1. 定义常量类(可选)
    其中"setCreateTime"直接硬编码,容易出错,并且不够优雅,可以将其定义为常量类
/*** 公共字段自动填充相关常量*/
public class AutoFillConstant {/*** 实体类中的方法名称*/public static final String SET_CREATE_TIME = "setCreateTime";public static final String SET_UPDATE_TIME = "setUpdateTime";public static final String SET_CREATE_USER = "setCreateUser";public static final String SET_UPDATE_USER = "setUpdateUser";
}
  1. 在Mapper中对应需要进行公共字段自动填充的方法上加上注解
@AutoFill(value = OperationType.UPDATE)
void update(Category category)@AutoFill(value = OperationType.INSERT)
void insert(Category category)

优缺点

  1. 优点:
    • 灵活性强,可自定义复杂的填充逻辑
    • 可以统一管理所有需要自动填充的字段
  2. 缺点:
    • 实现相对复杂
    • 需要编写较多代码
    • 需要手动添加注解

方案二:MyBatis-Plus方式

MyBatis-Plus提供了更简便的实现方式。

代码实现

  1. 添加AOPMP依赖

SpringBoot3

<!--springboot2整合MybatisPlus-->
<!--MybatisPlus起步依赖-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency><!--springboot3整合MybatisPlus-->
<!--MybatisPlus起步依赖-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency><!-- Spring Boot AOP依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 实现 MetaObjectHandler接口

需要定义一个类,需要实现 MetaObjectHandler,然后实现里面的 insertFill()updateFill() 方法,分别代表在执行插入操作和更新操作时执行对应的方法,而方法内部去实现你需要注入的字段的值

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");// 设置插入时的字段this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);this.setFieldValByName("createUser", BaseContext.getCurrentId(), metaObject);this.setFieldValByName("updateUser", BaseContext.getCurrentId(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");// 设置更新时的字段this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateUser", BaseContext.getCurrentId(), metaObject);}
} 
  1. 实体类字段添加@TableField注解
@TableField(fill = FieldFill.INSERT) // 执行插入时自动填充
private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT)
private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE) // 执行插入和更新时自动填充
private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;

@TableField(fill = FieldFill.INSERT): 执行插入时自动填充
@TableField(fill = FieldFill.INSERT_UPDATE) : 执行插入和更新时自动填充
还有其他的属性,可以根据业务需求自行添加
在这里插入图片描述

优缺点

  1. 优点:
    • 使用简单,代码量少
    • 开箱即用,无需复杂配置
    • 与MyBatis-Plus无缝集成
  2. 缺点:
    • 填充逻辑相对固定
    • 扩展性较差

方案三:数据库默认值

直接在数据库表设计时设置默认值。

SQL实现

CREATE TABLE `table_name` (`id` bigint NOT NULL AUTO_INCREMENT,`create_time` datetime DEFAULT CURRENT_TIMESTAMP,`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB;

优缺点

  1. 优点:
    • 实现最简单
    • 不需要额外代码
    • 数据库层面保证字段有值
  2. 缺点:
    • 只能处理简单的默认值场景
    • 无法获取当前登录用户等业务信息
    • 不同数据库实现可能不一样

方案选择建议

  1. 对于简单的时间字段(createTime/updateTime):
    • 建议使用数据库默认值
    • 可以保证数据的一致性
  2. 需要记录操作人等业务字段:
    • 推荐使用MyBatis-Plus方式
    • 简单高效,满足大部分需求
  3. 有特殊业务逻辑:
    • 考虑使用AOP方式
    • 可以实现更复杂的填充逻辑
  4. 实际项目中可以组合使用:
    • 时间字段用数据库默认值
    • 业务字段用MyBatis-Plus或AOP

总结

  1. 三种方案各有优劣,需要根据实际需求选择
  2. 推荐优先使用MyBatis-Plus方式,简单且功能足够
  3. 特殊场景再考虑其他方案
  4. 可以组合使用不同方案,扬长避短
http://www.lryc.cn/news/501259.html

相关文章:

  • 《MySQL 入门:数据库世界的第一扇门》
  • Qt之第三方库QCustomPlot使用(二)
  • JAVA-类与继承
  • SSH连接报错,Corrupted MAC on input 解决方法
  • 【C++】8___继承
  • C# 中的异常处理:构建健壮和可靠的程序
  • 基于智能合约的医院凭证共享中心路径探析
  • vba学习系列(9)--按需求计数单元格数量
  • scale index的计算
  • 鸿蒙实现Web组件开发
  • Linux——linux系统移植
  • 工业摄像头应对复杂环境的策略与解决方案
  • 重生之我在异世界学编程之C语言:深入动态内存管理篇
  • 【经典论文阅读】Latent Diffusion Models(LDM)
  • 智能指针中的weak_ptr(弱引用智能指针)
  • 【电子通识】机电继电器和固态继电器的区别
  • 工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet
  • 如何创建对话窗口
  • 新手上路,学Go还是Python
  • <!DOCTYPE html>的作用是什么
  • EasyExcel改名为FastExce做了那些改变呢
  • 狗狗的生育周期:关注与呵护
  • ABAP DIALOG屏幕编程2
  • 获取缓存大小与清除 Web 缓存 - 鸿蒙 HarmonyOS Next
  • 在Unreal Engine中,UHT与反射机制
  • SQL项目实战与综合应用——项目设计与需求分析
  • 分布式中的CAP定理和BASE理论与强弱一致性
  • C/C++常见符号与运算符
  • 了解 k8s 网络基础知识
  • 用户信息界面按钮禁用+发送消息功能