Implementing Environment-Based Data Isolation in MyBatis with Custom Interceptors and Annotations
This article describes a practical approach to achieve environment-based data isolation in a Java application by adding an 'env' field to tables, using a custom MyBatis interceptor to rewrite SQL, and defining annotations with AOP to control environment filtering, while discussing challenges and refactoring considerations.
Background
In a system where pre‑release, gray and online environments share a single database, an env column was added to each table to distinguish data.
Isolation before
Initially only one core table had the env field; later dozens of tables needed the column, requiring a migration strategy that preserved existing data.
Transformation
New rows are inserted with env='all' to be visible in all environments; queries are rewritten to add env in (${currentEnv},'all') .
Solution
A custom MyBatis interceptor reads the current environment from application.properties and rewrites SQL statements at runtime, avoiding changes to DO, Mapper or XML files.
Key benefits:
Business code remains unchanged.
Reduced manual field addition and error risk.
Easier future extensions.
Implementation details include using JSqlParser to parse and modify SQL, handling INSERT and SELECT cases, and storing the environment value in a thread‑local context.
@Intercepts
(
{
@Signature
(type = Executor
.
class
,
method
= "update", args = {MappedStatement
.
class
,
Object
.
class
})}
)
@
Component
public
class
EnvIsolationInterceptor
implements
Interceptor
{
......
@Override
publicObjectintercept(Invocation invocation) throwsThrowable {
......
if
(SqlCommandType.INSERT == sqlCommandType) {
try
{
// 重写 sql 执行语句,填充环境参数等
insertMethodProcess(invocation, boundSql);
}
catch
(Exception exception) {
log.error("parser insert sql exception, boundSql:" + JSON.toJSONString(boundSql), exception);
throwexception;
}
}
returninvocation.proceed();
}
}Evolution
Later requirements demanded skipping environment checks for certain tables or methods. An annotation @InvokeChainSkipEnvRule with AOP was introduced to declare skip rules at the method level.
@Target
({ElementType.METHOD})
@Retention
(RetentionPolicy.RUNTIME)
public
@interface
InvokeChainSkipEnvRule {
/**
* 是否跳过环境。默认 true,不推荐设置 false
*
*
@return
*/
boolean isKip() default true;
/**
* 赋值则判断规则,否则不判断
*
*
@return
*/
String[] skipEnvList() default {};
/**
* 赋值则判断规则,否则不判断
*
*
@return
*/
String[] skipTableList() default {};
}The interceptor reads these rules from the application context to decide whether to apply the environment filter.
Shortcomings
The whole table is skipped, which is a coarse granularity.
Annotations can only be placed on entry methods, not on internal utility methods.
Reflection
The article concludes that a well‑designed technical solution (or separate databases) should be considered from the start to avoid invasive code changes and maintain data safety across environments.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.