One-Line Config for Automatic Data Change Auditing with Spring Boot & MyBatis-Plus

The article presents a low‑intrusion, highly configurable data‑audit plugin for Spring Boot that leverages MyBatis‑Plus interceptors to automatically capture before‑and‑after snapshots of INSERT, UPDATE, and DELETE operations, offering extensible logging, masking, async handling, and monitoring for enterprise systems.

LuTiao Programming
LuTiao Programming
LuTiao Programming
One-Line Config for Automatic Data Change Auditing with Spring Boot & MyBatis-Plus

Data correctness and traceability are critical in enterprise systems because a single mis‑modified record can amplify risk. Manual logging in the Service layer is invasive, error‑prone, and costly to maintain.

Why use MyBatis‑Plus interceptors?

MyBatis‑Plus adds a mature plugin system on top of MyBatis, allowing non‑intrusive interception of SQL execution. The interceptable core components are Executor, StatementHandler, ParameterHandler, and ResultSetHandler. The Executor is the optimal entry point because every INSERT, UPDATE, and DELETE passes through it.

Overall design

The solution is decomposed into five steps:

Intercept write operations (INSERT/UPDATE/DELETE).

Parse the SQL to obtain the table name and operation type.

Capture before‑ and after‑data snapshots.

Persist an audit log (supporting masking and asynchronous writes).

Provide query, monitoring, and alert capabilities.

The flow adds almost no code intrusion to business logic.

Audit entity definition

package com.icoderoad.audit.entity;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "data_audit_log")
public class DataAuditLog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String tableName;          // 被操作的表名
    private String operationType;      // INSERT / UPDATE / DELETE
    private String recordId;           // 业务主键标识
    private String beforeData;         // 变更前数据快照(JSON)
    private String afterData;          // 变更后数据快照(JSON)
    private String operator;          // 操作人
    private String operationTime;      // 操作时间
    private String businessInfo;       // 业务描述信息
}

Core interceptor implementation

package com.icoderoad.audit.interceptor;

import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Component
public class DataAuditInterceptor implements Interceptor {
    @Autowired
    private DataAuditService dataAuditService;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        String operationType = ms.getSqlCommandType().name();
        String sql = ms.getBoundSql(parameter).getSql();
        String tableName = SqlParser.getTableName(sql);

        // before snapshot for UPDATE/DELETE
        String beforeData = null;
        if ("UPDATE".equals(operationType) || "DELETE".equals(operationType)) {
            beforeData = JSON.toJSONString(parameter);
        }

        // execute original SQL
        Object result = invocation.proceed();

        // after snapshot for INSERT/UPDATE
        String afterData = null;
        if ("INSERT".equals(operationType) || "UPDATE".equals(operationType)) {
            afterData = JSON.toJSONString(parameter);
        }

        DataAuditLog auditLog = new DataAuditLog();
        auditLog.setTableName(tableName);
        auditLog.setOperationType(operationType);
        auditLog.setBeforeData(beforeData);
        auditLog.setAfterData(afterData);
        auditLog.setOperator(CurrentUserUtil.getUsername());
        auditLog.setOperationTime(LocalDateTime.now().toString());
        dataAuditService.saveAuditLog(auditLog);
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}

SQL parsing utility

package com.icoderoad.audit.parser;

public class SqlParser {
    public static String getTableName(String sql) {
        sql = sql.toUpperCase().trim();
        if (sql.startsWith("INSERT INTO")) {
            return sql.split("\\s+")[2];
        }
        if (sql.startsWith("UPDATE")) {
            return sql.split("\\s+")[1];
        }
        if (sql.startsWith("DELETE FROM")) {
            return sql.split("\\s+")[2];
        }
        return "UNKNOWN_TABLE";
    }
}
In production you may replace the simple parser with professional libraries such as JSqlParser .

Additional capabilities

Data masking utility that obscures sensitive fields (phone numbers, IDs, emails).

Asynchronous log persistence using Spring's @Async to reduce latency.

Custom annotation @DataAudit to control audit scope at method level.

package com.icoderoad.audit.util;

public class DataMaskingUtil {
    public static String mask(String data) {
        if (data == null) return null;
        data = data.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        data = data.replaceAll("(\\d{6})\\d{8}(\\w{4})", "$1********$2");
        data = data.replaceAll("(\\w{2})\\w+@(\\w+)", "$1***@$2");
        return data;
    }
}
package com.icoderoad.audit.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncDataAuditService {
    @Async
    public void saveAsync(DataAuditLog auditLog) {
        saveAuditLog(auditLog);
    }
}
package com.icoderoad.audit.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataAudit {
    String value() default "";
    boolean enable() default true;
}

Performance and security recommendations

Batch insert audit logs to reduce write overhead.

Use message queues (Kafka, RabbitMQ) for asynchronous decoupling.

Periodically archive historical audit data.

Optimize indexes on the audit table.

Control access permissions for audit data.

Encrypt sensitive fields before storage.

Prevent tampering of audit logs and consider meta‑auditing of the logs themselves.

Real‑world usage examples

Financial system – fund movement audit

@Service
public class AccountService {
    @DataAudit("账户资金变动")
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 资金转账逻辑
    }
}

E‑commerce system – product info changes

@Service
public class ProductService {
    @DataAudit("商品信息更新")
    public void updateProduct(Product product) {
        // 更新商品
    }
}

Monitoring and alert integration

package com.icoderoad.audit.metrics;

import io.micrometer.core.instrument.*;
import org.springframework.stereotype.Component;

@Component
public class AuditMetrics {
    private final MeterRegistry registry;
    public AuditMetrics(MeterRegistry registry) { this.registry = registry; }
    public void record(String type) {
        Counter.builder("data.audit.events")
               .tag("operation", type)
               .register(registry)
               .increment();
    }
}

Conclusion

The approach achieves non‑intrusive data‑change tracking, a configurable and extensible audit capability, while balancing performance, security, and compliance requirements. Leveraging Spring Boot and MyBatis‑Plus interceptors enables developers to add this foundation to production‑grade systems with minimal effort.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendJavainterceptoraudit-loggingspring-bootdata-auditmybatis-plus
LuTiao Programming
Written by

LuTiao Programming

LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.

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.