Mybatis implements SQL query interception and modification details

Mybatis implements SQL query interception and modification details

Preface

One of the functions of an interceptor is that we can intercept the calls of certain methods. We can choose to add some logic before and after the execution of these intercepted methods, or we can execute our own logic when executing these intercepted methods instead of executing the intercepted methods.

One of the original intentions of designing the Mybatis interceptor is to allow users to implement their own logic at certain times without having to change the inherent logic of Mybatis. For example, I want to perform a fixed operation on all SQLs, perform security checks on SQL queries, or record related SQL query logs, etc.

Mybatis provides us with an Interceptor interface that can implement a custom interceptor.

 public interface Interceptor {
 Object intercept(Invocation invocation) throws Throwable;
 Object plugin(Object target);
 void setProperties(Properties properties);
}

The interface contains three method definitions

The intercept method is a specific processing method for the interception object. The passed Invocation contains the strength of the interception target class, the interception method and the input parameter group of the method. Use the Invocation procedure to execute the original function.

The plugin determines whether to intercept. If no interception is required, the target is returned directly. If interception is required, the wrap static method in the Plugin class is called. If the current interceptor implements any interface, a proxy object is returned, otherwise it is returned directly (recall the design of the proxy mode). The proxy object is actually an instance of the Plugin class, which implements the InvocationHandler interface. The InvocationHandler interface only contains the invoke method for the callback method.

When the interface method of the proxy object is executed, the invoke method of the Plugin is called, which packages the object, method and parameters to be executed into an Invocation object and passes it to the intercept method of the interceptor. Invocation defines a procced method to execute the intercepted original method.

Plugin Class Definition

public class Plugin implements InvocationHandler {
 
 private Object target;
 private Interceptor interceptor;
 private Map, Set> signatureMap;
 
 private Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) {
  this.target = target;
  this.interceptor = interceptor;
  this.signatureMap = signatureMap;
 }
 
 public static Object wrap(Object target, Interceptor interceptor) {
  Map, Set> signatureMap = getSignatureMap(interceptor);
  Class type = target.getClass();
  Class[] interfaces = getAllInterfaces(type, signatureMap);
  if (interfaces.length > 0) {
   return Proxy.newProxyInstance(
     type.getClassLoader(),
     interfaces,
     new Plugin(target, interceptor, signatureMap));
  }
  return target;
 }
 
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
   Set methods = signatureMap.get(method.getDeclaringClass());
   if (methods != null && methods.contains(method)) {
    return interceptor.intercept(new Invocation(target, method, args));
   }
   return method.invoke(target, args);
  } catch (Exception e) {
   throw ExceptionUtil.unwrapThrowable(e);
  }
 }
 
 private static Map, Set> getSignatureMap(Interceptor interceptor) {
  Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
  if (interceptsAnnotation == null) { // issue #251
   throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());   
  }
  Signature[] sigs = interceptsAnnotation.value();
  Map, Set> signatureMap = new HashMap, Set>();
  for (Signature sig : sigs) {
   Set methods = signatureMap.get(sig.type());
   if (methods == null) {
    methods = new HashSet();
    signatureMap.put(sig.type(), methods);
   }
   try {
    Method method = sig.type().getMethod(sig.method(), sig.args());
    methods.add(method);
   } catch (NoSuchMethodException e) {
    throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
   }
  }
  return signatureMap;
 }
 
 private static Class[] getAllInterfaces(Class type, Map, Set> signatureMap) {
  Set> interfaces = new HashSet>();
  while (type != null) {
   for (Class c : type.getInterfaces()) {
    if (signatureMap.containsKey(c)) {
     interfaces.add(c);
    }
   }
   type = type.getSuperclass();
  }
  return interfaces.toArray(new Class[interfaces.size()]);
 }
 
}

The setProperties method, as the name suggests, is used to set properties. There are many ways to initialize bean properties, this is one of them.

Mybatis provides the @Intercepts annotation to declare that the current class is an interceptor. Its value is an @Signature array, indicating the interface, method, and corresponding parameter type to be intercepted.

@Intercepts({@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class}),
    @Signature(method = "query", type = StatementHandler.class, args = {java.sql.Statement.class, ResultHandler.class})})
public class TenantInterceptor implements Interceptor {
.....

For example, in the above class declaration, the first Signature annotation intercepts the input parameter under the StatementHandler class, which is a Connection method named prepare.

The second Signature annotation intercepts the query method in the StatementHandler class that contains two input parameters (of type Statement and ResultHandler).

Finally, the declared Interceptor needs to be registered in the mybatis plug to take effect.

  <!-- Configure mybatis -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
    <!-- mapper scan -->
    <property name="mapperLocations" value="classpath:mybatis/*/*.xml"/>
    <property name="plugins">
      <array>
        <!-- Register your own interceptor -->
        <bean id="paginationInterceptor" class="xxx.xxx.TenantInterceptor">
        </bean>
      </array>
    </property>
  </bean>

The above is the full content of this article. I hope it will be helpful for everyone’s study. I also hope that everyone will support 123WORDPRESS.COM.

You may also be interested in:
  • How to pass multiple parameters to SQL query in Mybatis
  • In-depth analysis of SQL nodes in Mybatis

<<:  Detailed explanation of the steps of using ElementUI in actual projects

>>:  Linux disk management LVM usage

Recommend

Vue custom components use event modifiers to step on the pit record

Preface Today, when I was using a self-written co...

Rainbow button style made with CSS3

Result: Implementation code: html <div class=&...

Detailed explanation of the this pointing problem in JavaScript

Summarize Global environment ➡️ window Normal fun...

Some common advanced SQL statements in MySQL

MySQL Advanced SQL Statements use kgc; create tab...

Zabbix monitoring docker application configuration

The application of containers is becoming more an...

WeChat applet implements a simple dice game

This article shares the specific code of the WeCh...

React new version life cycle hook function and usage detailed explanation

Compared with the old life cycle Three hooks are ...

Detailed explanation of react setState

Table of contents Is setState synchronous or asyn...

How to change the MySQL database file directory in Ubuntu

Preface The company's Ubuntu server places th...

Tutorial on using Docker Compose to build Confluence

This article uses the "Attribution 4.0 Inter...

Introduction to the steps of deploying redis in docker container

Table of contents 1 redis configuration file 2 Do...

Vue+element ui realizes anchor positioning

This article example shares the specific code of ...

JS realizes the front-end paging effect

This article example shares the specific code of ...