Information Security 17 min read

Implementing Database Field Encryption and Decryption with MyBatis Plugins

This article explains how to use MyBatis plugins to transparently encrypt specified database fields on write and decrypt them on read, covering the requirement background, system architecture, interceptor design, annotation usage, mapper adjustments, handling of pagination count queries, and practical implementation details.

Architecture Digest
Architecture Digest
Architecture Digest
Implementing Database Field Encryption and Decryption with MyBatis Plugins

The article introduces a solution for encrypting and decrypting certain database fields using MyBatis plugins to meet security compliance requirements while minimizing code intrusion.

1. Requirement Background – Sensitive data stored in plaintext must be protected; the project cannot be refactored extensively, so a MyBatis plugin is chosen to handle encryption on write and decryption on read.

2. Design Overview

2.1 System Architecture – Shows the overall flow of MyBatis execution, highlighting the four interceptable components: Executor, StatementHandler, ParameterHandler, and ResultSetHandler.

2.2 System Process – Describes how Spring creates SqlSessionFactory, parses configuration and mapper XML, scans mapper interfaces, creates proxy objects, and ultimately invokes Executor methods that delegate to StatementHandler, ParameterHandler, and ResultSetHandler.

3. Solution Formulation

3.1 MyBatis Plugin Basics

public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties);
}

The three methods are intercept (core logic), plugin (creates JDK dynamic proxy), and setProperties (reads configuration parameters).

3.2 Spring‑MyBatis Execution Flow – Outlines how SqlSessionFactoryBean loads config, how MappedStatement is built, how MapperFactoryBean creates proxies, and how MapperMethod executes SQL via Executor.

public Object execute(SqlSession sqlSession, Object[] args) {
  // simplified logic for INSERT/UPDATE/DELETE/SELECT handling
}

3.3 Plugin Creation Timing – Plugins are applied in Configuration methods newExecutor , newStatementHandler , newParameterHandler , and newResultSetHandler . Example for StatementHandler:

StatementHandler statementHandler = new RoutingStatementHandler(...);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;

3.4 Interceptable Classes – Executor (executes SQL), StatementHandler (creates and holds ParameterHandler/ResultSetHandler), ParameterHandler (sets parameters), ResultSetHandler (converts ResultSet to Java objects).

Because encryption adds a new encrypted column, the plugin must intercept Executor’s update and query methods; ResultSetHandler or Executor can handle decryption.

4. Implementation Steps

4.1 Define a method‑level annotation to specify source and destination keys:

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface TEncrypt {
    String[] srcKey() default {};
    String[] destKey() default {};
}

4.2 Annotate DAO methods that need encryption, e.g.:

public interface UserMapper {
    @TEncrypt(srcKey = {"secret"}, destKey = {"secretCiper"})
    List
selectUserList(UserInfo userInfo);
}

4.3 Modify the corresponding mapper.xml to map both the plaintext and encrypted columns.

<resultMap id="BaseResultMap" type="com.xxx.internet.demo.entity.UserInfo">
  <id column="secret" property="secret" />
  <id column="secret_ciper" property="secretCiper" />
  ...
</resultMap>

<select id="selectUserList" resultMap="BaseResultMap" parameterType="com.xxx.internet.demo.entity.UserInfo">
  SELECT * FROM `t_user_info`
  <where>
    <if test="secret != null"> AND `secret` = #{secret} </if>
    <if test="secretCiper != null"> AND `secret_ciper` = #{secretCiper} </if>
  </where>
  ORDER BY `update_time` DESC
</select>

4.4 Implement the Executor interceptor:

@Intercepts({
  @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
  @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorEncryptInterceptor implements Interceptor {
    // constants omitted for brevity
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object param = args[1];
        String id = ms.getId();
        String className = id.substring(0, id.lastIndexOf('.'));
        String methodName = id.substring(id.lastIndexOf('.') + 1);
        if (methodName.endsWith("_COUNT")) {
            methodName = methodName.substring(0, methodName.length() - 6);
        }
        for (Method m : Class.forName(className).getMethods()) {
            if (m.getName().equalsIgnoreCase(methodName) && m.isAnnotationPresent(TEncrypt.class)) {
                TEncrypt ann = m.getAnnotation(TEncrypt.class);
                if (param instanceof Map) {
                    // handle map parameters
                } else {
                    // handle POJO parameters
                }
            }
        }
        return invocation.proceed();
    }
}

The interceptor extracts the method’s @TEncrypt annotation, determines which fields need encryption, and updates the parameter object before the SQL is bound.

5. Challenges

5.1 Pagination plugins (e.g., PageHelper) generate an additional *_COUNT query without the annotation, causing mismatched counts. The interceptor detects the _COUNT suffix, strips it, and re‑checks the original method for the annotation, ensuring the count query also encrypts the appropriate field.

6. Conclusion

The article demonstrates a lightweight, annotation‑driven MyBatis plugin that encrypts designated fields on write and decrypts them on read, providing a practical security layer with minimal impact on existing business code while deepening the author’s understanding of MyBatis internals.

JavapluginMyBatissecurityDatabase Encryption
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.