Mybatis源码学习第七课---核心层源码分析--接口层SqlSession

   

一、简介

    SqlSession 是 MyBatis 核心接口之一,也是 MyBatis 接口层的主要组成部分,对外提供 MyBatis 常用 API。 MyBatis 提供了两个 SqlSession 接口的实现,下图所示,这里使用了工 厂方法模式,其中开发人员最常用的是 DefaultSq!Session 实现。               

 SqLSessionFactory 负责创建 SqlSession 对象,其中只包含了多个 openSession()方法的重载, 可以通过其参数指定事务的隔离级别、底层使用 Executor 的类型以及是否自动提交事务等方面 的配置。 SqLSessionFactory 接口的定义比较简单,代码就不再展示了。

    在SqlSession 中定义了常用的数据库操作以及事务的相关操作,为了方便用户使用,每种 类型的操作都提供了多种重载。 

二 、源码分析

2.1 创建SqlSession的流程

代码如下:

 @Test
    public void testManyParamQuery(){
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 1.读取mybatis配置文件创SqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取sqlSession 方法级别的
        SqlSession sqlSession = sqlSessionFactory.openSession();
         3.获取对应mapper
        TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
        String email = "qq.com";
        Byte sex = 1;
        // 第三种方式用对象
        EmailSexBean esb = new EmailSexBean();
        esb.setEmail(email);
        esb.setSex(sex);
        List<TUser> list3 = mapper.selectByEmailAndSex3(esb);
        System.out.println("javabean:"+list3.size());
    }

2.2  SqlSessionFactory

    SqlSessionFactory 是创建 SqlSession对象工厂接口,其中默认实现类是DefaultSqlSessionFactory。

      DefaultSqlSessionFactory 中提供的所有 openSession()方法重载都是基于上述两种方式创建 DefaultSqlSession 对象的, 这里不再赘述。

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

DefaultSqlSessionFactory 主要提供了两种创建 SqlSession 对象的方式。

一种方式是通过数据源获取数据库连接, 并创建 Executor 对象以及 DefaultSqlSession 对象, 该方式的具体实现如下:

  private SqlSession openSessionFromDataSource(ExecutorType execType,
                                               TransactionIsolationLevel level,
                                               boolean autoCommit) {
    Transaction tx = null;
    try {
      //获取mybatis配置文件中的environment对象
      final Environment environment = configuration.getEnvironment();
      //获取的TransactionFactory对象
      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();
    }
  }

   另一种方式是用户提供数据库连接对象, DefaultSqlSessionFactory 会使用该数据库连接对象创建 Executor 对象以及 DefaultSq!Session 对象,具体实现如下:

private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      //获取当前连接是否设置事务自动提交
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }
      //获取mybatis配置文件中的environment对象
      final Environment environment = configuration.getEnvironment();
      //从environment获取transactionFactory对象
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //创建事务对象
      final Transaction tx = transactionFactory.newTransaction(connection);
      //根据配置创建executor
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

2.3  DefaultSqlSession

 SqlSession 对象实现 DefaultSqlSession 类型,它也是单独使用 MyBatis 进行开发时最常用的 SqI Session 接口实现。 DefaultSqlSession 中 核心字段的含义如下:

 private final Configuration configuration;//configuration对象,全局唯一
  private final Executor executor;//底层依赖的Executor对象

  private final boolean autoCommit;//是否自动提交
  private boolean dirty;//当前缓存是否有脏数据

  // 为防止用户忘记关闭已打开的游标对象,会通过 cursorList 字段记录由该 SqlSession 对象生成的游标
  //对象,在 DefaultSqlSession.close ()方法中会统一关闭这些游标对象
  private List<Cursor<?>> cursorList;

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

         在 DefaultSqlSession 中使用到了策略模式, DefaultSqlSession 扮演了 Context 的角色,而将 所有数据库相关的操作全部封装到 Executor 接口实现中,并通过 executor 字段选择不同的 Executor 实现。

    DefaultSqlSession 中实现了 SqISession 接口中定义的方法, 并且为每种数据库操作提供了多 个重载。 图 3-55 为 select()方法、 selectOne()方法、 selectList()方法以及 selectMap()方法的各个 重载方法之间的调用关系。

  

 

      上述重载方法最终都是通过调用 Executor.query(MappedStatement, Object, RowBounds, ResultHandler)方法实现数据库查询操作的,但各自对结果对象进行 了 相应的调整 ,例如 :

  •       selectOne()方法是从结果对象集合中获取了第一个元素返回;
  •       selectMap()方法会将 List 类型的结 果对象集合转换成 Map 类型集合返回:
  •       select()方法是将结果对象集合交 由用户 指定的 ResultHandler 对象处理,且没有返回值:   
  •       selectList()方法则是直接返回结果对象集合。

   所有select方法最终都会掉线下面两个方法

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //从configuration中获取要执行的sql语句的配置信息
      MappedStatement ms = configuration.getMappedStatement(statement);
      //通过executor执行语句,并返回指定的结果集
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
@Override
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }


       DefaultSqlSession.insert()方法、 update()方法、 delete()方法也有多个重载,它们最后都是通 过调用的DefaultSqlSession.update(String, Object)方法实现的,该重载首先会将dirty字段置为true, 然后再通过 Executor.update()方法完成数据库修改操作。

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 

    DefaultSqlSession.commit()方法、 rollback()方法以及 close()方法都会调用 Executor 中相应的 方法,其中就会涉及清空缓存的操作(具体实现请读者参考 Executor 小节〉, 之后就会将 dirty 字段设置为 false。

     上述的 dirty 宇段主要在 isCommitOrRollbackRequired()方法中, 与 autoCommit 字段以及用 户传入的 force 参数共同决定是否提交/回滚事务,具体实现如下所示。 该方法的返回值将作为 Executor.commit()方法和 rollback()方法的参数。

 @Override
  public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
  }

所以归根结底,SqlSession的所有查询接口最后都归结位Exector的方法调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值