See how MyBatis executes a SQL statement

background

In an interview two days ago, the interviewer asked the same question as the title. Since he always thought that MyBatis was just an ORM framework, he didn’t have a deep understanding of him, so he was asked, so this article Let's explore how MyBatis executes a SQL statement from the source code.

reading environment

Source code environment, the main branch code directly downloaded from github, the version number is 3.5.11-SNAPSHOT
github click to jump

Ide Jetbrain Idea 2021.2.1 Community Edition
Maven 3.8.2
JDK 17

configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://******/**"/>
        <property name="username" value="****"/>
        <property name="password" value="******"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="LogChartMapper.xml"/>
  </mappers>
</configuration>
复制代码

test main method

public class Main {
  public static void main(String[] args) throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = build.openSession();
    LogChartMapper mapper = sqlSession.getMapper(LogChartMapper.class);
    List<LogChart> logCharts = mapper.selectList();
    System.out.println(JSONUtil.toJsonStr(logCharts));
  }
}
复制代码

mapper

public interface LogChartMapper {
  @Select(value = "select * from log_chart")
  List<LogChart> selectList();
}
复制代码

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.mybatis.test.mapper.LogChartMapper">
</mapper>
复制代码

reading process

The process of loading XML

As can be seen from the main test method above, the more important thing is SqlSessionFactory. The first step is to see how it is built. Click the build method to follow up.

The Build method chases to the bottom and builds an XMLConfigBuilder, so it is necessary to go in and take a look at the Parser method.

This code is relatively short, and you can see two information points. The first returned type is the Configuration class, a parseConfiguration method is called, and the xml node of the configuration is passed in.
Explain that the method entry is correct, and we will continue to follow the parseConfiguration method later

For this method, I saw a few familiar things. These tags are parsed respectively for plugins, objectFactory, and mappers. We don't care about others here, let's mainly look at this method of processing Mappers tags.

Here, by traversing each mapper tag we wrote, we will get three variables: resource, url, and mapperClass. They are mutually exclusive at configuration time. The three ifs here also correspond to each case that is not empty.

For the specific meaning of each attribute, please refer to > Jump link

Our XML uses the Resources attribute, you can go back to the above to flip through the XML, let's look at the if judgment that the Resource tag is not empty.

Pay attention here, Myabtis reads the xml pointed to by Mapper, then creates a new XmlMapperBuilder, and then calls the parse method. The logic here is very similar to the place just started. It is worth noting that when creating the XmlMapperBuilder, the Configuration is passed in, that is to say, the things in the configuration will be changed in the parse. Go here and follow the code inside.

There are two important methods to see the name, ConfigurationElement and binMapperForNameSpace.
configurationElement Looking at the input parameters of this method, there is a high probability that some processing is done on XML, such as ResultMap, sql, and select tags.

Let's look at the BindMapperForNameSpace method later.

Create Mapper

In the above code, I saw the AddMapper method. The value passed in is the nameSpace configured in the xml just now, which is the complete class name of the Mapper class. Chase it in and have a look.

Seeing this, I know a knowledge point, why the Mapper class must be an Interface, if it is not an interface, this method cannot enter.

Pay attention, there is a detail, put a MapperProxyFactory into the knownMappers with this class as the key. When you get here, you can find out. All Mapper methods are called by the proxy class.

Next, I saw a similar parse method. See that its class name MapperAnnotationBuilder is used to process annotations. Let's take a look inside.

The IF judgment circled here has a Select or SelectProvider annotation on the judgment method, and there is no ResultMap annotation. It will parse the ResultMap. The high probability here is to traverse the attributes based on the return value, skip it here, and enter the next method.

The parseStatement method seems to go to the parsing method. There are a lot of codes, so I won’t paste them here. If you are interested, you can take a look for yourself. You can know that this is the parsing method.

It ends here, and finally returns the SqlSessionFactory class.

Get a Mapper

SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
LogChartMapper mapper = sqlSession.getMapper(LogChartMapper.class);
复制代码

Go straight in and look at the code.

The knownMapper object looks familiar, isn’t it? As mentioned above, the class is the key, and the value is MapperProxyFactory, so here you can get a factory, and then use the newInstance method to chase it in and have a look.

See here is Java's native dynamic proxy, and what is returned here is a proxy object.

Execute a Mapper method

Because it is Java's dynamic proxy when you see it here, it is also obvious. MapperProxy implements the InvocationHandler interface.

To implement this interface, the Invoke method will be rewritten. That is to say, although what we wrote in the code is to call the method pointed to in our Mapper, in fact, the jvm will call the invoke method, and then directly chase the Invoke method.

Since our mapper is an interface, the if above is false, that is, the else is the code we execute, and then chase it inside.

Here you can see that a new PlainMethodInvoker is created, and then its invoke method is called to catch up.

The next Execute is where he specifically executes the Select statement.

Since the following code is a bit long, I will extract a short section of key points here. Just take a look.

in conclusion

Well, it's over here, next time you encounter this question, you will know how to answer it, and I will summarize a complete one.

MyBatis first reads the configuration file to generate a configuration object, and then generates the corresponding XmlParseBuilder. The corresponding XmlParseBuilder will put the corresponding MapperProxyFactory into the knowMapper in the configuration. Then when the mapper method is to be executed, a dynamic proxy object of Mapper will be obtained through mapperProxyFactory. Then the dynamic proxy object will judge the different methods of calling SqlSession according to the current method to execute the Sql statement.

Guess you like

Origin blog.csdn.net/m0_48922996/article/details/125872681