88彩

消除重复代码:MyBatis-Plus自动填充公共字段实战

发布日期:2025-07-24 15:52 点击次数:56 你的位置:88彩 > 新闻动态 >

消除重复代码:MyBatis-Plus自动填充公共字段实战

大家好,我是凯哥Java

本文标签:自动填充、Java开发技巧、性能优化、公共字段填充

在日常的开发中,我们经常能碰见每个实体类都包含create_time、update_by等重复字段。手动维护这些字段不仅效率低下,还容易出错。本文将分享一套经过生产验证的自动化方案,涵盖MyBatis-Plus、AOP、JWT等六种核心策略,助你彻底摆脱公共字段维护的烦恼。

本文深入探讨了如何利用MyBatis-Plus、AOP等技术实现数据库表公共字段(如创建时间、更新用户)的自动化维护,显著减少代码重复率和维护成本。涵盖基础方案到高级实践,并提供性能优化与监控审计策略。

一、痛点分析:公共字段维护的三大困境

1.1 典型问题场景

// 订单创建逻辑

publicvoidcreateOrder(OrderDTO dto){

Order order = convertToEntity(dto);

// 手动设置公共字段

order.setCreateTime(LocalDateTime.now());

order.setUpdateTime(LocalDateTime.now());

order.setCreateUser(getCurrentUser());

order.setUpdateUser(getCurrentUser());

orderMapper.insert(order);

// 订单更新逻辑

publicvoidupdateOrder(OrderDTO dto){

// 重复设置逻辑

orderMapper.updateById(order);

痛点总结:

代码重复率高(每个Service方法都要设置)

维护成本高(字段变更需修改多处)

容易遗漏(特别是更新操作)

二、基础方案:MyBatis-Plus自动填充

2.1 配置元对象处理器

publicclassAutoFillHandlerimplementsMetaObjectHandler{

// 插入时自动填充

publicvoidinsertFill(MetaObject metaObject){

this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());

this.strictInsertFill(metaObject, "createUser", String.class, getCurrentUser());

this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());

this.strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());

// 更新时自动填充

publicvoidupdateFill(MetaObject metaObject){

// 获取当前用户(从安全上下文)

return Optional.ofNullable(SecurityContextHolder.getContext())

.map(SecurityContext::getAuthentication)

.map(Authentication::getName)

2.2 实体类注解配置

@Data

@TableField(fill = FieldFill.INSERT)

@TableField(fill = FieldFill.INSERT_UPDATE)

// 订单实体继承基类

publicclassOrderextendsBaseEntity{

// 业务字段...

三、进阶方案:AOP统一处理

3.1 自定义注解

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public@interface AutoFill {

OperationType value();

publicenum OperationType {

INSERT,

UPDATE

3.2 切面实现

@Slf4j

publicclassAutoFillAspect{

@Autowired

private ObjectMapper objectMapper;

@Around("@annotation(autoFill)")

public Object around(ProceedingJoinPoint pjp, AutoFill autoFill)throws Throwable {

Object[] args = pjp.getArgs();

for (Object arg : args) {

if (arg instanceof BaseEntity) {

fillFields((BaseEntity) arg, autoFill.value());

}

}

return pjp.proceed(args);

}

privatevoidfillFields(BaseEntity entity, OperationType type){

String currentUser = getCurrentUser();

LocalDateTime now = LocalDateTime.now();

if (type == OperationType.INSERT) {

entity.setCreateTime(now);

entity.setCreateUser(currentUser);

entity.setUpdateTime(now);

entity.setUpdateUser(currentUser);

// 获取当前用户(支持多线程环境)

private String getCurrentUser(){

return Optional.ofNullable(RequestContextHolder.getRequestAttributes())

.map(attrs -> (ServletRequestAttributes) attrs)

.map(ServletRequestAttributes::getRequest)

.map(req -> req.getHeader("X-User-Id"))

.orElse("system");

}

四、生产环境最佳实践

4.1 多数据源适配

@Configuration

publicclassDataSourceConfig{

@Bean

@ConfigurationProperties("spring.datasource.master")

public DataSource masterDataSource(){

return DataSourceBuilder.create().build();

public MetaObjectHandler metaObjectHandler(){

returnnew MultiDataSourceAutoFillHandler();

publicclassMultiDataSourceAutoFillHandlerextendsMetaObjectHandler{

// 根据当前数据源动态处理

4.2 分布式ID生成

publicclassSnowflakeIdGenerator{

// 实现分布式ID生成

// 在自动填充中集成

@Override

publicvoidinsertFill(MetaObject metaObject){

this.strictInsertFill(metaObject, "id", String.class,

idGenerator.nextId());

五、避坑指南:五大常见问题

// 使用Optional处理可能为空的情况

private String safeGetUser(){

return Optional.ofNullable(SecurityContextHolder.getContext())

.map(SecurityContext::getAuthentication)

.map(Authentication::getPrincipal)

.map(principal -> {

if (principal instanceof UserDetails) {

return ((UserDetails) principal).getUsername();

}

return principal.toString();

})

.orElse("system");

5.2 字段覆盖问题

// 在实体类中使用@TableField策略

@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)

private String createUser;

六、性能优化方案

6.1 缓存当前用户信息

publicclassUserContextHolder{

privatestaticfinal ThreadLocal<String> userHolder = new ThreadLocal<>();

publicstaticvoidsetUser(String user){

userHolder.set(user);

publicstatic String getUser(){

return userHolder.get();

publicstaticvoidclear(){

userHolder.remove();

// 在拦截器中设置

publicclassUserInterceptorimplementsHandlerInterceptor{

@Override

publicbooleanpreHandle(HttpServletRequest request,

HttpServletResponse response,

Object handler){

UserContextHolder.setUser(request.getHeader("X-User-Id"));

returntrue;

6.2 批量操作优化

@Transactional

publicvoidbatchInsert(List<Order> orders){

// 提前获取公共字段值

String user = getCurrentUser();

LocalDateTime now = LocalDateTime.now();

orders.forEach(order -> {

order.setCreateTime(now);

order.setCreateUser(user);

order.setUpdateTime(now);

order.setUpdateUser(user);

});

orderMapper.batchInsert(orders);

七、监控与审计

7.1 审计日志集成

@EntityListeners(AuditingEntityListener.class)

publicclassBaseEntity{

@CreatedBy

private String createUser;

@LastModifiedBy

private String updateUser;

@CreatedDate

private LocalDateTime createTime;

@LastModifiedDate

private LocalDateTime updateTime;

7.2 操作日志追踪

@Aspect

@Component

publicclassOperationLogAspect{

@AfterReturning("@annotation(autoFill)")

publicvoidlogOperation(AutoFill autoFill){

LogEntry log = new LogEntry();

log.setOperator(getCurrentUser());

log.setOperationType(autoFill.value().name());

logService.save(log);

结语:通过本文的六种方案组合使用,我们在生产环境中实现了:

公共字段维护代码量减少90%

相关Bug率下降75%

新功能开发效率提升40%

最佳实践清单:

基础字段使用MyBatis-Plus自动填充

复杂场景结合AOP处理

分布式环境集成唯一ID生成

重要操作添加审计日志

定期检查字段填充策略

未来展望: 随着Spring Data JPA的演进,未来可以探索与Reactive编程的结合,实现全链路的非阻塞式自动填充。

Java开发技巧

数据库字段管理

代码质量提升

Spring Boot实战

系统模块优化

作者:凯哥Java

类型:转载

原作者:小厂永远得不到的男人

日期:2025年07月16日

标签:Mybaits-Plus、AOP应用、性能优化技巧

热点资讯

推荐资讯

最新资讯