Summary of Mybatis from source and architecture level @[TOC]

An overview of the

What is Mybatis?

Mybatis is a semi-automated persistence layer framework that encapsulates JDBC operations, supports customized SQL, and advanced mapping. But its database independence is low, two different databases, may need two sets of SQL statements

Basic use of Mybatis?

  • Write a global configuration file
  • Write mapper mapping files
  • Load the configuration file and generate the SqlSessionFactory
  • Create a SqlSession that invokes SQL statements in the MAPper mapping file to perform database operations

Architecture process

Three layer structure

The interface layer

The use of SqlSession and Mapper interface, to complete the call to SQL statements, daily development of the main contact with this layer

Data processing layer

This layer is the work of Mybatis, responsible for SQL statement assembly, query parameter binding, result set mapping

Foundation support

This layer can be understood as our global configuration. Including database connection information, transaction management information, configuration cache, write SQL statements in mapper mapping files, etc

The working process

  • Pass the ID of the SQL statement, along with the query parameters, to the SqlSession
  • Find the SQL information to execute and hand it to the Executor Executor
  • Executor assembles and concatenates SQL statements for StatementHandler to handle
  • StatementHandler encapsulates JDBC operations. It generates statements based on SQL information and uses ParameterHandler to parse and bind query parameters
  • After StatemenHandler completes the query, the result set is sent to ResultSetHandler for result set information parsing and encapsulation processing (parameter parsing, result set parsing, both use TypeHandler to do type conversion, Java type and JDBC type).

The source code section

Global configuration file parsing process

  • Get the InputStream of the configuration file and create the Document object
  • Each configuration node is parsed using Xpath syntax
  • Encapsulate the information in a Configuration object to generate the SqlSessionFactory

Source process

SqlSessionFactoryBuilder # build 
 |- XMLConfigBuilder # parse 
   |- XMLConfigBuilder # parseConfiguration
Copy the code

Mapper Map file parsing process

  • A mapper. XML mapping file, uniquely identified by the namespace attribute
  • The mapper. XML mapping file with the namespace attribute is registered with mapperRegistry in the Configuration for subsequent generation of Mapper proxy objects
  • A mapper. XML corresponds to a MapperBuilderAssistant object. The builderAssistant object parses and stores the public tags in the mapper. XML, such as parameterMap, resultMap, Cache, SQL, These tags may be used in a CRUD tag
  • Parsing CRUD label, that is, the select | update | insert | delete tags, a CRUD label, is encapsulated into a MappedStatement object, to label the id attribute as a unique identifier, MappedStatement contains SQL statement information, parameter mapping information, and result set mapping information

Source process

XMLConfigBuilder # mapperElement
 |- XMLMapperBuilder # parse
   |- XMLMapperBuilder # configurationElement
Copy the code

SQL loading and assembly process

SQL loading

  • SQL statements are parsed and encapsulated when parsing CRUD tags in mapper mapping files
  • Encapsulate a CRUD tag into a SqlNode, and encapsulate its child elements (which may be text nodes or dynamic SQL nodes) into a SqlNode. Use the combination mode to assemble these SQLNodes, and finally encapsulate SqlNode and Configuration together. The formation of SqlSource
  • Those with dynamic SQL tags, or those with ${}, are encapsulated as DynamicSqlSource, and the rest are encapsulated as RawSqlSource (which is parsed and encapsulated as StaticSqlSource when Executor executes).
  • The SqlSource, along with other information, is encapsulated as a MapperStatement, a CRUD tag that corresponds to an MappedStatement

Source process

XMLMapperBuilder # buildStatementFromContext
 |- XMLStatementBuilder # parseStatementNode
   |- XMLLanguageDriver # createSqlSource 
     |- XMLScriptBuilder # parseScriptNode
Copy the code

SQL assembly

  • When the Executor is called for execution, the corresponding MappedStatement is looked up and its SqlSource getBoundSql is called to assemble the SQL statement and encapsulate the query parameters
  • When the getBoundSql method is called, SqlNode’s apply method is called. Different SqlNode subclasses parse dynamic SQL tags, concatenate SQL statements, and replace #{} with? ${} will be used to concatenate the string and encapsulate it into the StaticSqlSource. The StaticSqlSource will contain the SQL statement and query parameters

Source process

CachingExecutor # query
 |- MappedStatement # getBoundSql
  |- DynamicSqlSource # getBoundSql
   |- SqlNode # apply // Dynamic SQL assembly, and string concatenation ${}
   |- SqlSourceBuilder # parse // Replace #{} with?
   // encapsulate the StaticSqlSource
   // And call getBoundSql for StaticSqlSource
   //new a new BoundSql, passing in the assembled SQL statement and the query parameters
Copy the code

Execute the query procedure

  • Extract an MappedStatement based on the ID from the Configuration
  • Leave the MappedStatement to Executor
  • The Executor calls getBoundSql in MappedStatement to get the assembled SQL statement and query parameters
  • Create a StatementHandler based on the query parameters, MappedStatement, and other information
  • StatementHandler creates a Statement object and uses ParameterHandler to bind the Statement as an input parameter
  • Execute the query and submit the result set to a ResultSetHandler

Source process

DefaultSqlSession # selectList |- CachingExecutor # query |- BaseExecutor # query |- BaseExecutor # queryFromDatabase |-  SimpleExecutor # doQuery |- Configuration # newStatementHandler |- SimpleExecutor # prepareStatement |- StatementHandler # queryCopy the code

Cache processes

  • CachingExecutor checks whether level-2 cache is enabled. If level-2 cache is enabled, the cache accesses level-2 cache data
  • If level 2 cache is not enabled, or level 2 cache fails, BaseExecutor will attempt to fetch data from level 1 cache. If level 1 cache is not available, the database will be accessed
  • Level 2 cache is mapper level, that is, a mapper, corresponding to a level 2 cache. In the source code, the secondary cache is held by MappedStatement. Level 2 caching is through mapper mapping files<cache/>Label open
  • Level 1 caching cannot be turned off, but can be set in global configuration<setting name="localCacheScope" value="STATEMENT"/>Invalidate it (clears level 1 cache every time an operation is executed)

Source process

// Level 2 cache
CachingExecutor # query
 |- MappedStatement # getCache// Get the level 2 cache under the mapper
 |- TransactionalCacheManager # getObject // Find if there is data in the cache
   |- TransactionalCache # getObject // Find if there is data in the cache
   
 |-TransactionalCacheManager # putObject // Store level 2 cache
   |- TransactionalCache # getObject
   
// If the level-2 cache is not matched, go to level-2 cache
BaseExecutor # query
  |- PerpetualCache # getObject // Fetch data from level 1 cache
Copy the code

Lazy loading process

  • After the query ends, when the ResultSetHandler is called to process the result set, if lazy loading is enabled and there are nested queries, a proxy object will be generated for the result
  • When the get method of the result is invoked to access the lazy-loaded data and the data is found to be empty, the MappedStatement is obtained and the query result is set to the main object

Source process

DefaultResultSetHandler # createResultObject
 |- Configuration # getProxyFactory
 |- JavassistProxyFactory # createProxy
 // JavassistProxyFactory is used by default
Copy the code

The process of obtaining a Mapper proxy

  • Calling mapperRegistry in Configuration to find the class information for this Mapper will find a MapperProxyFactory object
  • Using the JDK’s built-in dynamic Proxy, call Proxy under java.lang.Reflect to generate a Proxy class

Source process

# # DefaultSqlSession getMapper | - MapperRegistry getMapper | - MapperProxyFactory # newInstance | - MapperProxy # constructor | -  Proxy.newProxyInstanceCopy the code

Mybatis plug-in process

The myBatis plugin works with the following classes

  • Executor
  • StatementHandler
  • ParameterHandler
  • ResultSetHandler

Interceptor interface, overwrite intercept method, intercept method in the completion of plug-in interception logic. The plugin method is overridden by calling plugin.wrap in the plugin method to generate a proxy object and return it. The underlying JDK dynamic proxy is also used

Source process

// Take Executor as an example
DefaultSqlSessionFactory # openSession
  |- Configuration # newExecutor
    |- InterceptorChain # pluginAll

Copy the code

Summary of class relation

Configuration file parsing is related

  • BaseBuilder

    • XMLConfigBuilder

    • XMLMapperBuilder

      Hold a MapperBuilderAssistant

    • XMLStatementBuilder

    • XMLScriptBuilder

SQL assembly correlation

  • SqlSource

    • DynamicSqlSource :

      Contains dynamic SQL, or ${}, which is parsed into this class

    • RawSqlSource :

      Non-dynamic SQL, and ${}, will be parsed into this class

    • StaticSqlSource

      When Executor executes the query and calls the getBoundSql method, it assembles the SQL statement and wraps it with the query parameters for this class

  • SqlNode

    • TextSqlNode

      Text node

    • StaticTextSqlNode

      Text node, and the text does not contain ${}

    • IfSqlNode

    • ForEachSqlNode

    • ChooseSqlNode

    • TrimSqlNode

      There are two subclasses, namely

      • WhereSqlNode
      • SetSqlNode
    • MixedSqlNode

      As the root node, it has a List

      field

Perform related

  • Executor

    • BaseExecutor

      It has 3 subclasses, respectively

      • SimpleExecutor
      • ReuseExecutor
      • BatchExecutor
    • CachingExecutor

  • StatementHandler

    • RoutingStatementHandler

      It is used for route selection only

    • BaseStatementHandler

      It has 3 subclasses, respectively

      • SimpleStatementHandler
      • PreparedStatementHandler
      • CallableStatementHandler
  • ParameterHandler

  • ResultSetHandler

  • Cache

    • PerpetualCache
    • LruCache
    • FifoCache
    • SerializedCache
    • LoggingCache
    • SynchronizedCache
    • SoftCache
    • WeakCache
    • TransactionalCache

Design patterns

The various Builders are the Builder pattern

SqlSessionFactory is the factory mode

BaseExecutor contains a hook method doQuery, which is a template method pattern

The CachingExecutor and second-level Cache Cache architectures are decorator patterns

The Mapper proxy object, as well as lazy loading, is the proxy mode

SqlNode is a composite system

ErrorContext records the ErrorContext in each thread, using a ThreadLocal implementation and a singleton pattern