SoFunction
Updated on 2025-03-03

SpringBoot implements two ways to automatically fill fields

Fields such as creatby, updateby, etc. are automatically filled

When inserting each field into the database or updating, it is necessary to fill fields such as creatby, updateby, etc. at the serviceimpl layer. This is too cumbersome. The following two methods can realize automatic filling of fields. This project uses the first type.

Method 1:

First create an AutoFillInterceptor class. The code will be analyzed line by line below.
The following code can also be copied and pasted directly, but only if the fields in your entity class are the same as the following four static constant names.

@Component
@Intercepts({
        @Signature(type = , method = "update", args = {, })
})
public class AutoFillInterceptor implements Interceptor {


    private static final String CREATE_BY = "createdBy";
    private static final String UPDATE_BY = "updatedBy";

    private static final String CREATE_TIME = "createdAt";
    private static final String UPDATE_TIME = "updatedAt";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = ();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ();
        Object parameter = args[1];
        if(parameter != null && sqlCommandType!=null){
            if((sqlCommandType)){
                if(parameter instanceof ){
                     paramMap = () parameter;
                    ArrayList list= (ArrayList) ("list");
                    (v->{
                        setFieldValByName(CREATE_TIME, (), v);
                        setFieldValByName(UPDATE_TIME, (), v);
                    });
                    ("list", list);
                }else{
                    // Single insertion                    // Set the create person and create time field values                    setFieldValByName(CREATE_TIME, (), parameter);
                    setFieldValByName(UPDATE_TIME, (), parameter);
                }
            }
            else if((sqlCommandType)){
                // Update operation                // Set the update person and update time field values                setFieldValByName(UPDATE_TIME, (), parameter);
            }
        }

        // Continue to execute the original method        return ();
    }

    private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {
        MetaObject metaObject = (parameter);

        if ((fieldName)) {
            (fieldName, fieldVal);
        }
    }

    @Override
    public void setProperties(Properties properties) {
        (properties);
    }

    @Override
    public Object plugin(Object target) {
        return (target);
    }
}

Code structure and function

This is an implementation of MyBatis interceptor (InterceptorInterface classAutoFillInterceptor, used to automatically fill some common fields when performing SQL operations (INSERT or UPDATE), such as creation time (createdAt), update time (updatedAt)wait.

In enterprise-level projects, it is usually necessary to record the creation time and modification time of data. This interceptor is to solve this requirement and automatically fill these fields when adding and modifying data. Let's analyze the code line by line.

Line by line analysis

@Component
@Intercepts({
        @Signature(type = , method = "update", args = {, })
})
  • @Component: Spring annotation, register this class as a Spring bean for easy management.
  • @Intercepts: MyBatis annotation, declares that this is an interceptor and specifies the target to be intercepted.
    • @Signature: Define the specific interception method of the interceptor.
      • type = : Indicates intercepting MyBatisExecutorkind.
      • method = "update": Indicates interceptionupdateMethod, this method is used to perform update operations (including INSERT, UPDATE, DELETE).
      • args = {, }:SpecifyupdateThe parameter type of the method, that is, SQL mapping informationMappedStatementand parameter objectsObject
public class AutoFillInterceptor implements Interceptor {
  • These lines define some constants that represent field names, such as creator, modifyer, creation time, and modification time. These constants will be used in intercept logic to automatically populate fields.
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = ();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ();
        Object parameter = args[1];
  • interceptThe method is the core logic of the interceptor.
    • Object[] args = (): Get parameters of the intercept method.
    • MappedStatement ms = (MappedStatement) args[0]: GetMappedStatement, contains information about SQL statements.
    • SqlCommandType sqlCommandType = (): Get the operation type of SQL (INSERT, UPDATE, DELETE).
    • Object parameter = args[1]: Get parameter objects, usually data that the user wants to insert or update.
        if(parameter != null && sqlCommandType != null){
  • Check whether the parameters are empty and confirm whether the operation type is not empty to ensure that it is necessary to continue to perform subsequent operations.
            if((sqlCommandType)){
                if(parameter instanceof ){
                     paramMap = () parameter;
                    ArrayList list= (ArrayList) ("list");
                    (v -> {
                        setFieldValByName(CREATE_TIME, (), v);
                        setFieldValByName(UPDATE_TIME, (), v);
                    });
                    ("list", list);
                } else {
                    // Single insertion                    // Set the create person and create time field values                    setFieldValByName(CREATE_TIME, (), parameter);
                    setFieldValByName(UPDATE_TIME, (), parameter);
                }
            }
  • if ((sqlCommandType)): If the current SQL is an INSERT operation:
    • if (parameter instanceof ): Determine whether the parameter istype, which is usually used for batch insertion.
      • ArrayList list = (ArrayList) ("list"): Get the name from the parameter MaplistThe parameter is a set of data inserted in batches.
      • (v -> {...}): Perform operations on each element and callsetFieldValByNameMethod SettingscreatedAtandupdatedAtis the current time.
    • elsePart: Handle the case of single insertion, give it directlyparameterObject settings creation time and update time.
            else if((sqlCommandType)){
                // Update operation                // Set the update person and update time field values                setFieldValByName(UPDATE_TIME, (), parameter);
            }
  • else if ((sqlCommandType)): If the current SQL is a UPDATE operation:
    • usesetFieldValByNameMethod willupdatedAtThe field is set to the current time.
        }

        // Continue to execute the original method        return ();
    }
  • Finally passed()Call the intercepted method and continue to perform the original database operation.
    private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {
        MetaObject metaObject = (parameter);

        if ((fieldName)) {
            (fieldName, fieldVal);
        }
    }
  • setFieldValByNameMethods are used to set the value of the specified field in the object:
    • MetaObject metaObject = (parameter):createMetaObject, used to manipulate the metadata of the incoming object.
    • if ((fieldName)): Check whether the object has a setter method for corresponding fields.
    • (fieldName, fieldVal): If there is a setter method, set the value of the field.
    @Override
    public void setProperties(Properties properties) {
        (properties);
    }

    @Override
    public Object plugin(Object target) {
        return (target);
    }
}
  • setPropertiesandpluginThe method isInterceptorThe default implementation of the interface,pluginMethods are used to generate proxy objects.

Summarize

  • The function of this interceptor is to fill automaticallycreatedAtandupdatedAtFields to automatically record creation and update times when INSERT and UPDATE operations are performed.
  • Main interceptionExecutorofupdateMethod: determine whether it is an INSERT or UPDATE operation by determining the SQL type, thereby setting the corresponding field.
  • Using MyBatisMetaObjectTool class to dynamically operate the field values ​​of parameter objects.

With this interceptor, developers do not need to manually set it in business codecreatedAtandupdatedAt, greatly reduces duplicate code and ensures the consistency and correctness of these public fields.

Method 2:

This method is written using custom annotations, so add this annotation to the SQL that needs to be filled. This may be more flexible and simple.

Public fields are automatically filled

Technical points: enumeration, annotation, AOP, reflection
Create time, modify time, create person, and modify person.
Annotate the mapper method AutoFill, and the identification needs to be automatically filled with public fields
Customize the section class AutoFillAspect, and unified intercepts the method of adding AutoFill annotation, assigning values ​​to public fields through reflection.
Connect AutoFill annotations to the Mapper method.

public enum OperationType {
    Update operation
    UPDATE,
    Insert operation
    INSERT
}

@Target()Where is the current annotation added
@Retention()
public @interface AutoFill {
    //Database operation type: UPDATE INSERT    OperationType value();
}

Supplementary annotation basic knowledge

public @interface MyAnnotation {
    // Define the members of the annotation    String value(); // This is a member named "value"    int count() default 1; // This is a member named "count" with default values}

@MyAnnotation(value = "Hello", count = 3)
public class MyClass {
    // Class code}

For AutoFillAspect classPoint-cutting, execution expression

/**
  * Customize the section to realize the automatic filling processing logic of common fields
  */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {


    /**
      * Point of entry
      */
     									All classes,All methods,All parameter types
    @Pointcut("execution(* .*.*(..)) && @annotation()")
    public void autoFillPointCut(){}

    /**
      * Pre-notification, assign public fields in the notification
      */
    @Before("autoFillPointCut()")Specify entry point
    public void autoFill(JoinPoint joinPoint){Connection point
        ("Start the public field autofill...");

        //Get the database operation type on the currently intercepted method        MethodSignature signature = (MethodSignature) ();//Method Signature Object        AutoFill autoFill = ().getAnnotation();//Get the annotation object on the method        OperationType operationType = ();//Get database operation type
        //Get the parameters of the currently intercepted method - Entity object Make a convention and place the first entity object        Object[] args = ();
        if(args == null ||  == 0){
            return;
        }

        Object entity = args[0];entity

        //Prepare the assigned data        LocalDateTime now = ();
        Long currentId = ();

        //Depend on the current different operation types, the corresponding attributes are assigned through reflection to the corresponding attributes        if(operationType == ){
            // Assign values ​​to 4 public fields            try {
                Method setCreateTime = ().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, );
                Method setCreateUser = ().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, );
                Method setUpdateTime = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, );
                Method setUpdateUser = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, );

                // Assign value to object attributes through reflection                (entity,now);
                (entity,currentId);
                (entity,now);
                (entity,currentId);
            } catch (Exception e) {
                ();
            }
        }else if(operationType == ){
            // Assign values ​​to 2 public fields            try {
                Method setUpdateTime = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, );
                Method setUpdateUser = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, );

                // Assign value to object attributes through reflection                (entity,now);
                (entity,currentId);
            } catch (Exception e) {
                ();
            }
        }
    }

}

use

@AutoFill(value = )
void update(Employee employee);

Customized sections: Implement automatic filling of common fields

This code uses Spring AOP (sectional programming) to automatically fill in some common fields when operating the database, such as creation time, update time, create person, update person, etc. Next, we parse this code line by line to help you understand the functions and implementation logic of each part.

Code structure overview

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    // entry point    @Pointcut("execution(* .*.*(..)) && @annotation()")
    public void autoFillPointCut(){}

    // Pre-Notice    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        ("Start the public field autofill...");
        ...
    }
}

This code defines a sectionAutoFillAspect, it will use a pre-notification before the database operation method that meets the criteria is executed (@Before) Automatically populate certain public fields.

Annotation explanation

  1. @Aspect: Indicates that the current class is a facet class that defines notifications and entry points.
  2. @Component: Register this aspect class as a component in the Spring container.
  3. @Slf4j: Used to enable logging function to facilitate debugging and logging information.

Point-cut definition

@Pointcut("execution(* .*.*(..)) && @annotation()")
public void autoFillPointCut(){}

explain:

  • @Pointcut: Used to define an entry point and describe which methods need to be intercepted by facet logic.
  • execution(* .*.*(..)): MatchAll classes and methods under the package,(..)Represents any parameter type and quantity.
  • && @annotation(): It means only intercepting@AutoFillThe method of annotating marks.

Through this definition, only the classes that meet the specified package and have@AutoFillThe annotation method will be intercepted by the facet logic.

Before Advice

@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
    ("Start the public field autofill...");
    ...
}
  • @Before("autoFillPointCut()"): This is a pre-notice, indicating that the autoFill() method is executed before the method matched by the point-cutting is executed.
  • JoinPoint joinPoint: JoinPoint is a connection point that represents the intercepted method, allowing some information about the target method, such as method name and parameters.

Get annotation and method information

MethodSignature signature = (MethodSignature) ();
AutoFill autoFill = ().getAnnotation();
OperationType operationType = ();
  1. MethodSignature signature = (MethodSignature) ();: Get the signature information of the currently intercepted method and convert it toMethodSignaturetype.
  2. AutoFill autoFill = ().getAnnotation();: Obtain the method@AutoFillAnnotate the object.
  3. OperationType operationType = ();: Gets the database operation type specified in the annotation (for example, INSERT or UPDATE).

Get method parameters

Object[] args = ();
if (args == null ||  == 0) {
    return;
}
Object entity = args[0];
  • Object[] args = ();: Get the parameters of the currently intercepted method.
  • if (args == null || == 0): If there are no parameters, return directly.
  • Object entity = args[0];: Assume that the first parameter is an entity object, used to operate the database. There is a convention here, that is, entity objects are always the first parameter.

Prepare the assigned data

LocalDateTime now = ();
Long currentId = ();
  • LocalDateTime now = ();: Get the current time to fill in the creation time and update time.
  • Long currentId = ();: Get the ID of the current operation user to populate the creator and updater information.

Assign values ​​according to operation type

Insert operation (INSERT)

if (operationType == ) {
    try {
        Method setCreateTime = ().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, );
        Method setCreateUser = ().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, );
        Method setUpdateTime = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, );
        Method setUpdateUser = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, );

        (entity, now);
        (entity, currentId);
        (entity, now);
        (entity, currentId);
    } catch (Exception e) {
        ();
    }
}
  • if (operationType == ): If the database operation type is Insert (INSERT).
  • Obtain the entity class through reflectionsetCreateTimesetCreateUsersetUpdateTimeandsetUpdateUsermethod.
  • invoke()Methods are used to call these setter methods and pass in corresponding values ​​to complete the assignment of public fields.

Update operation (UPDATE)

else if (operationType == ) {
    try {
        Method setUpdateTime = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, );
        Method setUpdateUser = ().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, );

        (entity, now);
        (entity, currentId);
    } catch (Exception e) {
        ();
    }
}
  • else if (operationType == ): If the operation type is Update (UPDATE).
  • Here you just need to fill in the fields related to update, that is, the update time and the update person.

summary

This code implements automatic filling of common fields for database operations, as follows:

  • Define a sectionAutoFillAspect, used to intercept methods in specific packages, and the method needs to be@AutoFillAnnotations are marked.
  • Use AOP's pre-notification to auto-fill fields before method execution.
  • The method of obtaining entity objects through reflection mechanism and assigning values, filling different fields according to the operation type.

This makes the code more concise and maintainable, reduces duplicate public field assignment logic, and facilitates consistency management of public attributes such as creation time and update time.

The above is the detailed content of two ways to implement automatic field filling in SpringBoot. For more information about automatic field filling in SpringBoot, please pay attention to my other related articles!