Old rules, first case code, we according to this case step by step to get Mybatis source code.

public class MybatisApplication { public static final String URL = "jdbc:mysql://localhost:3306/mblog"; public static final String USER = "root"; public static final String PASSWORD = "123456"; public static void main(String[] args) { String resource = "mybatis-config.xml"; InputStream inputStream = null; SqlSession sqlSession = null; try { inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); System.out.println(userMapper.selectById(1)); } catch (Exception e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } sqlSession.close(); }}Copy the code

Mybatis (Mybatis, Mybatis, Mybatis, Mybatis, Mybatis, Mybatis, Mybatis

How does Mybatis parse configuration files? After reading, I finally understand

Because a lot of small partners in urging, said Mybatis source series seems to have the following when, so Mr. Tian stayed up to write this article.

Keep going

SqlSession sqlSession = sqlSessionFactory.openSession();
Copy the code

The sqlSessionFactory is the DefaultSqlSessionFactory.

So here, we’re going to start with the openSession method in DefaultSqlSessionFactory.

public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } // Create session, This method directly calls another method in this class @override public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } / / is actually call this method private SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; Try {// corresponds to the XML tag <environments>, which is stored in configuration when the configuration file is parsed. final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); / / create a executor to execute SQL final executor executor. = the configuration newExecutor (tx, execType); Return new DefaultSqlSession(Configuration, Executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { if (environment == null || environment.getTransactionFactory() == null) { return new ManagedTransactionFactory(); } return environment.getTransactionFactory(); }Copy the code

The main elements of this method are:

Let’s break it down one by one.

Create Transaction Transaction

The transaction factory type can be configured as the JDBC or MANAGED type.

❝JdbcTransactionFactory produces jDBcTransactions. ManagedTransaction ManagedTransactionFactory production. ❞

If JDBC is configured, transactions are managed using the Commit (), rollback(), and close() methods of the Connection object.

If we configure MANAGED, we hand over transactions to a container, such as JBOSS, Weblogic. Since we are running locally, there will be no transactions if configured as MANAGED.

However, if Spring integrates Mybatis in our project, there is no need to configure transactions because we override the Mybatis configuration by configuring the data source and transaction manager directly in applicationContext.xml.

Create an Executor

The Executor is created by calling the newExecutor method of Configuration.

final Executor executor = configuration.newExecutor(tx, execType); Public Executor newExecutor(Transaction Transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; ExecutorType == ExecutorType {executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } // Step 2 if (cacheEnabled) {executor = new CachingExecutor(executor); } / / the third executor = (executor) interceptorChain pluginAll (executor); return executor; }Copy the code

This method consists of three steps.

Step 1: Create the actuator

There are three basic types of executors:

public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}
Copy the code

SIMPLE is the default type.

Why have the abstract BaseExecutor class implement the Executor interface, and then have the concrete implementation class inherit from the abstract class?

This is the implementation of the template method pattern.

The ❝ template method pattern defines an algorithm skeleton and allows subclasses to provide implementations for one or more steps. The template method is to obtain subclasses that can redefine certain steps of an algorithm without changing the structure of the algorithm. ❞

Recommended reading for the template method pattern:

How to quickly master the template method pattern

Abstract methods are implemented in subclass summary, each executor implements its own logic, and BaseExecutor eventually calls concrete subclasses.

Abstract methods

protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)  throws SQLException;
Copy the code

Step 2: Cache decoration

Step 2 in the code above

if (cacheEnabled) {
      executor = new CachingExecutor(executor);
}
Copy the code

If cacheEnabled=true, executors are decorated with decorator design mode.

Step 3: Plug-in proxy

Once the cache is decorated, it is executed

executor = (Executor) interceptorChain.pluginAll(executor);
Copy the code

This is where the Executor is embedded with plug-in logic.

For example, a paging plug-in needs to be embedded in an Executor

Okay, that’s it. We’re done creating the actuator.

Create the DefaultSqlSession object

Assign the Properties in DefaultSqlSession to the Configuration object and Executor created by parsing the Configuration file earlier.

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}
Copy the code

At this point, the SqlSession (DefaultSqlSession) object is created.

conclusion

In this article we went through several steps to create a SqlSession, and finally we got a DefaultSqlSession object containing the Executor and the Configuration object Configuration. Executor is the actual execution object of SQL. Configuration saves the Configuration file content.

The whole process of source code analysis in this paper is shown as follows:

The code word is not easy. Give it a thumbs up