Mybatis plug-in development

1. Perform process analysis

1. Parse the configuration file

Mybatis configuration file structure before understanding the parsing process:

mybatis-config.xml

<configuration>
  <properties/>
  <settting/>
  <typeHandlers/> <.... /> <mappers/> </configuration>Copy the code

mybatis-mapper.xml

<mapper >
  <cache/>
  <resultMap/>
  <select/> 
  <update/> 
  <delete/> 
  <insert/> 
</mapper>
Copy the code

The parsing process of the configuration file is to convert the above XML description elements into corresponding Java objects. The final converted objects and their relationship are shown as follows:

Configure the element resolution builder

>org.apache.ibatis.builder.xml.XMLConfigBuilder
 >org.apache.ibatis.builder.xml.XMLMapperBuilder
  >org.apache.ibatis.builder.xml.XMLStatementBuilder
   >org.apache.ibatis.builder.SqlSourceBuilder
    >org.apache.ibatis.scripting.xmltags.XMLScriptBuilder
 >org.apache.ibatis.builder.annotation.MapperAnnotationBuilder
Copy the code

SQL Statement build process source code

>org.apache.ibatis.session.SqlSessionFactoryBuilder#build()/ / 1. Config. The XML document parsing > org. Apache. Ibatis. Builder. XML. XMLConfigBuilder#parse
 >org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
  >org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement/ / 2. Mapper. The XML document parsing > org. Apache. Ibatis. Builder. XML. XMLMapperBuilder#parse 
 >org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement
  >org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)/ / 3. Statemen SQL block parsing > org. Apache. Ibatis. Builder. XML. XMLStatementBuilder#parseStatementNode
 >org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement/ / 4. Dynamic SQL script > org. Apache. Ibatis. Scripting. Xmltags. XMLLanguageDriver#createSqlSource()
>org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode()
>org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags()   
Copy the code

2. Create an SqlSession

First of all, let’s take a look at the composition structure of the session object as shown below:

Session build source code parsing

>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(boolean)
 >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
  >org.apache.ibatis.transaction.TransactionFactory#newTransaction
  >org.apache.ibatis.session.Configuration#newExecutor> org. Apache. Ibatis. Executor. SimpleExecutor > org. Apache. Ibatis. Executor. CachingExecutor / / actuator plug-in packaging >org.apache.ibatis.plugin.InterceptorChain#pluginAll(executor)
  >org.apache.ibatis.session.defaults.DefaultSqlSession#DefaultSqlSession() 
Copy the code

3. Run the StatementHandler method

StatementHandler source code parsing

>org.apache.ibatis.session.defaults.DefaultSqlSession#selectList()
 >org.apache.ibatis.executor.CachingExecutor#query()
  >org.apache.ibatis.executor.BaseExecutor#query()
   >org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
>org.apache.ibatis.session.Configuration#newStatementHandler
org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler
org.apache.ibatis.session.Configuration#newParameterHandler
org.apache.ibatis.plugin.InterceptorChain#pluginAll(parameterHandler)
org.apache.ibatis.session.Configuration#newResultSetHandler
org.apache.ibatis.plugin.InterceptorChain#pluginAll(resultSetHandler)
>org.apache.ibatis.plugin.InterceptorChain#pluginAll(statementHandler)
>org.apache.ibatis.executor.BaseExecutor#getConnection
>org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement
>org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize
>org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
org.apache.ibatis.type.BaseTypeHandler#setParameter
org.apache.ibatis.type.UnknownTypeHandler#setNonNullParameter
org.apache.ibatis.type.IntegerTypeHandler#setNonNullParameter
Copy the code

Second, myBatis plug-in development

Four extension points for plug-ins

  1. Executor
  2. StatementHandler
  3. ParameterHandler
  4. ResultSetHandler

Paging plug-in implementation: after the user declares the Page object implementation in the interface, the plug-in realizes automatic paging. The following is an example:

public class Page implements java.io.Serializable { private int szie; // private int number; // current page number}Copy the code

Page parameter declaration

@Select("select * from user")
List<User> selectByPage(String name, Page page);
Copy the code

Client call

mapper.selectByPage("Xiao Ming", new Page(3, 2))

select * from user  limit10, 20Copy the code

Achieve goal decomposition

  1. Modify Modify the SQL and add the limit statement
  2. Determines if there is a Page object in the method argument
  3. Take the Page object and generate the limit statement
  4. The above operations must be completed before the PreparedStatement object is generated

The complete code is as follows:

package com.niuh.mybatis.dao; import com.sun.deploy.util.ReflectionUtil; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.BaseStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; import java.util.Optional; import java.util.Properties; /** * @author: hejianhui * @create: The 2019-07-13 he * @ see PagePlugin * @ since JDK1.8 * / public class PagePlugin implements Interceptor {@ Override public Object intercept(Invocation invocation) throws Throwable {return null;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Proxy.newProxyInstance(PagePlugin.class.getClassLoader(),
                    new Class[]{StatementHandler.class},
                    new PageHandler((StatementHandler) target));
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) { } private class PageHandler implements InvocationHandler { StatementHandler handler;  public PageHandler(StatementHandler handler) { this.handler = handler; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equalsIgnoreCase("prepare")) {
                setParametersProxy();
            }
            return method.invoke(handler, args);
        }

        private void setParametersProxy() {
            if(handler.getBoundSql().getParameterObject() instanceof Map) { ((Map) handler.getBoundSql().getParameterObject()).values().stream() .filter(a -> a instanceof Page) .findFirst() .ifPresent( page -> { appendPageSql((Page) page); }); } } private void appendPageSql(Page page) { try { BoundSql sql = handler.getBoundSql(); sql.getSql(); Stringlimit = String.format(" limit %s,%s", page.getBegin(), page.getSzie());
                setFileValue("sql", sql, sql.getSql() + limit);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }


    private Object getFileValue(String fileName, Object target) throws NoSuchFieldException, IllegalAccessException {
        Field sqlField = target.getClass().getDeclaredField(fileName);
        sqlField.setAccessible(true);
        return sqlField.get(target);
    }

    private Object setFileValue(String fileName, Object target, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field sqlField = target.getClass().getDeclaredField(fileName);
        sqlField.setAccessible(true);
        sqlField.set(target, value);
        returnsqlField.get(target); }}Copy the code