How to Eliminate Repetitive Common Fields in Java Order Services with MyBatis‑Plus and AOP

This article analyzes the pain points of manually maintaining common fields in order‑related entities and presents a production‑tested solution that combines MyBatis‑Plus automatic filling, custom AOP annotations, multi‑data‑source handling, distributed ID generation, and auditing to dramatically reduce boilerplate code and bugs.

Java Web Project
Java Web Project
Java Web Project
How to Eliminate Repetitive Common Fields in Java Order Services with MyBatis‑Plus and AOP

Pain Points of Manual Common‑Field Maintenance

When developing an order module, each entity class contains fields such as createTime, updateTime, createUser and updateUser. Manually setting these fields causes three major problems:

High code duplication – every service method repeats the same assignments.

Maintenance cost – any field change requires updates in many places.

Easy omission – especially during update operations.

Basic Solution: MyBatis‑Plus Automatic Filling

MyBatis‑Plus provides a MetaObjectHandler interface that can automatically populate fields during insert or update.

public class AutoFillHandler implements MetaObjectHandler {
    // Insert fill
    @Override
    public void insertFill(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());
    }

    // Update fill
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        this.strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());
    }

    private String getCurrentUser() {
        return Optional.ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .map(Authentication::getName)
            .orElse("system");
    }
}

The base entity is annotated so MyBatis‑Plus knows when to fill each column:

@Data
public class BaseEntity {
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private String createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateUser;
}

public class Order extends BaseEntity {
    // business fields ...
}

Advanced Solution: AOP Unified Handling

For more complex scenarios a custom annotation @AutoFill and an aspect are introduced.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoFill {
    OperationType value();
}

enum OperationType { INSERT, UPDATE }

The aspect intercepts methods annotated with @AutoFill, extracts the target entity, and fills fields based on the operation type.

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    @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);
    }

    private void fillFields(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");
    }
}

Production‑Level Best Practices

Multi‑Data‑Source Adaptation

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MultiDataSourceAutoFillHandler();
    }
}

public class MultiDataSourceAutoFillHandler extends MetaObjectHandler {
    // implementation that selects the correct data source at runtime
}

Distributed ID Generation

public class SnowflakeIdGenerator {
    // implementation omitted for brevity
}

@Override
public void insertFill(MetaObject metaObject) {
    this.strictInsertFill(metaObject, "id", String.class, idGenerator.nextId());
}

Common Pitfalls and Their Fixes

Null‑Pointer Protection

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");
}

Field Overwrite Issue

@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)
private String createUser;

Performance Optimizations

Cache Current User Information

public class UserContextHolder {
    private static final ThreadLocal<String> userHolder = new ThreadLocal<>();
    public static void setUser(String user) { userHolder.set(user); }
    public static String getUser() { return userHolder.get(); }
    public static void clear() { userHolder.remove(); }
}

public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        UserContextHolder.setUser(request.getHeader("X-User-Id"));
        return true;
    }
}

Batch Insert Optimization

@Transactional
public void batchInsert(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);
}

Monitoring and Auditing

JPA Auditing Annotations

@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    @CreatedBy
    private String createUser;
    @LastModifiedBy
    private String updateUser;
    @CreatedDate
    private LocalDateTime createTime;
    @LastModifiedDate
    private LocalDateTime updateTime;
}

An aspect can log operations after successful method execution.

@Aspect
@Component
public class OperationLogAspect {
    @AfterReturning("@annotation(autoFill)")
    public void logOperation(AutoFill autoFill) {
        LogEntry log = new LogEntry();
        log.setOperator(getCurrentUser());
        log.setOperationType(autoFill.value().name());
        logService.save(log);
    }
}

Results

Combining the six strategies—MyBatis‑Plus auto‑fill, AOP annotation, multi‑data‑source handling, distributed ID generation, user‑context caching, and auditing—produced the following measurable improvements in production:

Common‑field code reduced by ~90%.

Related bugs decreased by ~75%.

New‑feature development speed increased by ~40%.

backendJavaAOPSpring BootMyBatis-PlusAuditingAutomatic Field Fill
Java Web Project
Written by

Java Web Project

Focused on Java backend technologies, trending internet tech, and the latest industry developments. The platform serves over 200,000 Java developers, inviting you to learn and exchange ideas together. Check the menu for Java learning resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.