乐趣区

关于java:扒开-SqlSession-的外衣

关注“Java 后端技术全栈”

回复“面试”获取全套面试材料

老规矩,先上案例代码,咱们依照这个案例一步一步的搞定 Mybatis 源码。

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();}
    }

后面咱们曾经讲了 Mybatis 是如何解析相干配置文件的,如果怕迷路,还是倡议先看前一篇文章:

Mybatis 是如何解析配置文件的?看完终于明确了

因为很多小伙伴在催,说 Mybatis 源码系列如同何时才有下文了,为此老田熬夜写了这篇。

持续开撸~~

SqlSession sqlSession = sqlSessionFactory.openSession();

后面那篇文章曾经剖析了,这里的 sqlSessionFactory 其实就是 DefaultSqlSessionFactory。

所以这里,咱们就从 DefaultSqlSessionFactory 里的 openSession 办法开始。

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  private final Configuration configuration;
  public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}
  // 创立 session,这个办法间接调用本类中的另外一个办法
  @Override
  public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  // 其实是调用这个办法
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 对应 xml 标签 <environments> , 这个在配置文件解析的时候就曾经寄存到 configuration 中了。final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 创立一个 executor 来执行 SQL 
      final Executor executor = configuration.newExecutor(tx, execType);
      // 这里也阐明了,为什么咱们代码里的 SqlSession 是 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();}
  }
  
    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {if (environment == null || environment.getTransactionFactory() == null) {return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();}

这个办法中的次要内容有:

上面咱们就来一一攻破。

创立事务 Transaction

事务工厂类型能够配置为 JDBC 类型或者 MANAGED 类型。

JdbcTransactionFactory 生产 JdbcTransaction。

ManagedTransactionFactory 生产 ManagedTransaction。

如果配置的 JDBC,则会应用 Connection 对象的 commit()、rollback()、close()办法来治理事务。

如果咱们配置的是 MANAGED,会把事务交给容器来治理,比方 JBOSS,Weblogic。因为咱们是本地跑的程序,如果配置成 MANAGED 就会不有任何事务。

然而,如果咱们我的项目中是 Spring 集成 Mybatis,则没有必要配置事务,因为咱们会间接在 applicationContext.xml 里配置数据源和事务管理器,从而笼罩 Mybatis 的配置。

创立执行器 Executor

调用 configuration 的 newExecutor 办法创立 Executor。

final Executor executor = configuration.newExecutor(tx, execType);
//Configuration 中
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;
  }

此办法分三个步骤。

第一步:创立执行器

Executor 的根本类型有三种:

public enum ExecutorType {SIMPLE, REUSE, BATCH}

SIMPLE 为默认类型。

为什么要让抽象类 BaseExecutor 实现 Executor 接口,而后让具体实现类继承抽象类呢?

这就是模板办法模式的实现。

模板办法模式就是定义一个算法骨架,并容许子类为一个或者多个步骤提供实现。模板办法是得子类能够再不扭转算法构造的状况下,从新定义算法的某些步骤。

对于模板办法模式举荐浏览:

如何疾速把握模板办法模式

形象办法是在子类汇总实现的,每种执行器本人实现本人的逻辑,BaseExecutor 最终会调用到具体的子类中。

形象办法
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;

第二步:缓存装璜

在下面代码中的第二步

if (cacheEnabled) {executor = new CachingExecutor(executor);
}

如果cacheEnabled=true,会用装璜器设计模式对 Executor 进行装璜。

第三步:插件代理

缓存装璜完后,就会执行

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

这里会对 Executor 植入插件逻辑。

比方:分页插件中就须要把插件植入的 Executor

好了,到此,执行器创立的就搞定了。

创立 DefaultSqlSession 对象

把后面解析配置文件创立的 Configuration 对象和创立的执行器 Executor 赋给 DefaultSqlSession 中的属性。

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

到这里,SqlSession(DefaultSqlSession)对象就创立结束。

总结

本文咱们讲了如何创立 SqlSession 的几个步骤,最初咱们取得一个 DefaultSqlSession 对象,外面蕴含了执行器 Executor 和配置对象 Configuration。Executor 是 SQL 的理论执行对象。Configuration 里保留着配置文件内容。

本文源码剖析的整个流程如下图:

码字不易,给个在看,点个赞呗

举荐浏览

面试官:Mybatis 里的设计模式有哪些?我一口气答了 8 种

面试官问我:能说几个常见的 Linux 性能调优命令吗?

退出移动版