4
头图

Author: Brother Xiaofu
<br/>Blog: https://bugstack.cn

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

I. Introduction

管你吃几碗粉,有流量就行!

Now we receive more and more information every day, but many individuals do not have much ability to distinguish knowledge. A lot of knowledge information is just pan-knowledge of hot spots, but pan-knowledge is just a group of vague, unsystematic, and possibly wrong information, but this kind of information gives content consumers a kind of “successfully acquired knowledge” to eat. The illusion, but lost control of the level of knowledge.

As a programmer who is very scientific in itself, if he is full of pan-knowledge, spends his energy and time, and does not have enough brainpower to think about the pan-technical content he has absorbed, it will be difficult to grow in the long run. of.

I think from my personal growth experience, I am more willing to spend a lot of practical work to solve a problem, rather than a problem. When a problem is solved thoroughly, clearly, and clearly enough, we can continue to expand and dig deeper in combination with the content required by this knowledge point. I'm glad that there were not so many pan-knowledge content pushes back then, otherwise I might have been very anxious too!

2. Goals

In the previous chapter, we parsed the SQL configuration information in XML, and called DefaultSqlSession in the proxy object to obtain and print operations. From the perspective of the entire framework structure, we solved the object proxy, Mapper mapping, and preliminary SQL parsing, then The next step should be to connect the library and execute the SQL statement and return the result.

Then this part of the content will involve parsing the configuration of dataSource data source information in XML, and establishing transaction management and the start and use of connection pools. And call this part of the ability when the DefaultSqlSession executes the SQL statement. But in order not to expand the whole project in one chapter, here we will focus on parsing configuration, establishing transaction framework, introducing DRUID connection pool, and initially completing SQL execution and simple packaging of results. It is convenient for readers to be familiar with the entire framework structure first, and then iterate and improve the framework details in subsequent chapters.

3. Design

Establish a data source connection pool and a JDBC transaction factory operation, and use the xml configuration data source information as the entry, add data source parsing and construction operations in XMLConfigBuilder, and add JDBC operating environment information to the configuration class configuration. In order to complete the operation of executing SQL on JDBC in DefaultSqlSession.

图 5-1 数据源的解析和使用

  • Parse the XML DB link configuration information in parse, and complete the registration environment of the transaction factory and connection pool to the configuration class.
  • Compared with the processing of the selectOne method in the previous chapter, it is no longer to print the SQL statement, but to put the SQL statement into the DB connection pool for execution, and complete the simple result encapsulation.

4. Realization

1. Engineering structure

 mybatis-step-04
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.mybatis
    │           ├── binding
    │           │   ├── MapperMethod.java
    │           │   ├── MapperProxy.java
    │           │   ├── MapperProxyFactory.java
    │           │   └── MapperRegistry.java
    │           ├── builder
    │           │   ├── xml
    │           │   │   └── XMLConfigBuilder.java
    │           │   └── BaseBuilder.java
    │           ├── datasource
    │           │   ├── druid
    │           │   │   └── DruidDataSourceFactory.java
    │           │   └── DataSourceFactory.java
    │           ├── io
    │           │   └── Resources.java
    │           ├── mapping
    │           │   ├── BoundSql.java
    │           │   ├── Environment.java
    │           │   ├── MappedStatement.java
    │           │   ├── ParameterMapping.java
    │           │   └── SqlCommandType.java
    │           ├── session
    │           │   ├── defaults
    │           │   │   ├── DefaultSqlSession.java
    │           │   │   └── DefaultSqlSessionFactory.java
    │           │   ├── Configuration.java
    │           │   ├── SqlSession.java
    │           │   ├── SqlSessionFactory.java
    │           │   ├── SqlSessionFactoryBuilder.java
    │           │   └── TransactionIsolationLevel.java  
    │           ├── transaction
    │           │   ├── jdbc
    │           │   │   ├── JdbcTransaction.java
    │           │   │   └── JdbcTransactionFactory.java
    │           │   ├── Transaction.java
    │           │   └── TransactionFactory.java
    │           └── type
    │               ├── JdbcType.java
    │               └── TypeAliasRegistry.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

Source address: https://t.zsxq.com/bmqNFQ7

Data source parsing and use of core class relationships, as shown in Figure 5-2

图 5-2 数据源的解析和使用核心类关系

  • The function of the data source DruidDataSourceFactory is wrapped with the implementation of the transaction interface Transaction and the transaction factory TransactionFactory. We use Ali's Druid for the data source connection pool here, and Mybatis's JNDI and Pooled connection pools have not been implemented yet. This part can be specially developed for the data source connection pool in the future.
  • When all data source related functions are ready, in the XMLConfigBuilder parsing XML configuration operation, the configuration of the data source is parsed and the corresponding service is created, which is stored in the environment configuration of Configuration.
  • Finally, SQL execution and result encapsulation are completed in the DefaultSqlSession#selectOne method, and finally the entire Mybatis core context is connected in series.

2. Transaction Management

A database operation should have transaction management capabilities, rather than directly executing it after obtaining a link through JDBC. It should also handle the handling of links, commits, rollbacks, and closes. So here we combine the capabilities of JDBC to encapsulate transaction management.

2.1 Transaction interface

See the source code for details : cn.bugstack.mybatis.transaction.Transaction

 public interface Transaction {

    Connection getConnection() throws SQLException;

    void commit() throws SQLException;

    void rollback() throws SQLException;

    void close() throws SQLException;

}
  • Define a standard transaction interface, link, commit, rollback, and close, which can be implemented by different transaction methods, including: JDBC and managed transactions. Managed transactions are managed by containers such as Spring.

See the source code for details : cn.bugstack.mybatis.transaction.jdbc.JdbcTransaction

 public class JdbcTransaction implements Transaction {

    protected Connection connection;
    protected DataSource dataSource;
    protected TransactionIsolationLevel level = TransactionIsolationLevel.NONE;
    protected boolean autoCommit;

    public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
        this.dataSource = dataSource;
        this.level = level;
        this.autoCommit = autoCommit;
    }

    @Override
    public Connection getConnection() throws SQLException {
        connection = dataSource.getConnection();
        connection.setTransactionIsolation(level.getLevel());
        connection.setAutoCommit(autoCommit);
        return connection;
    }

    @Override
    public void commit() throws SQLException {
        if (connection != null && !connection.getAutoCommit()) {
            connection.commit();
        }
    }
    
    //...

}
  • In the JDBC transaction implementation class, operations such as obtaining links and submitting transactions are encapsulated, in fact, the capabilities provided by JDBC are used.

2.2 Transaction Factory

See the source code for details : cn.bugstack.mybatis.transaction.TransactionFactory

 public interface TransactionFactory {

    /**
     * 根据 Connection 创建 Transaction
     * @param conn Existing database connection
     * @return Transaction
     */
    Transaction newTransaction(Connection conn);

    /**
     * 根据数据源和事务隔离级别创建 Transaction
     * @param dataSource DataSource to take the connection from
     * @param level Desired isolation level
     * @param autoCommit Desired autocommit
     * @return Transaction
     */
    Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}
  • Wrap JDBC transaction implementations in the factory method pattern, providing a corresponding factory for each transaction implementation. Unlike simple factory interface wrappers.

3. Type alias registrar

In the Mybatis framework, the basic types, array types, and our own defined transaction implementations and transaction factories need to be registered in the type alias registrar for management. When we need to use it, we can get specific details from the registrar. Object type, which is then used by instantiating it.

3.1 Basic Registrar

See the source code for details : cn.bugstack.mybatis.type.TypeAliasRegistry

 public class TypeAliasRegistry {

    private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();

    public TypeAliasRegistry() {
        // 构造函数里注册系统内置的类型别名
        registerAlias("string", String.class);

        // 基本包装类型
        registerAlias("byte", Byte.class);
        registerAlias("long", Long.class);
        registerAlias("short", Short.class);
        registerAlias("int", Integer.class);
        registerAlias("integer", Integer.class);
        registerAlias("double", Double.class);
        registerAlias("float", Float.class);
        registerAlias("boolean", Boolean.class);
    }

    public void registerAlias(String alias, Class<?> value) {
        String key = alias.toLowerCase(Locale.ENGLISH);
        TYPE_ALIASES.put(key, value);
    }

    public <T> Class<T> resolveAlias(String string) {
        String key = string.toLowerCase(Locale.ENGLISH);
        return (Class<T>) TYPE_ALIASES.get(key);
    }

}
  • In the TypeAliasRegistry type alias register, some basic type registration is done first, and the registerAlias registration method and the resolveAlias acquisition method are provided.

3.2 Registration Transaction

See the source code for details : cn.bugstack.mybatis.session.Configuration

 public class Configuration {

    //环境
    protected Environment environment;

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

    // 映射的语句,存在Map里
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();

    // 类型别名注册机
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
    }
    
    //...
}
  • In the Configuration configuration option class, add the type alias registration machine, and add the JDBC and DRUID registration operations through the constructor.
  • Readers should note that the entire operation of Mybatis is a serial process using Configuration configuration items, so everything will be linked in Configuration.

4. Parse the data source configuration

By extending the parsing of the environment information in the XML parser XMLConfigBuilder, we call the data source and transaction content as the environment for operating SQL. After parsing, the configuration information is written into the Configuration configuration item, which is convenient for subsequent use.

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

 public class XMLConfigBuilder extends BaseBuilder {
         
  public Configuration parse() {
      try {
          // 环境
          environmentsElement(root.element("environments"));
          // 解析映射器
          mapperElement(root.element("mappers"));
      } catch (Exception e) {
          throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
      }
      return configuration;
  }
    
  private void environmentsElement(Element context) throws Exception {
      String environment = context.attributeValue("default");
      List<Element> environmentList = context.elements("environment");
      for (Element e : environmentList) {
          String id = e.attributeValue("id");
          if (environment.equals(id)) {
              // 事务管理器
              TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();
              // 数据源
              Element dataSourceElement = e.element("dataSource");
              DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
              List<Element> propertyList = dataSourceElement.elements("property");
              Properties props = new Properties();
              for (Element property : propertyList) {
                  props.setProperty(property.attributeValue("name"), property.attributeValue("value"));
              }
              dataSourceFactory.setProperties(props);
              DataSource dataSource = dataSourceFactory.getDataSource();
              // 构建环境
              Environment.Builder environmentBuilder = new Environment.Builder(id)
                      .transactionFactory(txFactory)
                      .dataSource(dataSource);
              configuration.setEnvironment(environmentBuilder.build());
          }
      }
  }

}
  • Use the XMLConfigBuilder#parse parsing extension to parse the data source, including the transaction manager parsing and reading from the type register to the implementation class of the transaction project in the environmentsElement method. Similarly, the data source is also obtained from the type register.
  • Finally, the transaction manager and data source processing are stored in the Configuration configuration item through the environment construction Environment.Builder, and the data source can be obtained wherever the Configuration exists.

5. SQL execution and result encapsulation

In the previous chapter, DefaultSqlSession#selectOne just printed the SQL statement configured in XML. Now, after loading the configuration of the data source, you can put the SQL statement into the data source for execution and result encapsulation.

See the source code for details : cn.bugstack.mybatis.session.defaults.DefaultSqlSession

 public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        try {
            MappedStatement mappedStatement = configuration.getMappedStatement(statement);
            Environment environment = configuration.getEnvironment();

            Connection connection = environment.getDataSource().getConnection();

            BoundSql boundSql = mappedStatement.getBoundSql();
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
            preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
            ResultSet resultSet = preparedStatement.executeQuery();

            List<T> objList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
            return objList.get(0);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    // ...

}
  • Obtain the Connection data source link in the selectOne method, simply execute the SQL statement, and encapsulate the execution result.
  • Because this part is mainly for everyone to concatenate the entire functional structure, the execution of SQL, parameter transmission and result encapsulation are all hard-coded, and we will expand it later.

6. Test

1. Prepare in advance

1.1 Create library table

Create a database named mybatis and create a table user and add test data in the library, as follows:

 CREATE TABLE
    USER
    (
        id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',
        userId VARCHAR(9) COMMENT '用户ID',
        userHead VARCHAR(16) COMMENT '用户头像',
        createTime TIMESTAMP NULL COMMENT '创建时间',
        updateTime TIMESTAMP NULL COMMENT '更新时间',
        userName VARCHAR(64),
        PRIMARY KEY (id)
    )
    ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
insert into user (id, userId, userHead, createTime, updateTime, userName) values (1, '10001', '1_04', '2022-04-13 00:00:00', '2022-04-13 00:00:00', '小傅哥');

2. Configure the data source

 <environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="DRUID">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
  • Configure data source information through mybatis-config-datasource.xml , including: driver, url, username, password
  • In addition, it should be noted here that the DataSource is configured with DRUID, because what we implement is the processing method of this data source.

3. Configure Mapper

 <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.bugstack.mybatis.test.po.User">
    SELECT id, userId, userName, userHead
    FROM user
    where id = #{id}
</select>
  • The configuration content of Mapper has been configured in the analysis and learning in the previous chapter, and this chapter has made simple adjustments.

2. Unit testing

 @Test
public void test_SqlSessionFactory() throws IOException {
    // 1. 从SqlSessionFactory中获取SqlSession
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    // 2. 获取映射器对象
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);
    
    // 3. 测试验证
    User user = userDao.queryUserInfoById(1L);
    logger.info("测试结果:{}", JSON.toJSONString(user));
}
  • The unit test has not changed, it still obtains the SqlSession through the SqlSessionFactory and obtains the mapping object and executes the method call.

Test Results

 22:34:18.676 [main] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
22:34:19.286 [main] INFO  cn.bugstack.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小傅哥"}

Process finished with exit code 0
  • It can be seen from the current test results that through our analysis, packaging and use of the data source, the SQL statement can be executed and the returned result information can be packaged.
  • Readers can debug the code in the process of learning to see how each step is executed, and also learn the design skills of the Mybatis framework in the process.

7. Summary

  • Taking the parsing XML configuration parsing as the entry, adding the integration and packaging of the data source, leading out the transaction factory to process the JDBC transaction, and loading it into the environment configuration for use.
  • Then, through the introduction of the data source, the environment information can be introduced from the Configuration configuration in the DefaultSqlSession, the corresponding SQL statement can be submitted to JDBC for processing and the result data can be simply encapsulated.
  • Combined with the framework structure established in this chapter, data sources, transactions, and simple SQL calls, the next chapter will continue to expand the processing of this part of the content, so that the entire functional module will be gradually improved.

小傅哥
4.7k 声望28.4k 粉丝

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