How to learn MyBatis quickly? After reading this article, you will suddenly realize!

Preface

The content covers: Java, MyBatis, ZooKeeper, Dubbo, Elasticsearch, Memcached, Redis, MySQL, Spring, SpringBoot, SpringCloud, RabbitMQ, Kafka, Linux and other technology stacks.

The full version of Java interview questions address: JAVA back-end interview questions integration

How is the plug-in implemented in MyBatis

In MyBatis, the plug-in is implemented through interceptors. Since it is implemented through interceptors, there will be a question, which objects are allowed to be intercepted?

There are four objects that actually execute SQL: Executor, StatementHandler, ParameterHandler, and ResultSetHandler.

The MyBatis plug-in is implemented based on intercepting these four objects. It should be noted that although we can intercept these four objects, not all methods in these four objects can be intercepted. The following is a summary of the interceptable objects and methods provided by the official website:

Use of MyBatis plug-in

First, let's take an example to see how to use the plug-in.

1. First create a MyPlugin to implement the interface Interceptor, and then rewrite three of the methods (note that the Interceptor interface must be implemented here, otherwise it cannot be intercepted).

package com.lonelyWolf.mybatis.plugin;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;

@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})})
public class MyPlugin implements Interceptor {

    /**
     * 这个方法会直接覆盖原有方法
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("成功拦截了Executor的query方法,在这里我可以做点什么");
        return invocation.proceed();//调用原方法
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);//把被拦截对象生成一个代理对象
    }

    @Override
    public void setProperties(Properties properties) {//可以自定义一些属性
        System.out.println("自定义属性:userName->" + properties.getProperty("userName"));
    }
}
复制代码

@Intercepts is to declare that the current class is an interceptor, and the following @Signature is to identify the method signature that needs to be intercepted, which is determined by the following three parameters

(1) type: the name of the intercepted class

(2) method: the name of the intercepted method

(3) args: the parameter type of the labeling method

2. We also need to configure the plug-in in mybatis-config.

<plugins>
    <plugin interceptor="com.lonelyWolf.mybatis.plugin.MyPlugin">
        <property name="userName" value="张三"/>
    </plugin>
</plugins>
复制代码

If the property attribute is configured here, then we can get it in setProperties.

After completing the above two steps, we have completed the configuration of a plug-in. Next, let's run it:

As you can see, the setProperties method will be executed when the configuration file is loaded.

MyBatis plug-in implementation principle

Next, let us analyze the implementation principle of the entire process from the loading of the plug-in to the initialization to the running.

Plug-in loading

Since the plug-in needs to be configured in the configuration file, it must be parsed. Let's see how the plug-in is parsed. We enter the XMLConfigBuilder class to see

After parsing, the plug-in will be stored in the list attribute of the InterceptorChain object

Seeing InterceptorChain, can we think of the MyBatis plug-in is realized through the responsibility chain model.

How to block the plug-in

Now that the plug-in class has been loaded into the configuration file, there is a question next, when will the plug-in class be intercepted by the object we need to intercept?

In fact, the plug-in interception is related to the object, and the interception time for different objects will also be inconsistent. Next, we will analyze them one by one.

Intercept the Executor object

We know that the SqlSession object is returned by the openSession() method, and the Executor is an internal object of the SqlSession, so let us follow the openSession method to see the initialization process of the Executor object.

As you can see, when the Executor is initialized, the pluginAll method of the interceptorChain will be called. The pluginAll method itself is very simple. It loops the plugins we have stored in the list and calls the plugin method of the Interceptor object:

Click in again:

image

At this point, have we found that it is very familiar? Yes, this is the method we rewritten in the example above, and the plugin method is a default method in the interface.

This method is the key, let's go in and take a look:

You can see that the logic of this method is also very simple, but it should be noted that the MyBatis plug-in is implemented through the JDK dynamic proxy.

The condition of JDK dynamic proxy is that the proxy object must have an interface. This is not the same as in Spring. In Spring, JDK dynamic proxy is used if there is an interface, and CGLIB dynamic proxy is used if there is no interface.

It is precisely because the MyBatis plug-in only uses the JDK dynamic proxy, so we emphasized above that the Interceptor interface must be implemented.

After the proxy, the invoke method of Huizhixing Plugin, let's finally take a look at the invoke method:

The intercept method that is finally executed is the method we rewritten in the example above.
The full version of Java interview questions address: JAVA back-end interview questions integration

Other object plug-in analysis

Next, let's take a look at StatementHandler. StatementHandler is created in the doQuery method of Executor. In fact, the principle is the same. Find the method to initialize the StatementHandler object:

After entering, the pluginAll method is executed inside:

The other two objects are not given as examples. In fact, it is obvious to search the overall situation:

When the four objects are initialized, pluginAll is called to determine whether there is a proxy.

Plug-in execution process

The following is the execution sequence diagram after the plug-in is implemented:

If an object is proxied many times

Can an object be proxied by multiple proxy objects? That is to say, can the same method of the same object be intercepted by multiple interceptors?

The answer is yes, because the proxied object is added to the list, so the interceptor we configure at the top is proxied first, but when it is executed, the outermost interceptor is executed first.

more specific:

Suppose three plug-ins are defined in sequence: plug-in A, plug-in B and plug-in C.

Then the list will be stored in order: plug-in A, plug-in B and plug-in C.

When parsing, the list is traversed, so the parsing is also in the order of: plug-in A, plug-in B, and plug-in C.

But the reverse is the case when it is executed. When it is executed, it is executed in the order of plug-in C, plug-in B, and plug-in A.

Use of PageHelper plug-in

Above we have learned how the plug-ins in MyBatis are defined and how plug-ins are handled in MyBatis. Next, we will take the classic paging plug-in PageHelper as an example to further deepen our understanding.

First we look at the usage of PageHelper:

package com.lonelyWolf.mybatis;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lonelyWolf.mybatis.mapper.UserMapper;
import com.lonelyWolf.mybatis.model.LwUser;
import org.apache.ibatis.executor.result.DefaultResultHandler;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisByPageHelp {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //读取mybatis-config配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //创建SqlSession对象
        SqlSession session = sqlSessionFactory.openSession();

        PageHelper.startPage(0,10);
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<LwUser> userList = userMapper.listAllUser();
        PageInfo<LwUser> pageList = new PageInfo<>(userList);
        System.out.println(null == pageList ? "": JSONObject.toJSONString(pageList));

    }
}
复制代码

The output is as follows:

You can see that the object has been paged, so how is this done?

PageHelper plug-in principle

As we mentioned above, to implement the plug-in, the Interceptor interface provided by MyBatis must be implemented, so we went to find it and found that PageHeler implements the Interceptor:

After the above introduction, this class should be understandable at a glance. The key is to see what the intercept method of SqlUtil does:

This method has a lot of logic, because different database dialects need to be considered, so there will be a lot of judgments. We mainly focus on where PageHelper rewrites the sql statement. The red box in the figure above is the place where the sql statement is rewritten:

Here will get a Page object, and then when you love to write SQL, some paging parameters will be set to the Page object, let's see where the Page object is obtained:

We see that the object is obtained from the LOCAL_PAGE object. What is this?

This is a local thread pool variable, so when is the Page inside it stored? This will return to our example, the beginning of the paging must be called:

PageHelper.startPage(0,10);
复制代码

Here, a Page object will be constructed and set to ThreadLocal.

Why PageHelper is only valid for the first select statement after startPage

This is actually very simple, but some people may think that we still have to go back to the intercept method above:

The paging data in ThreadLocal is cleared in finally, so the paging information will be cleared as long as the query statement is executed once, so the following select statement is naturally invalid.

Can the core behavior of MyBatis be changed without plug-ins?

Above we introduced the use of plug-ins to change the core behavior of MyBatis, so can it be achieved without plug-ins?

The answer is yes, the official website mentioned that we can change the core behavior of MyBatis by overriding the configuration class, that is, we write a class to inherit the Configuration class, then implement the methods, and finally pass in the custom when building the SqlSessionFactory object The Configuration method:

SqlSessionFactory build(MyConfiguration)
复制代码

Of course, this method is very not recommended, because this method is equivalent to pulling out the foundation when building the house and rebuilding it. If you are not careful, the house will collapse.

to sum up

Recently, I have summarized most of the interview questions and answers involved in the interview of Java programmers in response to the knowledge points asked in the interview of Internet companies. I hope to help you review before the interview and find a good job, and save money. You learn in the time you search for information on the Internet.

The full version of Java interview questions address: JAVA back-end interview questions integration