【SpringBoot + Mybatis series 】 Plug-in mechanism Interceptor
In Mybatis, the plug-in mechanism provides a very powerful extension capability, providing four interception points to support the extension of different scenarios before the SQL is finally executed
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
This article will mainly introduce the use of custom Interceptor, and give a custom plug-in to output the execution of SQL, and the time of the case
I. Environment preparation
1. Prepare the database
Using mysql as the instance database for this article, add a new table
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT ' ' COMMENT 'Username',
`money` int(26) NOT NULL DEFAULT '0' COMMENT 'money',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time'.PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
Copy the code
2. Project environment
This article is developed with SpringBoot 2.2.1.RELEASE + Maven 3.5.3 + IDEA
Pom dependencies are as follows
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
Copy the code
Db configuration information Application.yml
spring:
datasource:
url: JDBC: mysql: / / 127.0.0.1:3306 / story? useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password:
Copy the code
II. Example demonstration
For details on myAbtis’s companion Entity/Mapper, check out the previous series of posts, which will focus on the implementation of Interceptor
1. Customize interceptor
Implement a custom plugin is relatively simple, try org. Apache. Ibatis. Plugin. The Interceptor interface
For example, define an interceptor, implement SQL output, execute time output
@Slf4j
@Component
@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), })
public class ExecuteStatInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// MetaObject is an object provided by Mybatis to access object properties
MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
BoundSql sql = statement.getBoundSql(invocation.getArgs()[1]);
long start = System.currentTimeMillis();
List<ParameterMapping> list = sql.getParameterMappings();
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(sql.getParameterObject());
List<Object> params = new ArrayList<>(list.size());
for (ParameterMapping mapping : list) {
params.add(Ognl.getValue(Ognl.parseExpression(mapping.getProperty()), context, context.getRoot()));
}
try {
return invocation.proceed();
} finally {
System.out.println("------------> sql: " + sql.getSql() + "\n------------> args: " + params + "------------> cost: "+ (System.currentTimeMillis() - start)); }}@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {}}Copy the code
Note the above implementation, the core logic in intercept method, internal implementation of SQL acquisition, parameter parsing, time statistics
1.1 SQL Parameter Parsing Description
In the above case, for parameter parsing, Mybatis uses Ognl to achieve parameter replacement, so the above directly uses Ognl expression to obtain SQL parameters, of course, this implementation is rather rough
// The following paragraph of logic is mainly OGNL use posture
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(sql.getParameterObject());
List<Object> params = new ArrayList<>(list.size());
for (ParameterMapping mapping : list) {
params.add(Ognl.getValue(Ognl.parseExpression(mapping.getProperty()), context, context.getRoot()));
}
Copy the code
In addition to the above posture, we know that eventually Mybatis will also achieve SQL parameter parsing, if you have analyzed the source partners, to the following posture should be more familiar
Source reference since: org. Apache. Ibatis. Scripting. Defaults. DefaultParameterHandler# setParameters
BoundSql sql = statementHandler.getBoundSql();
DefaultParameterHandler handler = (DefaultParameterHandler) statementHandler.getParameterHandler();
Field field = handler.getClass().getDeclaredField("configuration");
field.setAccessible(true);
Configuration configuration = (Configuration) ReflectionUtils.getField(field, handler);
// This posture, and mybatis source parameter parsing posture has been
//
MetaObject mo = configuration.newMetaObject(sql.getParameterObject());
List<Object> args = new ArrayList<>();
for (ParameterMapping key : sql.getParameterMappings()) {
args.add(mo.getValue(key.getProperty()));
}
Copy the code
With this pose, however, it is important to note that not all cuts will work; This involves the features of the four pointcuts provided by Mybatis, which will not be expanded in detail here. In the following source code, these are points that cannot be circumvent
1.2 Intercepts annotations
Next, focus on the @Intercepts annotation on the class, which indicates that this class is a myBatis plug-in class and specifies the pointcut via @signature
Where type, method and ARgs are used to accurately hit the specific method of the pointcut
For example, refer to the above example
@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), })
Copy the code
The cut point is Executor first, and then the execution of both methods is intercepted; The names of the two methods are Query, update, and parameter types are also defined. Using this information, you can accurately match the classes defined on the Executor interface as follows
// org.apache.ibatis.executor.Executor
// correspond to the first @signature
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
// Corresponds to the second @signature
int update(MappedStatement var1, Object var2) throws SQLException;
Copy the code
1.3 Description of pointcuts
Mybatis provides four pointcuts, so what is the difference between them, what kind of scene to choose what pointcuts?
In general, intercepting ParameterHandler is the most common ParameterHandler. Although the example above is intercepting Executor, the choice of pointcut is mainly related to its functionality. To better understand mybatis, we will only introduce the basic principles of mybatis
- Executor: an Executor that schedules StatementHandler, ParameterHandler, ResultSetHandler, and so on to execute SQL. StatementHandler is the most important one.
- StatementHandler: Performs operations using the Statement (PreparedStatement) of a database. It is the core of the four objects and serves as a link between the preceding and the following. Many important plug-ins are implemented by intercepting it.
- ParameterHandler: Is used to process SQL parameters.
- ResultSetHandler: Encapsulates and returns data sets (ResultSets). It is very complex and fortunately not commonly used.
Borrow a piece of Mybatis execution process online to assist the illustration
The original blog.csdn.net/weixin_3949…
2. Plug-in registration
The above is just a custom plug-in, the next step is to make the plug-in work, and there are several different postures
2.1 Spring Bean
This works by defining the plug-in as a plain Spring Bean object
2.2 SqlSessionFactory
It is also very common to register plug-ins directly through SqlSessionFactory, as with TypeHandler previously, as shown below
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(
// Set the XML location of Mybatis
new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml"));
// Register TypeHandler for global use
bean.setTypeHandlers(new Timestamp2LongHandler());
bean.setPlugins(new SqlStatInterceptor());
return bean.getObject();
}
Copy the code
2.3 the XML configuration
If you are used to XML configuration of Mybatis, you may prefer to use the following method in the mybatis-config. XML global XML configuration file
<! DOCTYPEconfiguration
PUBLIC "- / / ibatis.apache.org//DTD Config / 3.1 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<! -- Hump underline format support -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.git.hui.boot.mybatis.entity"/>
</typeAliases>
<! -- type handler definition -->
<typeHandlers>
<typeHandler handler="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler"/>
</typeHandlers>
<! -- Plug-in definition -->
<plugins>
<plugin interceptor="com.git.hui.boot.mybatis.interceptor.SqlStatInterceptor"/>
<plugin interceptor="com.git.hui.boot.mybatis.interceptor.ExecuteStatInterceptor"/>
</plugins>
</configuration>
Copy the code
3. Summary
This article mainly introduces the use of mybatis plug-in posture, a simple example demonstrates if the plug-in, to output the execution of SQL, and time
Custom plug-in implementation, focus on two steps
- Implementing an interface
org.apache.ibatis.plugin.Interceptor
@Intercepts
The annotation modifier plug-in class,@Signature
Define the point of tangency
The plugin registers three postures:
- Register as a Spring Bean
- SqlSessionFactory setup plug-in
- Myabtis.xml file configuration
III. Can’t miss the source code and related knowledge points
0. Project
- Project: github.com/liuyueyi/sp…
- Source: github.com/liuyueyi/sp…
- Source: github.com/liuyueyi/sp…
Mybatis series blog post
- TypeHandler SpringBoo series Mybatis custom TypeHandler
- 【DB series 】SpringBoot series Mybatis Mapper interface and Sql binding several posture
- 【DB series 】SpringBoot series Mybatis Mapper registration of several ways
- 【DB series 】Mybatis-Plus multi-data source configuration
- Mybatis based on DB series 】 【 AbstractRoutingDataSource multiple source switch with AOP implementation
- 【DB series 】Mybatis multi-data source configuration and use
- 【DB series 】 Multi-data source configuration and use of JdbcTemplate
- 【DB series 】Mybatis-Plus code automatic generation
- 【DB series 】MybatisPlus Integration
- 【DB series 】Mybatis+ annotations integration
- 【DB series 】Mybatis+ XML integration
1. Wechat official account: Yash Blog
As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate
Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll
- A grey Blog Personal Blog blog.hhui.top
- A Grey Blog-Spring feature Blog Spring.hhui.top