美文网首页程序员
Mybatis|SqlSession四大对象(一)

Mybatis|SqlSession四大对象(一)

作者: GGarrett | 来源:发表于2018-11-28 21:20 被阅读0次

    1. SqlSession四大对象之Executor

    1.1 Executor简介

    每一个SqlSession对象都被分配一个Executor,主要负责connection获取和statement对象管理方案。

    1.2 Statement对象给管理方案
    1. 简单管理方案:一个Statement接口对象只执行一次。执行完毕就会Statement接口对象进行销毁。
    2. 可重用方案: 使用一个Map集合,关键字就是一条Sql语句。对应内容Statement接口对象,等到SqlSession再次接收到相同命令时,就从map集合找到对应Statement接口使用map.put("select * from order", Statement1)。
    3. 批处理管理方案:将多个Statement包含的SQL语句,交给一个Statement对象输送到数据库,形成批处理操作。
    1.3 Executor继承结构
    • Excutor主要有两个实现类:
      BaseExcutor:是一个抽象类,这种通过抽象类实现接口的方式是适配器模式的体现,主要用于方便一级子类对接口中方法的实现。
      CachingExcutor(缓存执行器):提高效率,MyBatis框架默认到缓存中寻找对应的数据,如果存在就返回,不存在,再委托给其他执行器。
     @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
    • ReuseExecutor(可重用执行器):这里指的可重用是statement,它会在内部利用一个Map把创建的Statement都缓存起来,每次执行一条SQL语句时,它都会去判断之前是否存在基于该SQL缓存的Statement对象,存在,而且之前缓存的Statement对象对应的connection还没有关闭的时候就会使用之前Statement对象,否则将创建个新的Statement对象,并将其缓存起来。因为每一个新的sqlSession都有一个新的Executor对象,所以我们缓存在ReuseExecutor行的Statement的作用域是同一个sqlSession。
     private boolean hasStatementFor(String sql) {
        try {
          return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
        } catch (SQLException e) {
          return false;
        }
      }
    
      private Statement getStatement(String s) {
        return statementMap.get(s);
      }
    
      private void putStatement(String sql, Statement stmt) {
        statementMap.put(sql, stmt);
      }
    
    • BatchExecutor(批处理执行器):用于将多个sql语句一次性输送到数据库执行。(将多个statement中的sql语句存入到一个statement中)
    @Override
      public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        final Configuration configuration = ms.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
        final BoundSql boundSql = handler.getBoundSql();
        final String sql = boundSql.getSql();
        final Statement stmt;
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
          int last = statementList.size() - 1;
          stmt = statementList.get(last);
          applyTransactionTimeout(stmt);
         handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt); //
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
    
    • SimpleExecutor(简单执行器):是MyBatis中默认的执行器,每执行一次update或select ,就开启一个statement对象,用完立刻关闭statement对象。
     @Override
      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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    
    1.4 Executor对象的创建

    执行器对象由Coniguration对象负责创建(SqlSessionFactory),Coniguration对象会根据得到【ExecutorType】创建对应的Excecutor对象,并把这个Excecutor对象传给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);
    
    
          final Executor executor = configuration.newExecutor(tx, execType);
          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();
        }
      }
    

    继续往newExecutor里面看一下,会根据选择器的类型来配合cacheEnabled来配合使用。

     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;
      }
    
    1.5 ExcecutorType选择

    ExecutorType来决定Configuration对象创建何种类型的执行器。它的赋值可以通过两个地方进行赋值:

    • 通过<settings>标签来设置当前工程中所有SqlSession对象使用的默认Executour
    <settings>
        <setting name="cacheEnabled" value="false"/>
      <!--  <setting name="defaultExcutorType" value="SIMPLE"/> -->
      <!-- <setting name="defaultExcutorType" value="REUSE"/> -->
        <setting name="defaultExcutorType" value="BATCH"/>
    </settings>
    
    • 通过SqlSessionFactory中openSession方法来指定具体的SqlSession使用的执行器
     @Before
         public void start() throws IOException{
                InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
                SqlSessionFactoryBuilder builderObj = new SqlSessionFactoryBuilder();
                SqlSessionFactory factory = builderObj.build(is);
                factory.openSession(ExecutorType.BATCH);
                session = factory.openSession();
         }
    

    2. SqlSession四大对象之StatementHandler

    2.1 StatementHandler介绍

    是四大神器中最重要的一个对象,负责操作Statement与数据库进行交流。在工作时还会使用ParameterHandler进行参数配置,使用ResultHandler将查询结果与实体类对象进行绑定。

    在StatementHandler接口中有四种重要 方法,分别是:

    //创建Statement对象或则PreparedStatment或则CallableStatment
    Statement prepare(Connection connection, Integer transactionTimeout)
          throws SQLException;
      //主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
      void parameterize(Statement statement)
          throws SQLException;
      //用于通知Statement将[insert,update,delete]推送到数据库
      int update(Statement statement)
          throws SQLException;
      //用于通知Statement将[select]推送到数据库并返回对应查询结果
      <E> List<E> query(Statement statement, ResultHandler resultHandler)
          throws SQLException;
    
    2.2 StatementHandler继承结构
    • 从上图可以看到StatementHandler接口下有两个直接实现类:
      RoutingStatementHandler:是一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象。
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    
      }
    

    BaseStatementHandler:是个抽象类,用于简化StatementHandler接口开发的难度,是适配器模式的体现,他有三个实现类:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。在RoutingStatementHandler创建时,就根据接受的Excutor来创建这三个类型对象。

    • SimpleStatementHandler:管理statement对象向数据库中推送不需要预编译SQL语句。
    • PreparedStatementHandler:管理PreparedStatementHandler对象向数据库中推送预编译的SQL语句。
    • CallableStatementHandler:管理CallableStatementHandler调用数据库中的构造函数。
    2.3 StatementHandler对象的创建

    StatementHandler对象是在SqlSession对象接收到操作命令时,由Configuraion中newStatementHandler方法负责调用的。

    RoutingStatementHandler构造方法,将会根据Executor的类型决定创建SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler实例对象。

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
      }
    
    2.3 StatementHandler接口介绍
    @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          statement = instantiateStatement(connection);
          setStatementTimeout(statement, transactionTimeout);
          setFetchSize(statement);
          return statement;
        } catch (SQLException e) {
          closeStatement(statement);
          throw e;
        } catch (Exception e) {
          closeStatement(statement);
          throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
      }
    
    
    1. prepare方法:
    • prepare方法用于创建一个(Statement、PreparedStatement 、CallableStatement)对象,并设置statement对象最大等待时间和一次性读取的最大数据量,然后将生成statement对象返回。
    • prepare方法只在BaseStatementHandler被实现,再其三个子类中没有被重写,用于三个子类调用获得对应的statement接口对象。
    • prepare方法依靠instantiateStatement(connection)方法来实现返回具体的statement接口对象,这个方法是BaseStatementHandler中定义的抽象方法,由三个子类来具体实现。
      CallableStatementHandler方法中instantiateStatement(Connection connection)方法的实现
     @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        if (mappedStatement.getResultSetType() != null) {
          return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          return connection.createStatement();
        }
      }
    

    CallableStatementHandler

     @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
          String[] keyColumnNames = mappedStatement.getKeyColumns();
          if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
          } else {
            return connection.prepareStatement(sql, keyColumnNames);
          }
        } else if (mappedStatement.getResultSetType() != null) {
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          return connection.prepareStatement(sql);
        }
      }
    

    CallableStatementHandler

      @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getResultSetType() != null) {
          return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          return connection.prepareCall(sql);
        }
      }
    
    1. parameterize方法
      主要为PreparedStatement和CallableStatement传参,因此只在这两个对象中被重写。
      PreparedStatementHandler中的parameterize。
     @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    

    CallableStatementHandler中的parameterize.

      @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    

    在这两个方法中,可以看到都是ParameterHandler对象进行参数传参的。

    1. query方法
      输送查询语句,并将查询语句结果转换成对应的实体类。
      SimpleStatementHandler 中的 query 方法。
     @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = boundSql.getSql();
        statement.execute(sql);
        return resultSetHandler.<E>handleResultSets(statement);
      }
    

    PreparedStatementHandler中的query方法

    @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.<E> handleResultSets(ps);
      }
    

    CallableStatementHandler中的query方法

     @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
        resultSetHandler.handleOutputParameters(cs);
        return resultList;
      }
    

    可以看到在得到查询结果后,都是使用ResultSetHandler对结果进行转换.

    1. update方法
      输送insert、update、delete语句并返回处理数据行
      SimpleStatementHandler中的update方法
     @Override
      public int update(Statement statement) throws SQLException {
        String sql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        int rows;
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
          statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
          rows = statement.getUpdateCount();
          keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else if (keyGenerator instanceof SelectKeyGenerator) {
          statement.execute(sql);
          rows = statement.getUpdateCount();
          keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else {
          statement.execute(sql);
          rows = statement.getUpdateCount();
        }
        return rows;
      }
    

    PreparedStatementHandler中update方法

    @Override
      public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
      }
    

    CallableStatementHandler中update方法

    @Override
     public int update(Statement statement) throws SQLException {
       CallableStatement cs = (CallableStatement) statement;
       cs.execute();
       int rows = cs.getUpdateCount();
       Object parameterObject = boundSql.getParameterObject();
       KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
       keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
       resultSetHandler.handleOutputParameters(cs);
       return rows;
     }
    
    
    2.4 关于三种数据库操作对象选择(不需要人为控制)

    执行相关SQL语句:
    情况1: select * from order; SimpleStatementHander Statement
    情况2: select * from order where orderId=? PreparedStatementHandler PreparedStatement
    情况3: call{pro} CallableStatementHandler CallableStatment

    相关文章

      网友评论

        本文标题:Mybatis|SqlSession四大对象(一)

        本文链接:https://www.haomeiwen.com/subject/aoczqqtx.html