Automate Common Fields in Java Order Modules with MyBatis‑Plus, AOP & JWT
This article presents a production‑tested, six‑strategy solution—including MyBatis‑Plus auto‑fill, AOP, JWT, multi‑datasource handling, distributed ID generation, and audit logging—to eliminate repetitive maintenance of common fields in Java order modules, boosting efficiency and reducing bugs.
When developing an order module for a delivery system, each entity class often repeats fields such as create_time and update_by, making manual maintenance inefficient and error‑prone.
1. Pain points: three common dilemmas
1.1 Typical scenario
// Order creation logic
public void createOrder(OrderDTO dto) {
Order order = convertToEntity(dto);
// Manually set common fields
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(LocalDateTime.now());
order.setCreateUser(getCurrentUser());
order.setUpdateUser(getCurrentUser());
orderMapper.insert(order);
}
// Order update logic
public void updateOrder(OrderDTO dto) {
Order order = convertToEntity(dto);
// Repeated setting logic
order.setUpdateTime(LocalDateTime.now());
order.setUpdateUser(getCurrentUser());
orderMapper.updateById(order);
}Pain summary:
High code duplication (each service method must set the fields)
High maintenance cost (field changes require modifications in many places)
Easy to miss, especially in update operations
2. Basic solution: MyBatis‑Plus automatic filling
2.1 Configure meta‑object handler
@Slf4j
@Component
public class AutoFillHandler implements MetaObjectHandler {
// Insert auto‑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 auto‑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");
}
}2.2 Entity 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 {
// business fields ...
}3. Advanced solution: AOP unified handling
3.1 Custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoFill {
OperationType value();
}
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 {
// Dynamically handle based on current datasource
}4.2 Distributed ID generation
public class SnowflakeIdGenerator {
// Implement distributed ID generation
}
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "id", String.class, idGenerator.nextId());
}5. Pitfall guide: five common issues
5.1 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");
}5.2 Field overwrite issue
@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)
private String createUser;6. Performance optimization
6.1 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;
}
}6.2 Batch operation optimization
@Transactional
public void batchInsert(List<Order> orders) {
// Pre‑fetch common field values
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 Audit 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);
}
}Conclusion: By combining the six strategies presented, we achieved a 90% reduction in common‑field maintenance code, a 75% drop in related bugs, and a 40% boost in new‑feature development efficiency in production.
Best practice checklist:
Use MyBatis‑Plus auto‑fill for basic fields
Handle complex scenarios with AOP
Integrate unique ID generation in distributed environments
Add audit logs for critical operations
Periodically review field‑filling strategies
Future outlook: With the evolution of Spring Data JPA, exploring integration with reactive programming could enable fully non‑blocking, end‑to‑end automatic filling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
