Java开发 - 公共字段的自动填充

前言

如果说Java开发中有什么是让人很烦的一件事,那一定是无尽的填充字段,本篇作为观众瑰宝系列第二篇,将带来公共字段填充相关的知识点,学完此篇,让你摆脱公共字段填充带来的麻烦,节省代码,降低冗余,妥妥的给项目瘦身,还能一定程度上提高程序的运行效率,所以,你心动吗?

问题描述

可以说,几乎每张表,我们都会有create_time,update_time这样的公共字段,所以几乎每次请求时我们都要去操作这样的字段并重新赋值,这就降低了我们的开发效率,且重复的代码让人不胜其烦,所以我们想要写一个公共的方法可以统一添加这些字段,而不用处处都写相同的代码!

问题分析

我们以上一篇SpringCache的项目为基础,也可自行创建一个项目,在上一个项目中,我们在原来的数据库中添加create_time,update_time字段:

alter table user add create_time DATETIME;
alter table user add update_time DATETIME;

新表字段如下: 

我们清空了表中的数据, 记者修改模型数据,添加这两个新字段;

package com.codingfire.cache.entity;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class User implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Integer age;
    private String phone;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

接着去重新修改增删改查的代码,我们简单修改如下:

    @PostMapping
    @CachePut(value = "user", key = "#user.id")
//    @CachePut(value = "userCache", key = "#backUser.id")
    public User add(User user) {
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userService.save(user);
        User backUser = user;
        return backUser;
    }

    @PutMapping
    @CachePut(value = "user", key = "#user.id")
//    @CachePut(value = "userCache", key = "#backUser.id")
    public User update(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userService.updateById(user);
        User backUser = user;
        return backUser;
    }

下面运行MySQL和Redis服务,用postman调用添加用户接口:

结果意外遇到了新问题, Redis的jackson不支持LocalDateTime时间类,这就尴尬了,但并不是无解,报错中给出了解决办法,我们按照提示做出一些调整。

添加新依赖:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.13.0</version>
</dependency>

这时如果你直接运行项目,还是不行,还需要在实践属性上添加两个注解:

    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)

做完这些,重新启动项目,运行添加用户的API,果然运行成功,我们查看数据库:

 新的数据已经添加上去了,但又没有办法隐式的添加这两个时间,而不是直接通过代码呢?我想,应该是有的,我们之前学过过滤器和拦截器,他俩的差别我就不再介绍了,感兴趣的自行查看: 

Java开发 - 拦截器初体验_CodingFire的博客-CSDN博客

由此来看,这里面必定少不了他们的角色,这里我们就用过滤器吧,过滤器的拦截范围更大,一般项目都会使用,我们不妨也用一下,基调定好了,下一步我们就来看看怎么实现这个过滤器,让字段可以自动填充。 

然而我们发现,写在过滤器里也不是很合适,它只能在post时通过request.getParam(key)获取数据,不能进行写操作,这就麻烦了,看来过滤器也不是很好的选择啊。

好在,天无绝人之路,我们还可以通过自定义元数据对象处理器的方式来统一添加参数,而且,可行度非常高。

解决方案

创建元数据对象处理器

package com.codingfire.cache.common;

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;

/**
 * 自定义元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjectHandle implements MetaObjectHandler {
    /**
     * 插入操作,自动填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]...");
        log.info(metaObject.toString());

        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
    }

    /**
     * 更新操作,自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());

        metaObject.setValue("updateTime",LocalDateTime.now());
    }
}

删除接口内自定义添加字段

将接口中,我们手动添加的字段删掉:

    @PostMapping
    @CachePut(value = "user", key = "#user.id")
//    @CachePut(value = "userCache", key = "#backUser.id")
    public User add(User user) {
        userService.save(user);
        User backUser = user;
        return backUser;
    }

给时间字段添加注解

做完这些,还需要给自动填充的字段增加一个注解。

如果是插入填充:

    @TableField(fill = FieldFill.INSERT)

如果是插入和更新时都填充:

    @TableField(fill = FieldFill.INSERT_UPDATE)

测试

我们重新启动项目,用postman调用此接口,新增加一个用户:

查看数据库中新增加的用户信息是否包含了时间:

 

测试成功,公共字段的填充搞定。 

结语

看来,解决问题时,方向一定要找对,斗则方向错了,怎么也不可能完成目标的。一开始博主想要用过滤器来完成,但是发现过滤器无法进行写操作,转而更换了方向,从元数据处理器下手,最后终于实现了公共字段填充。觉得不错,就给个赞吧。

### Java实现公共字段自动填充Java中,为了减少重复代码并统一处理特定字段的操作,在插入或更新记录时可以通过多种方式来实现实体类中的公共字段自动填充。一种常见的做法是利用面向切面编程(AOP)结合自定义注解来完成这一目标。 #### 定义操作类型枚举 首先创建一个表示数据库操作类型的枚举`OperationType`,该枚举包含了两种基本的数据变更行为:更新(`UPDATE`) 和 插入 (`INSERT`) 。这有助于区分不同场景下的数据修改需求[^2]: ```java public enum OperationType { UPDATE, INSERT; } ``` #### 创建自定义注解 `@AutoFill` 接着设计一个名为`@AutoFill` 的自定义注解,用来标记那些希望对其执行自动填充逻辑的方法。此注解接受一个`OperationType` 类型的参数,指明其作用范围仅限于方法级别,并且会在运行期间保留以便后续反射机制能够访问到它: ```java import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { OperationType value(); } ``` #### 利用 AOP 进行拦截与处理 当某个带有上述注解的方法被执行时,Spring框架下配置好的AspectJ切面将会截获这次调用事件。此时可以从连接点对象(JoinPoint)中提取出关于此次调用的相关信息,比如方法签名、所带有的注解实例等。进而判断具体的业务逻辑应该怎样针对传入的对象实施属性赋值动作[^4]。 具体来说,会先取得当前正在执行的方法及其上的`@AutoFill` 注解;再依据注解内的指示决定是要做新增还是修改前后的特殊处理工作。对于每一个符合条件的目标实体,都可以按照预设规则为其某些成员变量设定默认初值或是基于上下文环境动态计算出来的适当数值。 例如下面这段伪代码展示了如何从JointPoint获取必要信息并对目标对象应用相应的填充策略: ```java // 假定已经获得了joinPoint对象 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象 AutoFill autoFillAnno = signature.getMethod().getAnnotation(AutoFill.class); // 获取方法上的注解对象 if(autoFillAnno != null){ Object[] args = joinPoint.getArgs(); // 参数列表 // ... 对args里的第一个参数进行操作 ... switch (autoFillAnno.value()){ case INSERT: // 执行插入之前的准备事项... break; case UPDATE: // 更新之前要做的准备工作... break; } } ``` #### 结合实际应用场景优化方案 考虑到性能因素以及用户体验方面的要求,还可以进一步改进整个流程的设计。例如提前准备好常用的基础数据缓存起来供快速查找使用,像用户身份验证成功之后就立即将相关信息存储至Redis当中去,这样每次涉及到需要补充此类辅助信息的地方就可以直接从中读取而无需每次都重新查询一次数据库[^5]。 通过以上步骤可以在很大程度上简化日常编码过程中频繁遇到的一些模式化任务,不仅提高了工作效率也增强了系统的可维护性和扩展能力。
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodingFire

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值