1. Demand analysis
Requirements: obtain the SQL executed by Mybatis at the code level, modify the SQL, and execute the modified SQL
Scheme: Mybatis Interceptor:
Note: after adding an interceptor, all methods will be intercepted
Thinking: in fact, interceptors are equivalent to Spring's AOP programming
Fine grained: in the Mybatis framework, sql is finally handed over to the Sqlsession for execution. What the interceptor intercepts is actually:
- 1. Executor execution phase
- 2. ParameterHandler parameter processing phase
- 3. StatementHandler precompile processing phase
- 4. ResultSetHandler result set processing phase
Note: we are implementing Sql Before, you need to get Mybatis of SqlSession Object, but frame SqlSession There are four other objects below,
therefore SqlSession Just a shopkeeper,
What really works is Executor And other four objects: Executor,StatementHandler,ParameterHandler,ResultSetHandler.
More vivid drawing metaphor:
Continue analysis:
Theoretically, if you want to intercept, all methods of these four objects can intercept
But principle is principle and reality is reality
Just like the String class in Java, there are always several common methods in it
Two common objects are mainly intercepted here:
According to the SQL execution cycle of Mybatis, the Executor will hand over the SQL to the StatementHandler for processing,
Therefore, our common interception method is StatementHandler, and the Sql at this stage has completed precompiling, and placeholders appear, which is more comprehensive for Sql (at this time, except for the parameter not assigned, everything else is complete, waiting to be executed)
2. Familiar with interceptor development structure
1. Code
Class implements the interceptor interface @ Override
// Core interception logic writing
public Object intercept(Invocation invocation) throws Throwable { // Write interception logic
// Release the method return invocation.proceed(); } // Pass the target of this interceptor to the next interceptor @Override public Object plugin(Object target) { // When the target class is StatementHandler Type, the target class is wrapped. Otherwise, the target class is returned directly,Reduce the number of times the target is represented if (target instanceof StatementHandler) { System.out.println("Wrapper: : "); return Plugin.wrap(target, this); } else { System.out.println("pass on Wrapper: : "); return target; } @Override public void setProperties(Properties properties) { //The of the configuration file can be received here property Parameters are like local variables in workflow. If you need to define parameters in business, you can use this method // System.out.println(properties.getProperty("name")); }
Class label interception methods and parameters (to prevent method overloading, the uniqueness of parameter discrimination method is required)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class AccessControlInterceptor implements Interceptor { }
2. Configure
In mybatis config Configure interceptors through plugins tag in XML #
Or use SpringBoot to quickly define the @ Configure tag to identify beans
Thinking: how to control the execution sequence of interceptors? For example, I want the interceptor x After execution, execute the interceptor y Reference link:https://juejin.cn/post/6926849748481605646
OK, here we go. The sql corresponding to the interface executed to each mapper file will work. (including the Count query automatically generated by PageHelper, which will also be intercepted)
Thinking: 1,How to intercept only SELECT Type sql,Automatic filtering update Type? 2,How to intercept the specified mapper Interface method? Scheme:1,Can be intercepted invocation Parameter use reflection to obtain this execution sql of MappedStatement,Then obtain sqlCommandType 2,Available in mapper Adding custom annotations at the interface method level is also through reflection Obtain the annotation flag bit to determine whether it should be intercepted or not, and pass meaningful information through the annotation value
3. More detailed analysis
Why intercept the prepare of StatementHandler
The Executor will be handed over to the StatementHandler (for lower level analysis, why use prepare)
When MyBatis works, the StatementHandler interface object used is actually the RoutingStatementHandler object. The meaning of routing is always access.
We can understand it as polymorphism:
StatementHandler statmentHandler = new RountingStatementHandler();
According to the source code, you can see the order of the call structure
Why? because
1. The query update needs to be prepre (see the source code)
2. Obtain the Connection (see the source code parameters)
Equivalent to JDBC} doing whatever you want
4. Under what circumstances will interceptors be used
When you need to operate sql: paging, optimistic locking (set version number verification, report errors directly if verification fails, and update the version directly if verification succeeds)
5. Example: handling general paging
Pseudo code
Interceptor coming in
Obtain pageIndex through the current thread (non thread needs to be used as a parameter, and it is more decoupled in a thread.) multiple links in a thread need the same variable, and remember to clear it with ThreadLocal.)
Connection executes cout query to obtain the starting page number of limit
Modify sql execution
6. Analyze the implementation principle of pageindex
1. How to generate_ How to execute the count method object
2. How to support the validation of only the first query statement under the current start
7. In depth development part II: optimistic lock
1
2
3
9
3
2