Mastering Data Isolation in Java: MyBatis Interceptor & Custom Annotations

This article walks through the challenges of environment‑specific data isolation in a Java project, explains why adding an env column to every table is impractical, and presents a clean solution using a MyBatis interceptor that rewrites SQL and custom annotations to control filtering, ensuring safe, maintainable code across pre‑release, gray, and production environments.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Data Isolation in Java: MyBatis Interceptor & Custom Annotations

1. Historical Background

In the pre‑release, gray, and online environments a single database is shared, with each table containing an env column to distinguish data per environment.

1.1 Data Isolation

All three environments share the same tables, differentiated by the env field.

1.2 Before Isolation

Initially only one core table had the env column; later, about twenty tables needed the column to prevent pre‑release operations from affecting production data.

1.3 Isolation Refactor

Historical data cannot be easily distinguished, so the new env field is initialized to all, allowing legacy data to be accessed by all environments.

1.4 Isolation Scheme

The naive approach would be to add env to every DO, Mapper, and XML, but this is rejected.

1.5 Final Implementation

A custom MyBatis interceptor rewrites SQL: it fills the env value on inserts and adds an env condition on queries. Historical data is handled by using env in (${currentEnv},'all').

SELECT xxx FROM ${tableName} WHERE env in (${currentEnv},'all') AND ${otherCondition}

The interceptor reads the environment value from application.properties and modifies the SQL accordingly.

2. Development Evolution

2.1 Business Requirements

PRC interface needs environment‑specific matching.

Some environments share data (pre‑release and gray).

Developers want to correct online data from pre‑release.

2.2 Initial Communication

The junior developer asked how to skip environment checks; suggestions included ignoring the field, always appending conditions, or using annotations.

2.3 Implementation

Use a ThreadLocal‑based context to store the environment filter and let the interceptor apply it.

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Component
public class EnvIsolationInterceptor implements Interceptor {
    // ...
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // ...
        if (SqlCommandType.INSERT == sqlCommandType) {
            // handle insert
        }
        return invocation.proceed();
    }
}

2.4 Refactoring

After refactor, the code fills env with all environments, and queries add the appropriate filter.

SELECT * FROM ${tableName} WHERE env in ('pre','gray','online','all') AND ${otherCondition}

2.5 Error Causes

Null env values were caused by multiple ThreadLocal manipulations where a downstream method cleared the context before the upstream method retrieved it.

2.6 Observations

Environment‑related code proliferated across the codebase, making maintenance hard.

2.7 Code Spread

String oriFilterEnv = UserHolder.getUser().getFilterEnv();
UserHolder.getUser().setFilterEnv(globalConfigDTO.getAllEnv());
// ...
UserHolder.getUser().setFilterEnv(oriFilterEnv);

2.8 Soul Questions

Is this the only way? Could the Open/Closed principle be better respected? Are we mixing business logic with infrastructure concerns?

3. Refactoring

3.1 Challenges

The MyBatis interceptor cannot directly access service‑layer methods, requiring stack‑frame inspection.

3.2 Problem List

Avoid modifying existing methods.

Separate concerns between business and infrastructure.

Make changes in a single place.

Promote reuse instead of copy‑pasting.

3.3 Implementation Analysis

Use an independent ThreadLocal for environment filtering.

Define a custom annotation + AOP to declare skip rules.

Handle method‑to‑method calls and recursion carefully.

3.4 Use Cases

Annotate entry methods to skip environment checks for specific tables.

@InvokeChainSkipEnvRule(skipEnvList = {"pre"}, skipTableList = {"project"})
public void importSignedUserData(HttpServletRequest request, HttpServletResponse response) { ... }

3.5 Implementation Details

Mark methods with the annotation.

AOP reads the annotation and stores rules in the application context.

The interceptor retrieves the rules and decides whether to apply the env filter.

3.6 Limitations

Granularity is coarse; all operations on a table are skipped.

Annotation can only be placed on entry points, not internal calls.

4. Summary & Reflection

4.1 Isolation Summary

The project demonstrates a practical approach to both data isolation and sharing by using a custom MyBatis interceptor and annotation‑driven AOP.

4.2 Coding Summary

Modify a single place instead of scattering changes.

Leverage custom annotations for cross‑cutting concerns.

Maintain a clear boundary between business and infrastructure code.

4.3 Scenario Summary

Scenario

Description

Distributed Lock

Use custom annotation to apply a lock on method execution.

Compliance Validation

Combine OGNL expressions with annotation to validate parameters.

API Data Permission

Different permissions per API based on annotation rules.

Routing Strategy

Route requests to different handlers via annotations.

4.4 Reflection

Early design decisions (e.g., separate databases) could avoid complex interceptors; always prioritize design before coding.

4.5 Final Thoughts

Custom annotations are flexible and widely applicable; explore their potential.

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.

JavaSQLMyBatisCustom AnnotationData IsolationInterceptor
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

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.