MyBatis’ interceptor is a very powerful feature. It allows us to insert our own logic when MyBatis calls database operations. It is very suitable for auditing, performance optimization, transaction management, execution log output, etc. of some data operations.
Interceptor triggering strategy
Interception interface
MyBatis allows interception of four key nodes in the SQL life cycle: Executor , ParameterHandler , ResultSetHandler , StatementHandler , which play a core role in database operations. Mybatis provides powerful support for inserting custom logic before and after the execution of these four objects. Normally, if interceptors are defined for all interfaces, the interception order is roughly as follows:
- StatementHandler
- Before MyBatis prepares to execute SQL, it will first create a Statement object, which will trigger the interception of StatementHandler.
- You can use the StatementHandler interceptor to perform custom operations before the SQL statement is sent to the database for execution, such as modifying the original SQL statement, setting special Statement properties, etc.
- ParameterHandler
- Before the Statement is ready to be executed, ParameterHandler will be called to set the parameters in the SQL statement.
- By intercepting ParameterHandler, operations can be performed before and after SQL parameter binding. Suitable for complex parameter processing logic, such as encrypting/decrypting data, or processing special parameter formats.
- Executor
- The executor Executor is the center of the entire execution process. It calls the above-mentioned StatementHandler and ParameterHandler to prepare and execute commands.
- By intercepting the Executor, you can add logic before and after SQL execution, such as caching logic, and check and add cache before and after query statements are executed.
- ResultSetHandler
- After the SQL statement is executed, if there are result sets returned, MyBatis will use ResultSetHandler to process these result sets and convert the ResultSet returned by JDBC into the result object specified in MyBatis.
- Intercepting ResultSetHandler supports inserting custom logic in the result set mapping process, such as result set processing, performance statistics, etc.
If all these interceptors are defined, they will be fired in the order above. However, the firing of the interceptor will be adjusted based on the specific execution operation. For example, if the SQL execution does not involve result set processing (such as insert, update, or delete operations), the ResultSetHandler will not be triggered. Likewise, if SQL execution is terminated in an Executor interceptor, subsequent interceptors will not be triggered again.
interception method
The above four core interfaces provide multiple refined methods, allowing precise intervention and interception at different stages of database operations.
Executor:
update
: Responsible for executing three types of SQL statements: insert, update, and delete.query
: Responsible for executing select type SQL statements.queryCursor
: Responsible for executing select type SQL statements and returning Cursor objects.flushStatements
: Submit batch processing statements and return batch processing results.commit
: Commit the transaction.rollback
: Roll back the transaction.getTransaction
: Get the transaction object.close
: Close the executor and decide whether to force rollback of uncommitted transactions based on parameters.isClosed
: Check whether the executor has been closed.clearLocalCache
: Clear local cache.
StatementHandler:
prepare
: Prepare a database Statement object for execution. This method creates a PreparedStatement or CallableStatement object based on configuration and context information.parameterize
: This method is responsible for setting the SQL parameters into the PreparedStatement object before the SQL statement is executed.batch
: Responsible for processing the logic of batch execution and submitting multiple update statements as a batch.update
: SQL statements that perform write operations (insert, update, delete).query
: Execute the SQL statement of the query operation (select) and return the results.queryCursor
: Responsible for executing query operations (select) SQL statements and returning Cursor objects.getBoundSql
: Returns the BoundSql object, which contains the SQL statement to be executed and the parameter information required in the statement.
ParameterHandler:
getParameterObject
: This method is used to obtain the SQL parameter object.setParameters
: This method matches the parameters in the SQL command with the actual parameter object. It is responsible for setting the incoming parameters into the PreparedStatement.
ResultSetHandler:
handleResultSets
: This is one of the main methods. It accepts a Statement object as a parameter and maps the ResultSet of the SQL execution to the result object.handleOutputParameters
: After the stored procedure call is completed, this method will process its output parameters. It also accepts a Statement object as a parameter.
Simple interceptor demo
step
Mybatis related dependencies have been introduced by default.
- Create a class that implements
Interceptor
the interface provided by MyBatis. This interface contains a methodintercept(Invocation invocation)
.
- In
intercept
the method,invocation
the target method of execution can be obtained through the object. You can add your own business logic code before or after executing the target method. - Use
@Intercepts
and@Signature
annotations to configure the interceptor and specify the interfaces and methods you want to intercept;@Signature
details about annotations are described below.
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class ExampleInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//
//
Object returnObject = invocation.proceed();
//
return returnObject;
}
}
- Register interceptors, detailed below.
Register Mybatis interceptor
In the Spring framework, if an interceptor class is created but not registered as a Spring Bean, the interceptor will not be automatically detected and used by MyBatis, causing the interceptor to fail. In order for the interceptor to take effect, the interceptor needs to be explicitly declared and registered in the configuration.
Spring
When using Spring to configure MyBatis, there are generally two ways to register interceptors:
- XML configuration:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.example.model" />
<property name="plugins">
<array>
<bean class="com.example.MyInterceptor"/>
</array>
</property>
</bean>
- Configuration configuration class:
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("com.example.model");
//
Interceptor interceptor = new MyInterceptor();
sessionFactory.setPlugins(new Interceptor[]{interceptor});
return sessionFactory.getObject();
}
}
Spring Boot
Spring Boot simplifies the configuration process of MyBatis through automatic configuration. There are also two ways to register interceptors:
- Registering MyBatis interceptors in Spring Boot is usually done by writing a configuration class.
@Configuration
public class MybatisConfig {
@Bean
public Interceptor myInterceptor() {
return new MyInterceptor();
}
//
@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.addInterceptor(myInterceptor());
}
};
}
}
- In Spring Boot 2.x and later versions, interceptor classes are defined as Spring components (using @Component and other annotations). There is no need to register them manually. Spring Boot’s automatic configuration will automatically scan and register them.
@Component
public class MyInterceptor implements Interceptor {
//
}
@Signature annotation
@Signature
Annotations are used to define target methods intercepted in the MyBatis plug-in. When you create a MyBatis interceptor, this annotation specifies the interface, method name, and method parameter type that the plug-in will intercept.
@Signature
Annotations are usually @Intercepts
used in conjunction with annotations, @Intercepts
which are used to annotate a class, and are used @Signature
in the @Intercepts
annotation’s signature
property array. Multiples can be specified in a single plugin @Signature
, meaning the interceptor can intercept multiple different points.
An @Signature
annotation contains the following three parameters:
- type : Specify the MyBatis interface to be intercepted, that is, the Class object of the intercepted interface introduced above. For example,
Executor.class
,ParameterHandler.class
,ResultSetHandler.class
orStatementHandler.class
. - method : Specify the method name to be intercepted. It is the name of the MyBatis interface method into which you want to insert custom behavior.
- args : A list of parameter types specifying the method to intercept. It is an array of Class type. Make sure you provide the method parameter types in the correct order.
For example, the interceptor source code of Mybatis Plus is defined like this:
@Intercepts({@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
), @Signature(
type = StatementHandler.class,
method = "getBoundSql",
args = {}
), @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}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class MybatisPlusInterceptor implements Interceptor {
//...
}