Backend Development 14 min read

Implementing Data Isolation in Spring Boot with MyBatis Interceptor and JSqlParser

This article demonstrates how to achieve data isolation in a Spring Boot application by creating a MyBatis interceptor that modifies SQL statements using JSqlParser, covering tool introductions, dependency setup, interceptor implementation, testing for SELECT/INSERT/UPDATE/DELETE, and the rationale for intercepting the prepare method.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Implementing Data Isolation in Spring Boot with MyBatis Interceptor and JSqlParser

When building multi‑tenant systems or applications that require data permission control, data isolation is a critical issue. One effective solution is to implement data filtering at the database access layer.

This article explains how to use MyBatis' powerful interceptor mechanism together with JSqlParser—a feature‑rich SQL parser—to easily achieve data isolation in a Spring Boot project, with examples based on the current runtime environment.

Tool Introduction

MyBatis Interceptor

MyBatis supports intercepting and inserting custom logic at different stages of SQL execution.

The interceptor will target the StatementHandler interface's prepare method to modify the SQL statement for data isolation.

JSqlParser

JSqlParser is an open‑source SQL parsing tool that can convert a SQL string into an abstract syntax tree (AST), allowing programs to understand and manipulate each component of the statement. By modifying the AST, additional filter conditions (e.g., an environment variable) can be added before converting it back to a SQL string.

SELECT syntax tree illustration:

Detailed Steps

1. Import Dependencies

MyBatis dependency:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

JSqlParser dependency:

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.6</version>
</dependency>

If the project already uses MyBatis‑Plus, the above dependencies are included automatically and should not be added again to avoid version conflicts.

2. Define the Interceptor

The interceptor intercepts all query statements and adds an env condition to the WHERE clause.

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.*;
import net.sf.jsqlparser.statement.*;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;

@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataIsolationInterceptor implements Interceptor {
    @Value("${spring.profiles.active}")
    private String env;
    // intercept, setEnvToStatement, setEnvToWhereExpression, multipleTableJoinWhereExpression, setEnvToInsert methods ...
}

3. Test

Select

Original SQL:

SELECT a.username,a.code,o.org_code,o.org_name,o.level
FROM admin a left join organize o on a.org_id=o.id
WHERE a.dr=0 and o.level=#{level}

After interception:

SELECT a.username, a.code, o.org_code, o.org_name, o.level 
FROM admin a LEFT JOIN organize o ON a.org_id = o.id 
WHERE a.dr = 0 AND o.level = ? AND a.env = 'test' AND o.env = 'test'

Insert

Original SQL:

INSERT INTO admin (id,username,code,org_id) VALUES (?,?,?,?)

After interception:

INSERT INTO admin (id,username,code,org_id,env) VALUES (?,?,?,?,'test')

Update

Original SQL:

UPDATE admin SET username=?,code=?,org_id=? WHERE id=?

After interception:

UPDATE admin SET username = ?, code = ?, org_id = ? WHERE id = ? AND env = 'test'

Delete

Original SQL:

DELETE FROM admin WHERE id=?

After interception:

DELETE FROM admin WHERE id = ? AND env = 'test'

4. Why Intercept the prepare Method of StatementHandler ?

The SQL statement is constructed and parameterized inside the prepare method, which creates the PreparedStatement . Modifying the SQL at this point ensures the changes affect the final execution. Intercepting query or update occurs after the statement is already prepared, so changes to the SQL would not take effect.

Key steps in MyBatis execution:

Parse configuration and mapper files.

Generate StatementHandler and BoundSql .

Execute prepare to create the PreparedStatement .

Execute query or update to run the SQL.

By intercepting prepare , we can inject additional WHERE clauses, modify ordering, or otherwise alter the SQL before it is sent to the database.

Source: juejin.cn/post/7352075798067986458

Backend Exclusive Tech Group

Build a high‑quality technical community; developers, recruiters, and anyone interested in sharing job referrals are welcome.

Speak civilly, focusing on technical exchange , job referrals , and industry discussion .

Ads are prohibited; do not trust private messages to avoid scams.

Add me as a friend to join the group.

Click the ❤ below to support us, thank you!

Spring BootMyBatisData IsolationSQL InterceptorJSqlParser
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.