Chapter 4 of handwritten mybatis: mapper XML parsing and registration

Time:2022-6-20

Author: Xiao Fuge
Series: https://bugstack.cn/md/spring/develop-mybatis/2022-03-20-%E7%AC%AC1%E7%AB%A0%EF%BC%9A%E5%BC%80%E7%AF%87%E4%BB%8B%E7%BB%8D%EF%BC%8C%E6%89%8B%E5%86%99Mybatis%E8%83%BD%E7%BB%99%E4%BD%A0%E5%B8%A6%E6%9D%A5%E4%BB%80%E4%B9%88%EF%BC%9F.html

Precipitation, sharing, growth, so that they and others can gain!

1、 Foreword

How do you face functional iteration?

In fact, many programmers do not have much chance to do a new project when they start programming or join a company. Most of the time, they are constantly iterating and updating on the old project. In this process, you may have to learn n various code fragments with different styles left by predecessors, find a place in these crisscross processes, andifelseAdd in.

Although such a random additionifelse, the mentality of “throwing rotten” at the beginning makes people very uncomfortable. However, it is difficult to deliver high-quality code under the compressed construction period, so part of the R & D is forced to work and run.

However, in fact, you can’t gradually clean up a piece of shit mountain and make the code clear, tidy and clean in your hands. Most of the time, it is also caused by the lack of experience as a coder, who doesn’t understand system reconfiguration, design principles, business background and product direction. So the best way is to improve your own ability. There are some technical changes before you receive a demand. Since it is a Shishan mountain, it should be upgraded as a monster. It will always be easier to maintain and expand in your hands if you repair it, change it, or supplement it.

2、 Objectives

In the process of gradually implementing the mybatis framework, we must first have a goal oriented idea, that is, how to implement the core logic of mybatis.

In fact, we can simply describe the goal of such an ORM framework as providing a proxy class for an interface, which includes SQL information in mapper, that is, XML files(typeInput parameterOutgoing referencecondition)The process of parsing and processing is to operate the database and return the corresponding results to the interface. As shown in Figure 4-1

Chapter 4 of handwritten mybatis: mapper XML parsing and registration

Then, according to the implementation process of the ORM core process, this chapter needs to continue to expand the parsing of mapper files and extract the corresponding SQL files on the basis of the previous chapter. At this stage, when we call Dao interface methods, we can return the corresponding SQL statements to be executed in mapper.In order not to expand the whole project, little brother Fu will lead you to complete these contents step by step. Therefore, this chapter will not operate the database for the time being, and it will be implemented step by step later

3、 Design

Combined with the previous chapter, we usedMapperRegistryScan the package path, register the mapper, andDefaultSqlSessionFor use in. After we can uniformly maintain these namespaces, SQL descriptions and mapping information to mapper XML files corresponding to each Dao, XML is actually our source. The mapper mapper can be registered and SQL managed by parsing and processing the XML file. This makes it easier for us to operate and use. As shown in Figure 4-2

Chapter 4 of handwritten mybatis: mapper XML parsing and registration

  • First you need to defineSqlSessionFactoryBuilderThe factory builder pattern class parses XML files through the way of entry io. At present, we mainly focus on parsing the SQL part and registering the mapper to connect the context of the whole core process.
  • After the file is parsed, it will be stored in the configuration class. Next, you will see that this configuration class will be connected to the entire mybatis process. All content storage and reading are inseparable from this class. For example, we need to read in the configuration class to obtain mapper and execute selectone in defaultsqlsession.

4、 Implementation

1. engineering structure

mybatis-step-03
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.mybatis
    │           ├── binding
    │           │   ├── MapperMethod.java
    │           │   ├── MapperProxy.java
    │           │   ├── MapperProxyFactory.java
    │           │   └── MapperRegistry.java
    │           ├── builder
    │           │   ├── xml
    │           │   │   └── XMLConfigBuilder.java
    │           │   └── BaseBuilder.java
    │           ├── io
    │           │   └── Resources.java
    │           ├── mapping
    │           │   ├── MappedStatement.java
    │           │   └── SqlCommandType.java
    │           └── session
    │               ├── defaults
    │               │   ├── DefaultSqlSession.java
    │               │   └── DefaultSqlSessionFactory.java
    │               ├── Configuration.java
    │               ├── SqlSession.java
    │               ├── SqlSessionFactory.java
    │               └── SqlSessionFactoryBuilder.java
    └── test
        ├── java
        │   └── cn.bugstack.mybatis.test.dao
        │       ├── dao
        │       │   └── IUserDao.java
        │       ├── po
        │       │   └── User.java
        │       └── ApiTest.java
        └── resources
            ├── mapper
            │   └──User_Mapper.xml
            └── mybatis-config-datasource.xml

Project source code: https://t.zsxq.com/bmqNFQ7

The implementation relationship between XML parsing and registration classes is shown in Figure 4-2

Chapter 4 of handwritten mybatis: mapper XML parsing and registration

  • Sqlsessionfactorybuilder, as the entrance of the whole mybatis, provides the builder factory, wraps the XML parsing processing, and returns the corresponding sqlsessionfactory processing class.
  • The XML information is registered in the configuration class through parsing, and then the configuration class is transferred to various logic processing classes, including defaultsqlsession, so that the corresponding content can be obtained from the configuration class when obtaining the mapper and executing SQL.

2. build sqlsessionfactory builder factory

See source code for detailscn.bugstack.mybatis.session.SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(Reader reader) {
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
        return build(xmlConfigBuilder.parse());
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

}
  • Sqlsessionfactorybuilder, as the entry class of the whole mybatis, guides the start of the whole process by specifying the IO for parsing XML.
  • Starting from this class, two processing classes, xmlconfigbuilder and configuration, are added to parse XML and concatenate the object saving operation of the whole process. Next, we will introduce these newly introduced objects respectively.

3. XML parsing

See source code for detailscn.bugstack.mybatis.builder.xml.XMLConfigBuilder

public class XMLConfigBuilder extends BaseBuilder {

    private Element root;

    public XMLConfigBuilder(Reader reader) {
        // 1.  Call parent class to initialize configuration
        super(new Configuration());
        // 2.  Dom4j processing XML
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(new InputSource(reader));
            root = document.getRootElement();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public Configuration parse() {
        try {
            //Resolution mapper
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
        return configuration;
    }

    private void mapperElement(Element mappers) throws Exception {
        List<Element> mapperList = mappers.elements("mapper");
        for (Element e : mapperList) {
                    //Parsing and processing, please refer to the source code for details
                    
                //Add parsing SQL
                configuration.addMappedStatement(mappedStatement);
            }

            //Register mapper mapper
            configuration.addMapper(Resources.classForName(namespace));
        }
    }
    
}
  • The core operation of xmlconfigbuilder is to initialize configuration. Since the use of configuration is the most recent operation from parsing XML and storing, it is more suitable here.
  • Then there is the specific parse() parsing operation, and the parsed information is stored through the configuration class, including adding parsing SQL and registering mapper mapper mapper.
  • The overall resolution configuration includes: type alias, plug-in, object factory, object packaging factory, settings, environment, type conversion, and mapper. However, at present, we do not need so much, so we only do some necessary SQL resolution processing.

4. package the register and SQL statements by configuring classes

See (configuration item) for source codecn.bugstack.mybatis.session.Configuration

public class Configuration {

    /**
     *Mapping registrar
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    /**
     *The mapped statement is stored in the map
     */
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();

    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    }

    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }
}

Add the mapper registration machine and the storage of mapping statements in the configuration class;

  • The mapper registrar is implemented in the previous chapter to register the operation classes provided by mapper mapper locks.
  • Another mappedstatement is a newly added SQL information record object in this chapter, including records: SQL type, SQL statement, input parameter type, output parameter type, etc.Please refer to the source code for details

5. defaultsqlsession obtains information in combination with configuration items

See source code for detailscn.bugstack.mybatis.session.defaults.DefaultSqlSession

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        MappedStatement mappedStatement = configuration.getMappedStatement(statement);
        Return (T) ("you have been delegated!"+ "\ nmethod:" + statement + "\ ninput parameter:" + parameter + "\ nto execute sql:" + mappedstatement getSql());
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }

}
  • Defaultsqlsession is relative to the previous chapter. Here, Mr. FuMapperRegistry mapperRegistryReplace withConfiguration configurationIn this way, richer information content can be transmitted, not just the Registrar operation.
  • Then use configuration in the defaultsqlsession\selectone and defaultsqlsession\getmapper methods to obtain the corresponding information.
  • At present, the selectone method only prints the obtained information. Subsequently, the SQL executor will be introduced to query and return the results.

5、 Testing

1. preparation in advance

Provide Dao interface and corresponding mapper XML configuration

public interface IUserDao {

    String queryUserInfoById(String uId);

}
<mapper namespace="cn.bugstack.mybatis.test.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.bugstack.mybatis.test.po.User">
        SELECT id, userId, userHead, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>

2. unit test

@Test
public void test_SqlSessionFactory() throws IOException {
    // 1.  Get sqlsession from sqlsessionfactory
    Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 2.  Get mapper object
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);

    // 3.  Test validation
    String res = userDao.queryUserInfoById("10001");
    logger. Info ("test result: {}", RES);
}
  • The current usage is very similar to that of mybatis. The XML configuration file is loaded, sent to sqlsessionfactorybuilder for construction and parsing, and the sqlsessionfactory factory is obtained. In this way, you can start the session and complete subsequent operations.

test result

07:07:40.519 [main] INFO  cn. bugstack. mybatis. test. Apitest - test result: you are represented!
Method: cn bugstack. mybatis. test. dao. IUserDao. queryUserInfoById
Input parameter: [ljava.lang.object; @23223dd8
SQL to be executed:
        SELECT id, userId, userHead, createTime
        FROM user
        where id = ?
    

Process finished with exit code 0
  • From the test results, we can see that the current agent operation can print the SQL information parsed from XML. In the future, we will continue to complete the database operation in combination with this part of processing.

6、 Summary

  • Understand the core process of ORM processing, the steps we are in and the contents to be completed. Only by knowing the process of proxy, encapsulation, parsing and return of results can we better complete the implementation of the whole framework.
  • The introduction of sqlsessionfactorybuilder wraps the whole execution process, including XML file parsing and configuration class processing, so that defaultsqlsession can more flexibly get the corresponding information and get mapper and SQL statements.
  • In addition, during the construction of the whole project, we can see that there are many factory modes, builder modes, agent modes, and many design principles. These skills can make the whole project easy to maintain and iterate.This is also the place that researchers should pay attention to in the process of learning the source code.