面试被问Mybatis底层实现:你连这个知识点都说不明白?

343 阅读10分钟

面试真的是越来越难了,一直关注我的朋友应该清楚,小编有一个习惯,喜欢每年都去面试一下,试试水也看看自己的缺陷,这不,前几天有一个机会,让小编成功的被刺激了一耙(虽然知道这样不是很好,但是还是想操作一下,给各位hr道歉)

事情是这样的,小编的简历一直挂在网上,然后有人给我打电话,说有没有时间去面试一下,刚过完双十一,闲着也是闲着,我就应下来去了,然后前面谈的都不错,直到要结束最后一下面试,马上就是hr面了,面试官脑回路不知道怎么回事,就问了我一个意想不到的问题:看你简历写的精通mybatis源码,那你能不能给我讲一下sqlsession啊,WTF?我就反问一句,这有点多啊,那我主要说那一块呢?

1、SqlSession 操作模板实现类==SqlSessionTemplate

2、用于保存当前 SqlSession 对象==SqlSessionInterceptor

3、SqlSession 的事务同步器==SqlSessionSynchronization

幸好我之前看过这一块,然后磕磕绊绊的讲完了,面试官眼神复杂的看了我一下,说可以了,先这样吧。。。。

公众号:Java架构师联盟,每日更更新技术好文

偶,你NNGT,我这美好的一天,就这么没了,面试完了也没啥心思去吃好吃的,回到家就开始冲浪,将这几个知识点进行整理

面试被问Mybatis底层实现:你连这个知识点都说不明白?

其实官网上,sqlsession相关得知识还是很多的,但是时间问题,就针对源码整理被问到的这几个知识点,大家有什么其他想看的可以评论区告诉我,我会抽时间进行整理,好了,话不多说,看重点

SqlSessionTemplate

org.mybatis.spring.SqlSessionTemplate:实现 SqlSession 和 DisposableBean 接口,SqlSession 操作模板实现类

实际上,代码实现和 org.apache.ibatis.session.SqlSessionManager 相似,承担 SqlSessionFactory 和 SqlSession 的职责

构造方法

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  /**
   * a factory of SqlSession
   */
  private final SqlSessionFactory sqlSessionFactory;

  /**
   * {@link Configuration} 中默认的 Executor 执行器类型,默认 SIMPLE
   */
  private final ExecutorType executorType;

  /**
   * SqlSessionInterceptor 代理对象
   */
  private final SqlSession sqlSessionProxy;

  /**
   * 异常转换器,MyBatisExceptionTranslator 对象
   */
  private final PersistenceExceptionTranslator exceptionTranslator;

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  }

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
  }

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // 创建一个 SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
  }
}
  • sqlSessionFactory:用于创建 SqlSession 对象
  • executorType:执行器类型,创建 SqlSession 对象是根据它创建对应的 Executor 执行器,默认为
  • sqlSessionProxy:SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
  • exceptionTranslator:异常转换器

在调用SqlSessionTemplate中的SqlSession相关方法时,内部都是直接调用sqlSessionProxy动态代理对象的方法,我们来看看是如何处理的

SqlSessionInterceptor

SqlSessionTemplate的内部类,实现了 InvocationHandler 接口,作为sqlSessionProxy动态代理对象的代理类,对 SqlSession 的相关方法进行增强

代码如下:

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // <1> 获取一个 SqlSession 对象
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 执行 SqlSession 的方法
        Object result = method.invoke(sqlSession, args);
        // 当前 SqlSession 不处于 Spring 托管的事务中
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          // 强制提交
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          // 关闭 SqlSession 会话,释放资源
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          // 对异常进行转换,差不多就是转换成 MyBatis 的异常
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator
              .translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
}
  1. 调用SqlSessionUtils的getSqlSession方法,获取一个 SqlSession 对象
  2. 执行 SqlSession 的方法
  3. 当前 SqlSession 不处于 Spring 托管的事务中,则强制提交
  4. 调用SqlSessionUtils的closeSqlSession方法,“关闭”SqlSession 对象,这里的关闭不是真正的关闭

SqlSessionHolder

org.mybatis.spring.SqlSessionHolder:继承 org.springframework.transaction.support.ResourceHolderSupport 抽象类,SqlSession 持有器,用于保存当前 SqlSession 对象,保存到 org.springframework.transaction.support.TransactionSynchronizationManager 中,代码如下:

public final class SqlSessionHolder extends ResourceHolderSupport {

  /**
   * SqlSession 对象
   */
  private final SqlSession sqlSession;

  /**
   * 执行器类型
   */
  private final ExecutorType executorType;

  /**
   * PersistenceExceptionTranslator 对象
   */
  private final PersistenceExceptionTranslator exceptionTranslator;

  public SqlSessionHolder(SqlSession sqlSession, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSession, "SqlSession must not be null");
    notNull(executorType, "ExecutorType must not be null");

    this.sqlSession = sqlSession;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
  }

}
  • 当存储到 TransactionSynchronizationManager 中时,使用的 KEY 为创建该 SqlSession 对象的 SqlSessionFactory 对象,后续会分析

SqlSessionUtils

org.mybatis.spring.SqlSessionUtils:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对象

getSqlSession方法

getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)方法,注释如下:

Gets an SqlSession from Spring Transaction Manager or creates a new one if needed. Tries to get a SqlSession out of current transaction. If there is not any, it creates a new one. Then, it synchronizes the SqlSession with the transaction if Spring TX is active and SpringManagedTransactionFactory is configured as a transaction manager.

从事务管理器(线程安全)中获取一个 SqlSession 对象,如果不存在则创建一个 SqlSession,然后注册到事务管理器中,方法如下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
  PersistenceExceptionTranslator exceptionTranslator) {
    
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    // 从 Spring 事务管理器中获取一个 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    // 获取到 SqlSession 对象
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    // 上面没有获取到,则创建一个 SqlSession
    session = sessionFactory.openSession(executorType);

    // 将上面创建的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
}
  1. 从 Spring 事务管理器中,根据 SqlSessionFactory 获取一个 SqlSessionHolder 对象
  2. 调用 sessionHolder 方法,获取到 SqlSession 对象,方法如下private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; if (holder != null && holder.isSynchronizedWithTransaction()) { // 如果执行器类型发生了变更,抛出 TransientDataAccessResourceException 异常 if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException( "Cannot change the ExecutorType when there is an existing transaction"); } // 增加计数,关闭 SqlSession 时使用 holder.requested(); LOGGER.debug(() -> "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); // 获得 SqlSession 对象 session = holder.getSqlSession(); } return session; }
  3. 如果 SqlSession 对象不为 null,则直接返回,接下来会创建一个
  4. 上面没有获取到,则创建一个 SqlSession 对象
  5. 调用 registerSessionHolder 方法,将上面创建的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
  6. 返回新创建的 SqlSession 对象

registerSessionHolder方法

registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session)方法

注释如下:

Register session holder if synchronization is active (i.e. a Spring TX is active).

Note: The DataSource used by the Environment should be synchronized with the transaction either through DataSourceTxMgr or another tx synchronization.

Further assume that if an exception is thrown, whatever started the transaction will handle closing / rolling back the Connection associated with the SqlSession.

如果事务管理器处于激活状态,则将 SqlSession 封装成 SqlSessionHolder 对象注册到其中,方法如下:

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
  PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
    SqlSessionHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();

      // <1> 如果使用 Spring 事务管理器
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

        // <1.1> 创建 SqlSessionHolder 对象
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        // <1.2> 绑定到 TransactionSynchronizationManager 中
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        // <1.3> 创建 SqlSessionSynchronization 到 TransactionSynchronizationManager 中
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        // <1.4> 设置同步
        holder.setSynchronizedWithTransaction(true);
        // <1.5> 增加计数
        holder.requested();
      } else {
        // <2> 如果非 Spring 事务管理器,抛出 TransientDataAccessResourceException 异常
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
          LOGGER.debug(() -> "SqlSession [" + session
              + "] was not registered for synchronization because DataSource is not transactional");
        } else {
          throw new TransientDataAccessResourceException(
              "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else {
      LOGGER.debug(() -> "SqlSession [" + session
          + "] was not registered for synchronization because synchronization is not active");
    }
}
  1. 如果使用 Spring 事务管理器,才会进行注册
  2. 创建 SqlSessionHolder 对象holder
  3. 绑定到 TransactionSynchronizationManager 中,key 为 SqlSessionFactory 对象
  4. 创建 SqlSessionSynchronization 对象(事务同步器)到 TransactionSynchronizationManager 中
  5. 设置 holder 的 synchronizedWithTransaction 属性为ture,和事务绑定了
  6. 增加 holder 的 referenceCount 引用数量

closeSqlSession方法

closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)方法,注释如下:

Checks if SqlSession passed as an argument is managed by Spring TransactionSynchronizationManager

If it is not, it closes it, otherwise it just updates the reference counter and lets Spring call the close callback when the managed transaction ends

如果 SqlSessionFactory 是由 Spring 的事务管理器管理,并且和入参中的 session 相同,那么只进行释放,也就是将 referenceCount 引用数量减一,否则就直接关闭了

方法如下:

public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    // <1> 从 TransactionSynchronizationManager 中,获得 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    // <2.1> 如果相等,说明在 Spring 托管的事务中,则释放 holder 计数
    if ((holder != null) && (holder.getSqlSession() == session)) {
      LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
      holder.released();
    } else {
      // <2.2> 如果不相等,说明不在 Spring 托管的事务中,直接关闭 SqlSession 对象
      LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
      session.close();
    }
}
  1. 从事务管理器中,根据 SqlSessionFactory 获得 SqlSessionHolder 对象
  2. 如果相等,说明在 Spring 托管的事务中,则释放 holder 计数
  3. 否则,不在 Spring 托管的事务中,直接关闭 SqlSession 对象

isSqlSessionTransactional方法

isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory)方法,判断 SqlSession 对象是否被 Sping 的事务管理器管理,代码如下:

public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    // 从 TransactionSynchronizationManager 中,获得 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    // 如果相等,说明在 Spring 托管的事务中
    return (holder != null) && (holder.getSqlSession() == session);
}

SqlSessionSynchronization

org.mybatis.spring.SqlSessionUtils的内部类,继承了 TransactionSynchronizationAdapter 抽象类,SqlSession 的事务同步器,基于 Spring Transaction 体系

注释如下:

Callback for cleaning up resources.

It cleans TransactionSynchronizationManager and also commits and closes the SqlSession.

It assumes that Connection life cycle will be managed by DataSourceTransactionManager or JtaTransactionManager

回调的时候清理资源

构造方法

private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {

    private final SqlSessionHolder holder;

    private final SqlSessionFactory sessionFactory;

    /**
     * 是否开启
     */
    private boolean holderActive = true;

    public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
      notNull(holder, "Parameter 'holder' must be not null");
      notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");

      this.holder = holder;
      this.sessionFactory = sessionFactory;
    }
}

getOrder方法

@Override
public int getOrder() {
  // order right before any Connection synchronization
  return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
}

suspend方法

当事务挂起时,取消当前线程的绑定的 SqlSessionHolder 对象,方法如下:

@Override
public void suspend() {
  if (this.holderActive) {
    LOGGER.debug(() -> "Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
    TransactionSynchronizationManager.unbindResource(this.sessionFactory);
  }
}

resume方法

当事务恢复时,重新绑定当前线程的 SqlSessionHolder 对象,方法如下:

@Override
public void resume() {
  if (this.holderActive) {
    LOGGER.debug(() -> "Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
    TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
  }
}

beforeCommit方法

在事务提交之前,调用 SqlSession#commit() 方法之前,提交事务。虽然说,Spring 自身也会调用 Connection#commit() 方法,进行事务的提交。但是,SqlSession#commit() 方法中,不仅仅有事务的提交,还有提交批量操作,刷新本地缓存等等,方法如下:

@Override
public void beforeCommit(boolean readOnly) {
  // Connection commit or rollback will be handled by ConnectionSynchronization or DataSourceTransactionManager.
  // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so they are actually executed.
  // SpringManagedTransaction will no-op the commit over the jdbc connection
  // TODO This updates 2nd level caches but the tx may be rolledback later on!
  if (TransactionSynchronizationManager.isActualTransactionActive()) {
    try {
      LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
      // 提交事务
      this.holder.getSqlSession().commit();
    } catch (PersistenceException p) {
      // 如果发生异常,则进行转换,并抛出异常
      if (this.holder.getPersistenceExceptionTranslator() != null) {
        DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
            .translateExceptionIfPossible(p);
        if (translated != null) {
          throw translated;
        }
      }
      throw p;
    }
  }
}

beforeCompletion方法

提交事务完成之前,关闭 SqlSession 对象,在 beforeCommit 之后调用,方法如下:

@Override
public void beforeCompletion() {
  // Issue #18 Close SqlSession and deregister it now
  // because afterCompletion may be called from a different thread
  if (!this.holder.isOpen()) {
    LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
    // 取消当前线程的绑定的 SqlSessionHolder 对象
    TransactionSynchronizationManager.unbindResource(sessionFactory);
    // 标记无效
    this.holderActive = false;
    LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
    // 关闭 SqlSession 对象
    this.holder.getSqlSession().close();
  }
}

afterCompletion方法

在事务完成之后,关闭 SqlSession 对象,解决可能出现的跨线程的情况,方法如下:

@Override
public void afterCompletion(int status) {
  if (this.holderActive) { // 处于有效状态
    // afterCompletion may have been called from a different thread
    // so avoid failing if there is nothing in this one
    LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
    // 取消当前线程的绑定的 SqlSessionHolder 对象
    TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
    // 标记无效
    this.holderActive = false;
    LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
    // 关闭 SqlSession 对象
    this.holder.getSqlSession().close();
  }
  this.holder.reset();
}

总结

我这次面试的公司不大,只能算是一家中型公司,也没有什么说面试前的准备,都是平时学习的记忆,毕竟目的不一样,但是,同样也反映出一个真实情况:在日常工作中要多注意一些细节,当出现问题的时候在解决完,有时间的情况下多深入看一下源码的东西,其实源码真的不难,嘿嘿嘿,好了,整理完了就觉得高兴多了,看时间还早,夜生活该开始了

面试被问Mybatis底层实现:你连这个知识点都说不明白?

祝周末愉快