Backend Development 9 min read

Automating Common Entity Fields in Java Backend with MyBatis-Plus, AOP, and JWT

This article presents a production‑tested solution for automatically handling common entity fields such as createTime, updateTime, createUser, and updateUser in Java backend systems, combining MyBatis‑Plus auto‑fill, AOP interceptors, custom annotations, multi‑datasource support, distributed ID generation, and auditing techniques to eliminate repetitive code and reduce bugs.

Architecture Digest
Architecture Digest
Architecture Digest
Automating Common Entity Fields in Java Backend with MyBatis-Plus, AOP, and JWT

When developing an order module in a delivery system, each entity class often contains repeated fields like create_time and update_by . Manually maintaining these fields is inefficient and error‑prone.

This article shares a production‑tested automation scheme that includes six core strategies: MyBatis‑Plus automatic filling, AOP, JWT, multi‑datasource adaptation, distributed ID generation, and auditing.

1. Pain points

High code duplication – every Service method must set the fields manually.

High maintenance cost – field changes require modifications in many places.

Easy to miss – especially during update operations.

2. Basic solution: MyBatis‑Plus automatic filling

2.1 Configure MetaObjectHandler

// 订单创建逻辑
public void createOrder(OrderDTO dto) {
    Order order = convertToEntity(dto);

    // 手动设置公共字段
    order.setCreateTime(LocalDateTime.now());
    order.setUpdateTime(LocalDateTime.now());
    order.setCreateUser(getCurrentUser());
    order.setUpdateUser(getCurrentUser());

    orderMapper.insert(order);
}

// 订单更新逻辑 
public void updateOrder(OrderDTO dto) {
    Order order = convertToEntity(dto);

    // 重复设置逻辑
    order.setUpdateTime(LocalDateTime.now());
    order.setUpdateUser(getCurrentUser());

    orderMapper.updateById(order);
}

2.2 Entity class annotation configuration

@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 {
    // 业务字段...
}

3. Advanced solution: AOP unified handling

3.1 Custom annotation

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

public enum OperationType {
    INSERT,
    UPDATE
}

3.2 Aspect implementation

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

4. Production best practices

4.1 Multi‑datasource 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 {
    // 根据当前数据源动态处理
}

4.2 Distributed ID generation

public class SnowflakeIdGenerator {
    // 实现分布式ID生成
}

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

5. Pitfall guide

Null‑pointer protection using Optional to safely obtain the current user.

Field overwrite issues solved by setting updateStrategy = FieldStrategy.NEVER on @TableField .

6. Performance optimization

6.1 Cache current user information

public class UserContextHolder {
    private static final ThreadLocal
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;
    }
}

6.2 Batch operation 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);
}

7. Monitoring and auditing

7.1 Auditing log integration

@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    @CreatedBy
    private String createUser;

    @LastModifiedBy
    private String updateUser;

    @CreatedDate
    private LocalDateTime createTime;

    @LastModifiedDate
    private LocalDateTime updateTime;
}

7.2 Operation log tracing

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

By combining these six solutions, the project reduced common‑field code by 90%, lowered related bug rates by 75%, and increased new‑feature development efficiency by 40%.

Best‑practice checklist: use MyBatis‑Plus auto‑fill for basic fields, combine AOP for complex scenarios, integrate unique ID generation in distributed environments, add audit logs for critical operations, and regularly review field‑fill strategies.

BackendJavaautomationAOPMyBatis-Plus
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login 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.