2
头图

Author: Brother Xiao Fu 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

Precipitate, share, grow, and let yourself and others gain something! 😄

I. Introduction

你是怎么面对功能迭代的?

In fact, many programmers do not have much opportunity to do a new project when they first start programming or join a company. Most of the time, they are constantly iterating and updating old projects. In this process, you may have to learn all kinds of code fragments with different styles left by N predecessors. In these criss-crossing processes, find a place and add your own ifelse into it.

Although such a random addition ifelse , the mentality of being "rotten" as soon as you get started makes people uncomfortable. However, it is actually difficult to deliver high-quality code under the already compressed construction period, so part of the research and development is forced to be usable and run.

But then again, in fact, you can't gradually clean up a mountain of shit, and make the code gradually clear, neat, and clean in your hands. In many cases, it is also due to the lack of experience as a code farmer, who does not understand system refactoring, design principles, and unfamiliarity. Business background, unclear product direction, etc. So the best way is to improve one's own ability. There are some technical changes before receiving a demand. Since it is a mountain of shit, it is regarded as an upgrade for fighting monsters. Your hands are getting easier to maintain and extend.

2. Goals

In the process of our gradual realization of the Mybatis framework, we must first have a goal-oriented idea, that is, how to realize 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. The class includes the SQL information for the Mapper, that is, the xml file ( 类型 , 入参 , 出参 , 条件 ) for parsing and processing. This processing process is to operate the database and return the corresponding results to the interface. Figure 4-1

图 4-1 ORM 框架核心流程

Then according to the execution process of the ORM core process, we need to continue to expand the analysis of the Mapper file and extract the corresponding SQL file on the basis of the previous chapter. And at this stage, when we call the DAO interface method, we can return the corresponding SQL statement to be executed in the Mapper. In order not to expand the entire project, Brother Fu will lead you to complete these contents step by step, so this chapter will not operate the database for the time being, and will be implemented gradually in the future.

3. Design

Combined with the previous chapter, we used MapperRegistry to scan the package path to register the mapper, and used it in DefaultSqlSession . Then after we can uniformly maintain these namespaces, SQL descriptions, and mapping information to the Mapper XML file corresponding to each DAO, XML is actually our source. The registration and SQL management of Mapper can be completed by parsing and processing the XML file. This will make it easier for us to operate and use. Figure 4-2

图 4-2 XML 文件解析注册处理

  • First need to define SqlSessionFactoryBuilder factory builder mode class, and parse the XML file by means of entry IO. At present, we mainly focus on parsing the SQL part, and register the mapper to connect the whole core process.
  • After the file is parsed, it will be stored in the Configuration configuration class. Next, you will see that this configuration class will be concatenated into the entire Mybatis process. All content storage and reading are inseparable from this class. If we get Mapper and execute selectOne in DefaultSqlSession, we also need to read in the Configuration class.

4. Realization

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

XML parsing and registration class implementation relationship, as shown in Figure 4-2

图 4-2 XML 解析和注册类实现关系

  • SqlSessionFactoryBuilder, as the entrance of the whole Mybatis, provides a builder factory, wraps XML parsing processing, and returns the corresponding SqlSessionFactory processing class.
  • Register the XML information in the Configuration configuration class by parsing, and then pass the Configuration configuration class to each logic processing class, including DefaultSqlSession, so that when the mapper is obtained and SQL is executed, the corresponding configuration class can be obtained from the configuration class. content.

2. Build the SqlSessionFactory builder factory

See the source code for details : cn.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 is the entry class of the entire Mybatis, which guides the startup of the entire process by specifying the IO for parsing XML.
  • From this class, two processing classes, XMLConfigBuilder and Configuration, are newly added, which are respectively used for parsing XML and concatenating the object saving operation of the whole process. Next, we will introduce these newly introduced objects separately.

3. XML parsing processing

See the source code : cn.bugstack.mybatis.builder.xml.XMLConfigBuilder

 public class XMLConfigBuilder extends BaseBuilder {

    private Element root;

    public XMLConfigBuilder(Reader reader) {
        // 1. 调用父类初始化Configuration
        super(new Configuration());
        // 2. dom4j 处理 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 {
            // 解析映射器
            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) {
                    // 解析处理,具体参照源码
                    
                // 添加解析 SQL
                configuration.addMappedStatement(mappedStatement);
            }

            // 注册Mapper映射器
            configuration.addMapper(Resources.classForName(namespace));
        }
    }
    
}
  • The core operation of XMLConfigBuilder is to initialize Configuration, because the use of Configuration is the closest operation to parsing XML and storing, so it is more suitable to put it here.
  • Then there is the specific parse() parsing operation, and the parsed information is stored through the Configuration configuration class, including: adding parsing SQL and registering the Mapper mapper.
  • The overall parsing configuration includes: type aliases, plugins, object factories, object packaging factories, settings, environments, type conversions, and mappers, but we don't need that much yet, so we only do some necessary SQL parsing processing.

4. Wrap the registration machine and SQL statements through the configuration class

See the source code (configuration item) : cn.bugstack.mybatis.session.Configuration

 public class Configuration {

    /**
     * 映射注册机
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    /**
     * 映射的语句,存在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 mapper registration machine and mapping statement storage in the configuration class;

  • The mapper registry is what we implemented in the previous chapter and is used to register the operation classes provided by the Mapper mapper lock.
  • Another MappedStatement is the newly added SQL information record object in this chapter, including records: SQL type, SQL statement, input parameter type, output parameter type, etc. For details, please refer to the source code

5. DefaultSqlSession combines configuration items to obtain information

See the source code : cn.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) ("你被代理了!" + "\n方法:" + statement + "\n入参:" + parameter + "\n待执行SQL:" + mappedStatement.getSql());
    }

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

}
  • Compared with the previous chapter, Mr. Fu replaces MapperRegistry mapperRegistry with Configuration configuration in DefaultSqlSession, so as to convey richer information content, not just register operations.
  • After that, configuration is used in the DefaultSqlSession#selectOne and DefaultSqlSession#getMapper methods to obtain the corresponding information.
  • At present, the selectOne method only prints the obtained information, and the SQL executor will be introduced to query and return the result.

5. Test

1. Prepare 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 testing

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

    // 2. 获取映射器对象
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);

    // 3. 测试验证
    String res = userDao.queryUserInfoById("10001");
    logger.info("测试结果:{}", res);
}
  • The current usage is very similar to Mybatis. By loading the xml configuration file, it is handed over to SqlSessionFactoryBuilder for construction and analysis, and the SqlSessionFactory factory is obtained. In this way, the session can be successfully opened and subsequent operations can be completed.

Test Results

 07:07:40.519 [main] INFO  cn.bugstack.mybatis.test.ApiTest - 测试结果:你被代理了!
方法:cn.bugstack.mybatis.test.dao.IUserDao.queryUserInfoById
入参:[Ljava.lang.Object;@23223dd8
待执行SQL:
        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 proxy operation can already print the SQL information we parsed from XML, and we will continue to complete the database operation in combination with this part of the processing.

6. Summary

  • Understand the core process of ORM processing, know the steps we are currently in and what to complete. Only by clearly knowing the process of proxying, encapsulation, parsing and returning results can the implementation of the entire framework be better completed.
  • The introduction of SqlSessionFactoryBuilder wraps the entire execution process, including: XML file parsing, Configuration configuration class processing, so that DefaultSqlSession can get the corresponding information more flexibly, and obtain Mapper and SQL statements.
  • In addition, from the entire project construction process, we can see that there are many uses of factory mode, builder mode, proxy mode, and many design principles. These techniques can make the entire project easy to maintain and iterate. This is also the place that developers should pay attention to in the process of learning the source code.

小傅哥
4.7k 声望28.4k 粉丝

CodeGuide | 程序员编码指南 - 原创文章、案例源码、资料书籍、简历模版等下载。