Mastering MyBatis Interceptors: Custom Encryption, Decryption, and Parameter Handling

This article explains MyBatis interceptors, showing how to intercept SQL execution, parameter setting, and result processing, and provides step‑by‑step code for custom encryption/decryption annotations, a parameter interceptor, a result‑set interceptor, and the supporting IEncryptDecrypt interface.

Programmer DD
Programmer DD
Programmer DD
Mastering MyBatis Interceptors: Custom Encryption, Decryption, and Parameter Handling

Interceptor Introduction

Mybatis Interceptor

is treated as a plugin in MyBatis, located under org.apache.ibatis.plugin.

Interceptors can intercept execution of SQL statements, request parameters, and return values when MyBatis reaches the persistence layer.

Interceptor Usage

To implement a custom interceptor, implement org.apache.ibatis.plugin.Interceptor with three methods:

Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);

Example code for a parameter handling interceptor that encrypts data before setting parameters:

@Intercepts({
    @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)
})
@ConditionalOnProperty(value = "domain.encrypt", havingValue = "true")
@Component
@Slf4j
public class ParameterInterceptor implements Interceptor {
    @Autowired
    private IEncryptDecrypt encryptDecrypt;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("Interceptor ParamInterceptor");
        if (invocation.getTarget() instanceof ParameterHandler) {
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
            PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
            Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
            parameterField.setAccessible(true);
            Object parameterObject = parameterField.get(parameterHandler);
            if (Objects.nonNull(parameterObject)) {
                Class<?> parameterObjectClass = parameterObject.getClass();
                EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class);
                if (Objects.nonNull(encryptDecryptClass)) {
                    Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                    Object encrypt = encryptDecrypt.encrypt(declaredFields, parameterObject);
                }
            }
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // no custom properties
    }
}

Similarly, a result‑set interceptor can decrypt data after query execution:

@Intercepts({
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@ConditionalOnProperty(value = "domain.decrypt", havingValue = "true")
@Component
@Slf4j
public class ResultInterceptor implements Interceptor {
    @Autowired
    private IEncryptDecrypt encryptDecrypt;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (Objects.isNull(result)) {
            return null;
        }
        if (result instanceof ArrayList) {
            ArrayList resultList = (ArrayList) result;
            if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                for (int i = 0; i < resultList.size(); i++) {
                    encryptDecrypt.decrypt(resultList.get(i));
                }
            }
        } else {
            if (needToDecrypt(result)) {
                encryptDecrypt.decrypt(result);
            }
        }
        return result;
    }

    public boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);
        return Objects.nonNull(encryptDecryptClass);
    }

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

    @Override
    public void setProperties(Properties properties) {
        // no custom properties
    }
}

Custom Annotations

Define class‑level and field‑level annotations to mark entities and fields that require encryption/decryption:

@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptClass {
}
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptField {
}

Encryption/Decryption Interface

The IEncryptDecrypt interface declares methods for encrypting parameters and decrypting results:

public interface IEncryptDecrypt {
    <T> T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException;
    <T> T decrypt(T result) throws IllegalAccessException;
}

Both interceptors are conditionally loaded via YAML properties, enabling flexible global configuration of encryption and decryption logic.

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.

JavaSpring BootMyBatisencryptiondecryption
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.