Mybatis source code analysis, the whole process of executing the select statement

To analyze the source code, first understand the execution process (or principle) of Mybatis, which is actually the four steps shown in the figure:

Write the code according to the idea and check the code logic again. It is found that:

Create SqlSessionFactory

In fact, it is the code written line by line according to the logic. Next, focus on the source code: press and hold ctrl and click in the build method:

Return to another build method and click again

Let's not focus on how to parse xml. First, if you create a factory, you actually pass the config that parses xml. Then new creates a default factory and returns directly. At this time, the factory is established


Next, let's talk about how to parse the xml file:

Click the parse() method to enter the
Go deep again



Then I found that the code is very long, but I can roughly know what it means by looking at it line by line. Make a breakpoint to test it:


Enter else and click the new XMLMapperBuilder method to enter the internal

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

Finally, it is found that the essence of parsing xml is to create the return value, input parameter, sql, etc. into a statement

At this point, complete the creation of SqlSessionFactory, get a DefaultSqlSessionFactory related to the xml file, and then start creating SqlSession

Create SqlSession

Then create a SqlSession, enter the method through the break point test, and find that DefaultSqlSessionFactory is the implementation class of SqlSessionFactory

Enter the openSessionFromDataSource method again

Create an executor, and then create a corresponding DefaultSqlSession through configuration, which is also an implementation class of SqlSession

Get Mapper object

Continue the break point test to see how the mapper layer is obtained. After entering the method, you can find that there is another mapper factory

According to the breakpoint,

Obviously, it is a dynamic agent. Create the agent object through the factory and execute the invoke method to complete the creation of the mapper object

Create a mapper object according to the information obtained by creating DefaultSqlSessionFactory

Get the returned result according to Mapper object

Then look at the method of executing mapper, how to run sql, how to get the results, and enter the method as follows:

public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }


Execute methods that return multiple results:

It is found that it is actually to obtain the unique identifier of the current tag according to the previously obtained statement object, and then start executing the sql in it



Here, the entity class is assigned a value. Whether it can be assigned is whether the result is consistent with the resultType. (if the entity class is, judge whether it corresponds

Then, after executing the results, the whole process of a search is completed

Tags: Java Mybatis

Posted on Mon, 13 Sep 2021 17:24:02 -0400 by nyfael