MyBatis原理分析之获取SqlSession

简介: MyBatis原理分析之获取SqlSession

获取sqlsession主要是通过SqlSessionFactory的几个重载方法,从configuration中的environment获取datasource与transactionFactory来得到Transaction。然后得到Transaction、Executor与DefaultSqlSession。


mybatis全局配置文件中environments 结点配置如下

<environments default="development">
    <environment id="development">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
      </dataSource>
    </environment>
  </environments>

时序图如下所示:

【1】DefaultSqlSessionFactory


SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择其中一个方法时,你需要考虑以下几点:


事务处理:你希望在 session 作用域中使用事务作用域,还是使用自动提交(auto-commit)?(对很多

数据库和/或 JDBC 驱动来说,等同于关闭事务支持)


数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接?

语句执行:你希望 MyBatis 复用 PreparedStatement 和/或批量更新语句(包括插入语句和删除语句)吗?


基于以上需求,有下列已重载的多个 openSession() 方法供使用。

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:


事务作用域将会开启(也就是不自动提交)。

将由当前环境配置的 DataSource 实例中获取 Connection 对象。

事务隔离级别将会使用驱动或数据源的默认设置。

预处理语句不会被复用,也不会批量处理更新。

相信你已经能从方法签名中知道这些方法的区别。向 autoCommit 可选参数传递 true 值即可开启自动提交功能。若要使用自己的 Connection 实例,传递一个 Connection 实例给 connection 参数即可。注意,我们没有提供同时设置 Connection 和 autoCommit 的方法,这是因为 MyBatis 会依据传入的 Connection 来决定是否启用 autoCommit。对于事务隔离级别,MyBatis 使用了一个 Java 枚举包装器来表示,称为 TransactionIsolationLevel,事务隔离级别支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),并且与预期的行为一致。


你可能对 ExecutorType 参数感到陌生。这个枚举类型定义了三个值:


ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。

ExecutorType.REUSE:该类型的执行器会复用预处理语句。

ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

提示 在 SqlSessionFactory 中还有一个方法我们没有提及,就是 getConfiguration()。这个方法会返回一个 Configuration 实例,你可以在运行时使用它来检查 MyBatis 的配置。


提示 如果你使用过 MyBatis 的旧版本,可能还记得 session、事务和批量操作是相互独立的。在新版本中则不是这样。上述三者都包含在 session 作用域内。你不必分别处理事务或批量操作就能得到想要的全部效果。


下面我们分析openSessionFromDataSource

① 核心方法openSessionFromDataSource

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
   Transaction tx = null;
   try {
     final Environment environment = configuration.getEnvironment();
     final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
     tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     // 根据不同的execTYPE创建对应的executor实例
     final Executor executor = configuration.newExecutor(tx, execType);
     // 根据configuration executor以及autoCommit 创建DefaultSqlSession实例
     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();
   }
 }


代码解释如下:

  • ① 从configuration获取environment实例,主要有属性datasource、id、transactionFactory


② transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit)获取事务实例对象Transaction

③ Executor executor = configuration.newExecutor(tx, execType)获取执行器实例对象;

④ 创建DefaultSqlSession实例对象,new DefaultSqlSession(configuration, executor, autoCommit)


② 核心方法getTransactionFactoryFromEnvironment

configuration实例的TypeAliasRegistry中关于DataSourceTransactionFactory默认配置如下:

jdbc=class org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory, 
managed=class org.apache.ibatis.transaction.managed.ManagedTransactionFactory, 
unpooled=class org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory, 
pooled=class org.apache.ibatis.datasource.pooled.PooledDataSourceFactory, 
jndi=class org.apache.ibatis.datasource.jndi.JndiDataSourceFactory,

如下方法所示,如果mybatis全局配置文件中environments结点没有配置transactionManager则会默认创建一个ManagedTransactionFactory实例对象。

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


③ JdbcTransactionFactory 和JdbcTransaction

public class JdbcTransactionFactory implements TransactionFactory {
  @Override
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }
  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
}


JdbcTransaction 主要属性与构造方法

public class JdbcTransaction implements Transaction {
  private static final Log log = LogFactory.getLog(JdbcTransaction.class);
  protected Connection connection;
  protected DataSource dataSource;
  //失物隔离级别
  protected TransactionIsolationLevel level; 
  //是否自动提交
  protected boolean autoCommit;
  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }
  //...
}  


【2】configuration.newExecutor(tx, execType)

这里是很关键的一步,会创建当前sqlsession对应的执行器Executor,并对其进行拦截器链包装(mybatis提供的插件支持)。


四大对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler

四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。


newExecutor方法如下所示:

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }


代码解释如下:


① 获取执行器类型,默认为SIMPLE (有SIMPLE, REUSE, BATCH三种);

② 根据executorType创建对应的Executor实例对象;

③ 如果开启了二级缓存配置,则创建CachingExecutor;

CachingExecutor有成员属性Executor delegate,主要工作交给delegate来做。CachingExecutor是在②中创建的Executor实例对象基础上做了缓存处理。这里是委派模式的应用


④ interceptorChain.pluginAll(executor)对最后得到的Executor实例对象进行拦截器链包装


BatchExecutor|ReuseExecutor|SimpleExecutor|CachingExecutor

SimpleExecutor主要属性与构造方法

public class SimpleExecutor extends BaseExecutor {
  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
//...
}  

BatchExecutor 主要属性与构造方法

public class BatchExecutor extends BaseExecutor {
  public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
  private final List<Statement> statementList = new ArrayList<>();
  private final List<BatchResult> batchResultList = new ArrayList<>();
  private String currentSql;
  private MappedStatement currentStatement;
  public BatchExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
//...
}  


ReuseExecutor 主要属性与构造方法

public class ReuseExecutor extends BaseExecutor {
  private final Map<String, Statement> statementMap = new HashMap<>();
  public ReuseExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
  //...
}  

可以发现上述几个Exceutor的构造方法都调用了super(configuration, transaction);,其父类是抽象类BaseExecutor:

public abstract class BaseExecutor implements Executor {
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
  //事务
  protected Transaction transaction;
  //执行器包装器,比如CachingExecutor 可能是SimpleExecutor的包装对象(也可能是其他)
  protected Executor wrapper;
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  //本地缓存-一级缓存
  protected PerpetualCache localCache;
  //OutputParameter该类型的本地缓存
  protected PerpetualCache localOutputParameterCache;
  //全局配置实例对象configuration
  protected Configuration configuration;
  protected int queryStack;
  private boolean closed;
  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }
 //...
} 


CachingExecutor 主要属性与构造方法

public class CachingExecutor implements Executor {
  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  //委派模式
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
  //...
}  

CachingExecutor是在其他Executor 实例对象的基础上做了包装,实例了自己的事务管理器TransactionalCacheManager。CachingExecutor通常将数据行为**委派**给其成员实例Executor delegate处理。


这里CachingExecutor 应用了设计模式中的装饰者模式(Decorator Pattern)/包装器模式(Wrapper Pattern)


interceptorChain.pluginAll(executor)

InterceptorChain 拦截器链对象,源码如下

public class InterceptorChain {
  private final List<Interceptor> interceptors = new ArrayList<>();
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}

其核心方法Object pluginAll(Object target) 就是对target进行拦截器链的层层代理,最后返回一个代理对象。如下代码所示就是对executor进行拦截器链包装然后返回,这也是mybatis对插件开发的支持。

 executor = (Executor) interceptorChain.pluginAll(executor);

不只是executor实例化时进行了插件包装,如下图所示ParameterHandler、ResultSetHandler和StatementHandler实例化时都进行了插件包装。

target = interceptor.plugin(target);

这里以mybatisplus的分页插件PaginationInterceptor 为例说明。


如下所示,可以看到PaginationInterceptor 标明了对StatementHandler.class的prepare方法感兴趣。那么在实例化StatementHandler时触发Object pluginAll(Object target)就会调用PaginationInterceptor 的plugin方法对StatementHandler进行代理。

@Setter
@Accessors(chain = true)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {
//...
}

PaginationInterceptor 的plugin方法

public Object plugin(Object target) {
     if (target instanceof StatementHandler) {
         return Plugin.wrap(target, this);
     }
     return target;
 }

可以看到,如果target类型是StatementHandler,将会执行Plugin.wrap(target, this);

这里面target理解为StatementHandler实例,this为当前PaginationInterceptor 实例

Plugin的wrap方法

Plugin主要属性和构造方法

public class Plugin implements InvocationHandler {
  //目标对象  
  private final Object target;
  //拦截器对象
  private final Interceptor interceptor;
  //拦截器感兴趣的类型与方法
  private final Map<Class<?>, Set<Method>> signatureMap;
  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }


wrap方法获取代理对象

//假设这里面target理解为StatementHandler实例,interceptor为当前PaginationInterceptor 实例
public static Object wrap(Object target, Interceptor interceptor) {
  Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  Class<?> type = target.getClass();
  //type 是target类型  signatureMap是拦截器的
  //获取interceptor感兴趣的类型,该类型与type的接口类型是同一类型
  Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  if (interfaces.length > 0) {
    return Proxy.newProxyInstance(
        type.getClassLoader(),
        interfaces,
        new Plugin(target, interceptor, signatureMap));
  }
  return target;
}

代码解释如下:


① 根据interceptor类上@Intercepts注解获取 Map<Class<?>, Set<Method>> signatureMap;

② 获取target的Class类型;

③ 从①和②中确定interceptor支持的接口类型;

④ Proxy.newProxyInstance创建代理对象


代理(包装)后的对象示例如下:

这里h表示其是一个InvocationHandler实例对象,target是一个代理对象。当目标方法被调用时会先被hinvoke方法处理,然后再调用目标方法。

目录
相关文章
|
3月前
|
SQL XML Java
一文搞懂Mybatis执行原理
一文搞懂Mybatis执行原理
41 1
|
5月前
|
Java 数据库连接 mybatis
mybatis 框架分析——mybatis框架使用篇
mybatis 框架分析——mybatis框架使用篇
28 0
|
5月前
|
SQL Java 数据库连接
mybatis常见分页技术和自定义分页原理实战
mybatis常见分页技术和自定义分页原理实战
|
5月前
|
druid Java 数据库连接
SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis
SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis
66 0
|
5月前
|
SQL Java 数据库连接
MyBatis【源码探究 01】mapper.xml文件内<if test>标签判断参数值不等于null和空(当参数值为0)时筛选条件失效原因分析
MyBatis【源码探究 01】mapper.xml文件内<if test>标签判断参数值不等于null和空(当参数值为0)时筛选条件失效原因分析
94 0
MyBatis【源码探究 01】mapper.xml文件内<if test>标签判断参数值不等于null和空(当参数值为0)时筛选条件失效原因分析
|
6天前
|
Java 关系型数据库 数据库连接
MyBatis 执行流程分析
MyBatis 执行流程分析
11 2
|
5月前
|
SQL Java 数据库连接
日志输出-查看 SQL:深入分析 MyBatis 执行过程
日志输出-查看 SQL:深入分析 MyBatis 执行过程
87 0
|
18天前
|
存储 缓存 Java
探秘MyBatis缓存原理:Cache接口与实现类源码分析
探秘MyBatis缓存原理:Cache接口与实现类源码分析
35 2
探秘MyBatis缓存原理:Cache接口与实现类源码分析
|
18天前
|
SQL Java 数据库连接
MyBatis之魂:探索核心接口SqlSession的神秘力量
MyBatis之魂:探索核心接口SqlSession的神秘力量
25 3
MyBatis之魂:探索核心接口SqlSession的神秘力量