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

Mybatis|SqlSession四大对象(二)

作者: GGarrett | 来源:发表于2018-11-29 00:05 被阅读0次

    1. SqlSession中四大神器之ParameterHandler

    1.1 ParameterHandler介绍

    参数处理器负责为ParameterHandler的sql语句参数动态赋值。

    /**
     * A parameter handler sets the parameters of the {@code PreparedStatement}
     *
     * @author Clinton Begin
     */
    public interface ParameterHandler {
    
      Object getParameterObject();
    
      void setParameters(PreparedStatement ps)
          throws SQLException;
    
    }
    

    这个接口中只有两个方法:
    getParameterObject 方法用于读取参数。
    setParameters用于对ParameterHandler的参数赋值。

    1.2 ParameterHandler对象创建

    参数处理器对象时在创建StatementHandler对象时同时被创建的,由configuration对象负责创建。

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
    
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
    
        if (boundSql == null) { // issue #435, get the key before calculating the statement
          generateKeys(parameterObject);
          boundSql = mappedStatement.getBoundSql(parameterObject);
        }
    
        this.boundSql = boundSql;
    
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
      }
    

    创建时传入三个对象:mappedStatement(执行SQL对应的配置信息)、parameterObject、boundSql。

    注意:一个BoundSql对象,代表了SQL语句的一次执行,而SqlSource对象的责任就是根据传入的参数对象动态计算出这个BoundSql,也就是说Mapper文件中的节点的计算是由SqlSource对象完成的。SqlSource最常用是实现类是DynamicSqlSource。

    1.3 ParameterHandler中的参数从何而来?
     @Test
         public void Test01(){
                DeptDao dao =  session.getMapper(DeptDao.class);
                Dept dept = dao.findByDeptNo(10);
                System.out.println(dept.getDname());
         }
    

    看看上述的实参10是如何添加到对应的SQL语句中的

     <select id="findByDeptNo" resultType="dept">
            <include refid="DeptFindSql"></include>
            where deptno = #{deptNo}
       </select>
    

    在MyBatis中,使用动态代理模式,当dao.findByDeptNo(10)将要执行的时候,会被JVM进行拦截交给MyBatis中的代理实现类MapperProxy中的invoke方法。

    @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }
    

    最后交给ParameterHandler中setParameter方法,将参数交给对应占位符。

     @Override
      public Object getParameterObject() {
        return parameterObject;
      }
    
    @Override
      public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        //parameterMappings  是对#{}里面的参数的封装,
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //如果是参数化sql(sql语句中带占位符?)便要设置参数   
        if (parameterMappings != null) {
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty();
              if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) {
                value = null;
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                value = parameterObject;
              } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (TypeException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              } catch (SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
    

    读取ParameterObject参数对象,然后用typeHandler对参数进行设置,而typeHandler里面需要对jdbcType和javaType进行处理,设置参数。所以使用TypeHandler的时候完全可以控制如何设置SQL参数。

    2. Sqlsession四大神器之ResultSetHandler

    2.1 ResultSetHandler介绍

    ResultSetHandler接口主要负责两件事:

    • 处理Statement执行后产生的结果集,生成结果列表。
    • 处理存储过程执行后的输出参数 。
    /**
     * @author Clinton Begin
     */
    public interface ResultSetHandler {
    
      <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    
      <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
    
      void handleOutputParameters(CallableStatement cs) throws SQLException;
    
    }
    

    相关文章

      网友评论

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

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