mybatis获取SqlSession

1. 获取SqlSession

创建了SqlSessionFactory后,调用openSession方法获取SqlSession对象。

SqlSession sqlSession = sqlSessionFactory.openSession();

2. 大致流程

image-20241014161905716

3.源码分析

3.1 openSession()

SqlSessionFactory是接口,实际是DefaultSqlSessionFactory对象调用openSession()方法。DefaultSqlSessionFactory提供了很多重载openSession方法获取SqlSession对象:

/**
 * @param autoCommit 是否自动提交
 * @return SqlSession
 */
@Override
public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

/**
 * @param execType
 * @return
 */
@Override
public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
}

@Override
public SqlSession openSession(TransactionIsolationLevel level) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}

@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
}

@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return openSessionFromDataSource(execType, null, autoCommit);
}

@Override
public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}

@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
    return openSessionFromConnection(execType, connection);
}

openSession()实际调用的是openSessionFromDataSource()方法获取SqlSession:

/**
 * 从数据源创建SqlSession
 *
 * @param execType   ExecutorType:执行器类型 SIMPLE, REUSE, BATCH
 * @param level      事务级别
 * @param autoCommit 是否自动提交
 * @return SqlSession
 */
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);
        // 创建Executor执行器
        final Executor executor = configuration.newExecutor(tx, execType);
        // 创建并返回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();
    }
}

3.2 newExecutor(tx, execType)

openSessionFromDataSource方法获取了环境和事务管理器后,执行newExecutor方法获取Executor 执行器。Executor执行器是MyBatis四大组件之一,每一个SqlSession都会拥有一个Executor对象,SqlSession执行增删改查都是委托给Executor完成的。

MyBatis四大组件:

Executor:sql执行器

StatementHandler:JDBC与SQL语句执行的两大主流对象:java.sql.Statement、java.sql.PrepareStatement对象,对象的execute方法就是执行SQL语句的入口,通过java.sql.Connection对象创建Statement对象。Mybatis的StatementHandler,是Mybatis创建Statement对象的处理器,即StatementHandler会接管Statement对象的创建。

ParameterHandler:参数处理器

ResultSetHandler:结果处理器

Executor接口,有主要四个实现类,对应每个不同类型执行器。

SimpleExecutor:最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;拼接完SQL之后,直接交给 StatementHandler 去执行

BatchExecutor:通过批量操作来优化性能。通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。

ReuseExecutor :可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。

CachingExecutor:启用于二级缓存时的执行器;采用静态代理;代理一个 Executor 对象。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。

image-20241014161958392

Executor接口源码如下:

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    // 更新
    int update(MappedStatement ms, Object parameter) throws SQLException;

    // 查询,先查缓存,再查数据库
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

    // 查询
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

    // 游标查询
    <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

    // 刷新Statements
    List<BatchResult> flushStatements() throws SQLException;

    // 事务提交
    void commit(boolean required) throws SQLException;

    // 事务回滚
    void rollback(boolean required) throws SQLException;

    // 创建缓存的键对象
    CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

    // 缓存中是否有这个查询的结果
    boolean isCached(MappedStatement ms, CacheKey key);

    // 清空缓存
    void clearLocalCache();

    void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

    Transaction getTransaction();

    void close(boolean forceRollback);

    boolean isClosed();

    void setExecutorWrapper(Executor executor);
}

SimpleExecutor继承自BaseExecutor,该类比较简单。当执行增删改查时,该类获取数据库连接,创建PrepareStatement或者Statement对象,执行SQL语句,最后将数据库返回结果转化为设定的对象。下面以doQuery方法为例:

//父类执行查询时调用该方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
  Configuration configuration = ms.getConfiguration();
   //创建StatementHandler对象
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  //获得数据库连接,创建Statement或者PrepareStatement
  stmt = prepareStatement(handler, ms.getStatementLog());
  //执行SQL语句,将数据库返回结果转化为设定的对象,比如List,Map或者是POJO
  return handler.<E>query(stmt, resultHandler);
} finally {
  //关闭Statement对象
  closeStatement(stmt);
}
}

newExecutor(tx, execType)方法的作用是创建一个执行器

final Executor executor = configuration.newExecutor(tx, execType);

创建执行器源码如下:

/**
 * 获取执行器
 *
 * @param transaction  事务管理器
 * @param executorType 执行器类型
 * @return Executor
 */
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 根据配置生成不同的执行器  SIMPLE BATCH BATCH
    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.BATCH == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    // 如果开启了二级缓存,包装成CachingExecutor 缓存执行器
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    // 每个拦截器都重新包装执行器对象
    executor = (Executor) interceptorChain.pluginAll(executor);
    // 返回
    return executor;
}

3.3 interceptorChain.pluginAll(executor)

在之前的解析中,我们了解到连接器都保存在Configuration类的interceptorChain中,interceptorChain类中保存中所有Interceptor集合组成的拦截器链。

//Configuration类,添加拦截器
public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

在创建了执行器后,还执行了pluginAll方法会传入当前执行器对象。

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

pluginAll()会循环所有的拦截器

/**
 * 包装执行器
 *
 * @param target
 * @return
 */
public Object pluginAll(Object target) {
    // 循环所有拦截器,使用拦截器重新包装一个执行器
    for (Interceptor interceptor : interceptors) {
        // 拦截器对每一个执行器,进行层层包装,当前执行器就绑定了所有的拦截器,当执行器运行时,拦截器就会根据规则进行拦截
        target = interceptor.plugin(target);
    }
    // 包装完成后 返回
    return target;
}

在循环中,调用了每个拦截器的plugin方法,当我们自己定义了一个拦截器后,会重写plugin()。这样就为执行器添加了所有的拦截器,执行器进行操作时,插件就会介入

@Override
public Object plugin(Object target) {
  return Plugin.wrap(target, this);
}

3.4 返回SqlSession对象

最后,返回SqlSession的实现类DefaultsqlSession对象,主要包含了Executor和configuration,整个获取SqlSession流程结束。

// 创建并返回DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);

image-20241014162056711