background

Sqlsession mainly includes data source, executor, transaction management and so on.

The entrance

Sqlsession can SqlSessionFacotry# openSessionFromDataSource.

org... defaults.DefaultSqlSessionFactory#openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  // Get the data source environment
  final Environment environment = configuration.getEnvironment();
  // Create transaction factory
  final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
  // Create transaction
  tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
  // Create the actuator
  final Executor executor = configuration.newExecutor(tx, execType);
  return new DefaultSqlSession(configuration, executor, autoCommit);/ / build sqlsession
}
Copy the code

Create transaction factory & data source factory

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}
Copy the code

The transaction factory is removed from the environment and we go back to the entry created by the environment (the environment object is parsed in the environmentsElement method of XMLConfigBuilder).

private void environmentsElement(XNode context) throws Exception {
  for (XNode child : context.getChildren()) {
    String id = child.getStringAttribute("id");
    if (isSpecifiedEnvironment(id)) {
    / / transactionManagerElement and dataSourceElement method according to the type of configuration from typeAliasRegistry (category manager) in the corresponding type
      TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
      DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
      DataSource dataSource = dsFactory.getDataSource();
      Environment.Builder environmentBuilder = newEnvironment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); }}}Copy the code

The transaction factory type and data source type alias are added during Configuration initialization

public Configuration(a) {// Transaction work in 2
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    // Three data source factories
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
Copy the code

The data source type configured in our demo is POOLED, which stands for POOLED data source. In other words, there is a thread pool to maintain connections and improve network I/O efficiency. The specific content will be analyzed separately later.

Create executor

Batch,reuse, and simple are used by default. Simple is used by default. Batch is used by batch and stored procedures. But we see that all three implement the self-Executor interface, and Executor has four implementation classes, plus a CachingExecutor. Let’s look at the implementation of this class.

public class CachingExecutor implements Executor {
  private final Executor delegate;
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
  }
Copy the code

The CachingExecutor function is similar to the Wrapper in Dubbo. For example, the CachingExecutor function is used to cache a row of data from db, and then retrieve it from the cache based on the query criteria. The packaging logic is specified in Configuration#newExecutor().

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    Executor executor;
    if (ExecutorType.BATCH == executorType) { / /...
    } else { executor = new SimpleExecutor(this, transaction); }if (cacheEnabled) {/ / CachingExecutor packing
      executor = newCachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor);return executor;
}
Copy the code

Methods in the code lines and a key code interceptorChain. PluginAll (executor), the code is our familiar mybatis – plugins, commonly used pagehelper at ordinary times is also implemented by this feature, Let’s focus on the ins and outs of the interceptor. The interceptors attribute is initialized when the XML is loaded.

#mybatis-config.xml
<plugins>
    <plugin interceptor="package.Class1"></plugin>
    <plugin interceptor="package.Class2"></plugin>
</plugins>
#
private void pluginElement(XNode parent) throws Exception {
  for (XNode child : parent.getChildren()) {// All tags of plugin are traversed
    String interceptor = child.getStringAttribute("interceptor");
    Properties properties = child.getChildrenAsProperties();
    Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();// Instantiate the plug-in implementation class
    interceptorInstance.setProperties(properties);
    configuration.addInterceptor(interceptorInstance);// Add to configuration}}Copy the code
org.apache.ibatis.plugin.InterceptorChain#pluginAll
public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {// Iterate over the configured interceptor implementation object
      target = interceptor.plugin(target);
    }
    return target;
}
default Object plugin(Object target) {
   return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) { Map<Class<? >, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<? > type = target.getClass(); Class<? >[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
}
Copy the code

Analyze it before enteringpluginAllMethod is passed inCachingExecutorObject, and pass throughPlugin.wrap()CachingExecutorThe target object is put into the JDK dynamic proxy callback class Plugin, and the dynamic proxy class is returned. The target object is already a dynamic proxy object, and the loop starts again. The debugging result is as follows:As you can see, Object goes through two layers of JDK dynamic proxies and returns a proxy object$Proxy3@22187, we use the form of block diagram to show the object assembly structure again.

The executor executes the process

We simulate the downcall link, and the call process belongs to the responsibility chain mode, and the order should be relatively clear, that is, write in the front, add first, then execute.

sequenceDiagram
DefaultSqlSession->>$Executor2: query()
$Executor2->>$Executor2: invoke()
$Executor2->>MyPlugin2: intercept()
MyPlugin2->>$Executor1: query()
$Executor1->>$Executor1: invoke()
$Executor1->>MyPlugin1: intercept()
MyPlugin1->>CachingExecutor: query()
CachingExecutor->>db: query()

That’s the end of the analysis.