暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

003 - prepareStatement(sql)方法执行过程

博博JAVA学习之路 2021-02-18
494

Connection.prepareStatement(String sql)方法执行过程

1. prepareStatement(String sql)方法调用代码

    // 主要作用:对预编译的sql语句与参数内容组装
    String sql = "select id, name, password, is_delete, create_by, create_time, modify_by, modify_time from user where name like concat_ws('', '%', ?,'%') and is_delete = ? order by create_time desc";
    log.info("== sql = " + sql);
    Connection conn = jdbcUtil.getConnection();
    // bobo 此处,已经将sql进行拆分了,以?为分隔符,进行分割,并存放到staticSql字节数组中,以ParseInfo类对象存储,进行数据传递
    // bobo 通过ParseInfo对象获取需要参数个数,并初始化参数数组对象bindValues,以ClientPreparedQueryBindings类对象存储,进行数据传递
    // bobo 此时,参数内容是null值,在设置参数环节,对bindValues数组对象进行赋值操作
    // byte[][] staticSql
    // T[] bindValues
    PreparedStatement ps = conn.prepareStatement(sql);
    // bobo 参数的赋值操作,最终赋值给bindValues数组对象中,并且设置好mysql的参数类型,在执行sql的时候,使用拼装好的sql语句执行
    ps.setString(1, username);
    ps.setString(2, "0");


    ResultSet rs = ps.executeQuery();

    2. Connection.prepareStatement(String sql)方法

    ConnectionImpl.java

      /**
      * - 对sql语句进行预编译过程
      */
      @Override
      public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException {
      // bobo sql - 预编译的sql语句
      // bobo DEFAULT_RESULT_SET_TYPE - 默认的结果集类型
      // bobo DEFAULT_RESULT_SET_CONCURRENCY - 默认的结果集并发效果,指示可能不会更新的ResultSet对象的并发模式的常量。 return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
      }

      ConnectionImpl.java

        /**
        * - 预编译sql语句
        */
        @Override
        public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        // bobo 并发,加锁
        synchronized (getConnectionMutex()) {
        // bobo 校验连接是否关闭了
        checkClosed();


        //
        // FIXME: Create warnings if can't create results of the given type or concurrency
        // - 修正:如果不能创建给定类型或并发性的结果,则创建警告
        //
        // bobo ClientPreparedStatement是StatementImpl实现类的子类
        ClientPreparedStatement pStmt = null;


        boolean canServerPrepare = true;


        // bobo 预编译的sql语句,本方法范围内
        String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;


        // bobo false && true
        if (this.useServerPrepStmts.getValue() && this.emulateUnsupportedPstmts.getValue()) {
        canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
        }


        // bobo false && true
        if (this.useServerPrepStmts.getValue() && canServerPrepare) {
        if (this.cachePrepStmts.getValue()) {
        synchronized (this.serverSideStatementCache) {
        pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql));


        if (pStmt != null) {
        ((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).setClosed(false);
        pStmt.clearParameters();
        }


        if (pStmt == null) {
        try {
        pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType,
        resultSetConcurrency);
        if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
        ((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).isCacheable = true;
        }


        pStmt.setResultSetType(resultSetType);
        pStmt.setResultSetConcurrency(resultSetConcurrency);
        } catch (SQLException sqlEx) {
        // Punt, if necessary
        if (this.emulateUnsupportedPstmts.getValue()) {
        pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);


        if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
        this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
        }
        } else {
        throw sqlEx;
        }
        }
        }
        }
        } else {
        try {
        pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, resultSetConcurrency);


        pStmt.setResultSetType(resultSetType);
        pStmt.setResultSetConcurrency(resultSetConcurrency);
        } catch (SQLException sqlEx) {
        // Punt, if necessary
        if (this.emulateUnsupportedPstmts.getValue()) {
        pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
        } else {
        throw sqlEx;
        }
        }
        }
        } else {
        // bobo 创建一个ClientPreparedStatement对象
        pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
        }


        return pStmt;
        }
        }

        ConnectionImpl.java

          /**
          * - 创建一个ClientPreparedStatement对象
          * - sql - 预编译sql语句
          * - resultSetType - 结果集类型
          * - resultSetConcurrency - 结果集并发效果
          */
          public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, boolean processEscapeCodesIfNeeded)
          throws SQLException {


          // bobo 预编译sql语句
          String nativeSql = processEscapeCodesIfNeeded && this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;


          // bobo 创建ClientPreparedStatement对象
          ClientPreparedStatement pStmt = null;


          // bobo 是否缓存预编译语句
          if (this.cachePrepStmts.getValue()) {
          ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql);


          if (pStmtInfo == null) {
          pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);


          this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo());
          } else {
          pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, pStmtInfo);
          }
          } else {
          // bobo 获取ClientPreparedStatement实例对象
          pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
          }


          // bobo 设置结果集类型
          pStmt.setResultSetType(resultSetType);
          // bobo 设置结果集并发效果
          pStmt.setResultSetConcurrency(resultSetConcurrency);


          return pStmt;
          }

          执行语句 ConnectionImpl.java 的 getInstance(JdbcConnection conn, String sql, String db)

          ClientPreparedStatement.java

            /**
            * Creates a prepared statement instance
            * - 创建一个预编译语句实例对象
            *
            * @param conn
            * the connection creating this statement
            * - 创建此语句的连接
            * @param sql
            * the SQL for this statement
            * - 此语句的SQL
            * @param db
            * the database this statement should be issued against
            * - 执行此语句对应的数据库
            * @return ClientPreparedStatement
            * @throws SQLException
            * if a database access error occurs
            * - 数据库访问错误
            */
            protected static ClientPreparedStatement getInstance(JdbcConnection conn, String sql, String db) throws SQLException {
            // bobo 创建一个新ClientPreparedStatement对象
            return new ClientPreparedStatement(conn, sql, db);
            }

            ClientPreparedStatement.java

              /**
              * Constructor for the PreparedStatement class.
              * - PreparedStatement类的构造函数
              *
              * @param conn
              * the connection creating this statement
              * - 创建此语句的连接
              * @param sql
              * the SQL for this statement
              * - 此语句的SQL
              * @param db
              * the database this statement should be issued against
              * - 执行此语句对应的数据库
              *
              * @throws SQLException
              * if a database error occurs.
              * - 数据库访问错误
              */
              public ClientPreparedStatement(JdbcConnection conn, String sql, String db) throws SQLException {
              this(conn, sql, db, null);
              }

              ClientPreparedStatement.java

                /**
                * Creates a new PreparedStatement object.
                * - 创建一个新的PreparedStatement对象
                *
                * @param conn
                * the connection creating this statement
                * - 创建此语句的连接
                * @param sql
                * the SQL for this statement
                * - 此语句的SQL
                * @param db
                * the database this statement should be issued against
                * - 执行此语句对应的数据库
                * @param cachedParseInfo
                * already created parseInfo or null.
                * - 已经创建的parseInfo或者是null。*
                * @throws SQLException
                * if a database access error occurs
                * - 数据库访问错误
                */
                public ClientPreparedStatement(JdbcConnection conn, String sql, String db, ParseInfo cachedParseInfo) throws SQLException {
                // bobo 构造,初始化对象数据
                this(conn, db);


                try {
                // bobo 校验sql语句是否是null或者""
                ((PreparedQuery<?>) this.query).checkNullOrEmptyQuery(sql);
                // bobo 设置源sql语句
                ((PreparedQuery<?>) this.query).setOriginalSql(sql);
                // bobo 设置ParseInfo对象,创建一个ParseInfo对象
                ((PreparedQuery<?>) this.query).setParseInfo(cachedParseInfo != null ? cachedParseInfo : new ParseInfo(sql, this.session, this.charEncoding));
                } catch (CJException e) {
                throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor);
                }


                // bobo 是否需要先ping一下数据库连接
                this.doPingInstead = sql.startsWith(PING_MARKER);


                // 以ParseInfo对象为基础,初始化数据
                initializeFromParseInfo();
                }

                执行语句 ClientPreparedStatement.java 的 this(conn, db);

                ClientPreparedStatement.java

                  /**
                  * Constructor used by server-side prepared statements
                  * - 服务器端准备语句使用的构造函数
                  *
                  * @param conn
                  * the connection that created us
                  * - 创建的连接
                  * @param db
                  * the database in use when we were created
                  * - 创建时使用的数据库
                  *
                  * @throws SQLException
                  * if an error occurs
                  * - 如果出现错误
                  */
                  protected ClientPreparedStatement(JdbcConnection conn, String db) throws SQLException {
                  // bobo 父类构造方法,StatementImpl
                  super(conn, db);


                  // bobo 可以放到缓存池中
                  setPoolable(true);
                  // bobo 抵消重复更新 false
                  this.compensateForOnDuplicateKeyUpdate = this.session.getPropertySet().getBooleanProperty(PropertyKey.compensateOnDuplicateKeyUpdateCounts).getValue();
                  }

                  执行语句 ClientPreparedStatement.java 的 checkNullOrEmptyQuery(String sql);

                  AbstractPreparedQuery.java

                    /**
                    * Method checkNullOrEmptyQuery.
                    * - 校验查询sql是否是null或""的方法
                    *
                    * @param sql
                    * the SQL to check
                    * - 需要校验的sql语句
                    *
                    * @throws WrongArgumentException
                    * if query is null or empty.
                    * - 如果sql语句是null或者""
                    */
                    public void checkNullOrEmptyQuery(String sql) {
                    // bobo sql语句是否是null
                    if (sql == null) {
                    throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedQuery.0"), this.session.getExceptionInterceptor());
                    }


                    // bobo sql语句是""
                    if (sql.length() == 0) {
                    throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedQuery.1"), this.session.getExceptionInterceptor());
                    }
                    }

                    执行语句  setParseInfo(ParseInfo parseInfo)

                    ParseInfo.java

                      /**
                      * - 创建一个ParseInfo实例
                      * - sql - 预编译sql语句
                      * - session - 会话,调用者
                      * - encoding - 编码格式
                      */
                      public ParseInfo(String sql, Session session, String encoding) {
                      // bobo 调用构造
                      this(sql, session, encoding, true);
                      }

                      此方法是将预编译的sql语句以预编译的参数 "?" 进行拆分,截取对应的sql语句数组。

                      例如:

                      sql语句 :

                        select id, name, password, is_delete, create_by, create_time, modify_by, modify_time from user where name like concat_ws('', '%', ?,'%') and is_delete = ? order by create_time desc

                        staticSql数组就会进行如下的拆分:

                          staticSql[0] = select id, name, password, is_delete, create_by, create_time, modify_by, modify_time from user where name like concat_ws('', '%', 
                          staticSql[1] = ,'%') and is_delete =
                          staticSql[2] = order by create_time desc

                          ParseInfo.java

                            /**
                            * - 创建一个ParseInfo对象
                            * - sql - 预编译sql语句
                            * - session - 会话,调用者
                            * - encoding - 编码格式
                            * - buildRewriteInfo - 构建重写信息
                            */
                            public ParseInfo(String sql, Session session, String encoding, boolean buildRewriteInfo) {


                            try {
                            // bobo 校验sql语句
                            if (sql == null) {
                            throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.61"),
                            session.getExceptionInterceptor());
                            }


                            // bobo 设置编码格式
                            this.charEncoding = encoding;
                            // bobo 设置上次调用时间点
                            this.lastUsed = System.currentTimeMillis();


                            // bobo 调用者字符串 是`值
                            String quotedIdentifierString = session.getIdentifierQuoteString();


                            // bobo 调用者的字符串
                            char quotedIdentifierChar = 0;


                            if ((quotedIdentifierString != null) && !quotedIdentifierString.equals(" ") && (quotedIdentifierString.length() > 0)) {
                            quotedIdentifierChar = quotedIdentifierString.charAt(0);
                            }


                            // sql语句的长度
                            this.statementLength = sql.length();


                            // bobo 创建一个端点列表,用来存放两个端点的值,及开始字符串的位置和结束字符串的位置
                            ArrayList<int[]> endpointList = new ArrayList<>();
                            // bobo 是否在引号中
                            boolean inQuotes = false;
                            // bobo 引号字符串char值
                            char quoteChar = 0;
                            // bobo 是否在引号中的id
                            boolean inQuotedId = false;
                            // bobo 上次结束的位置
                            int lastParmEnd = 0;
                            int i;


                            // bobo 没有反斜杠转义 false
                            boolean noBackslashEscapes = session.getServerSession().isNoBackslashEscapesSet();


                            // we're not trying to be real pedantic here, but we'd like to skip comments at the beginning of statements, as frameworks such as Hibernate
                            // use them to aid in debugging
                            // bobo 我们不想在这里卖弄学问,但是我们想跳过语句开头的注释,就像Hibernate这样的框架一样
                            // bobo 使用它们来帮助调试


                            // bobo 查找sql语句中注释的位置, -- 或者 **/ 或者 #
                            this.statementStartPos = findStartOfStatement(sql);


                            // bobo 遍历sql语句的所有字符
                            for (i = this.statementStartPos; i < this.statementLength; ++i) {
                            // bobo 获取当前字符
                            char c = sql.charAt(i);


                            // bobo 第一个字符 && 是一个字母
                            if ((this.firstStmtChar == 0) && Character.isLetter(c)) {
                            // Determine what kind of statement we're doing (_S_elect, _I_nsert, etc.)
                            // bobo 确定我们正在执行的语句类型(_S_elect, _I_nsert,等等)
                            // bobo sql语句的首字母大写是 S
                            this.firstStmtChar = Character.toUpperCase(c);


                            // no need to search for "ON DUPLICATE KEY UPDATE" if not an INSERT statement
                            // bobo 如果不是INSERT语句,不需要搜索“ON DUPLICATE KEY UPDATE”
                            if (this.firstStmtChar == 'I') {
                            this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql,
                            session.getPropertySet().getBooleanProperty(PropertyKey.dontCheckOnDuplicateKeyUpdateInSQL).getValue(),
                            session.getPropertySet().getBooleanProperty(PropertyKey.rewriteBatchedStatements).getValue(),
                            session.getServerSession().isNoBackslashEscapesSet());
                            this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1;
                            }
                            }


                            // bobo 是否是转义字符
                            if (!noBackslashEscapes && c == '\\' && i < (this.statementLength - 1)) {
                            i++;
                            continue; // next character is escaped 下一个转义字符
                            }


                            // are we in a quoted identifier? (only valid when the id is not inside a 'string')
                            // bobo 我们是在带引号的标识符中吗?(仅当id不在'string'内时有效)
                            if (!inQuotes && (quotedIdentifierChar != 0) && (c == quotedIdentifierChar)) {
                            inQuotedId = !inQuotedId;
                            } else if (!inQuotedId) {
                            // only respect quotes when not in a quoted identifier
                            // bobo 只有在标识符中没有引号时才使用引号


                            // bobo是否是引号
                            if (inQuotes) {
                            if (((c == '\'') || (c == '"')) && c == quoteChar) {
                            if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
                            i++;
                            continue; // inline quote escape
                            }


                            inQuotes = !inQuotes;
                            quoteChar = 0;
                            } else if (((c == '\'') || (c == '"')) && c == quoteChar) {
                            inQuotes = !inQuotes;
                            quoteChar = 0;
                            }
                            } else {
                            // bobo 是否是#或者--,即是否是注释语句,如果是,那么这段注释中的内容都不做处理,直接忽略
                            if (c == '#' || (c == '-' && (i + 1) < this.statementLength && sql.charAt(i + 1) == '-')) {
                            // run out to end of statement, or newline, whichever comes first
                            // bobo 运行到语句结束,或换行,以最先出现的为限
                            // bobo 最后一个字符的位置
                            int endOfStmt = this.statementLength - 1;


                            // bobo 从当前位置向后遍历
                            for (; i < endOfStmt; i++) {
                            // bobo 获取字符内容
                            c = sql.charAt(i);


                            // bobo 如果是\r 或者 \n 则跳出循环
                            if (c == '\r' || c == '\n') {
                            break;
                            }
                            }


                            continue;
                            } else if (c == '/' && (i + 1) < this.statementLength) {
                            // bobo 如果是

                            // Comment?
                            // bobo 注释

                            // bobo 下一个字符的值
                            char cNext = sql.charAt(i + 1);


                            // bobo 下一个字符内容是*,则是注释语句
                            if (cNext == '*') {
                            // bobo 跳过注释字符串,即/*
                            i += 2;


                            // bobo 从当前位置遍历
                            for (int j = i; j < this.statementLength; j++) {
                            // bobo 忽略注释中的内容
                            i++;
                            // bobo 获取字符内容
                            cNext = sql.charAt(j);


                            // bobo 是*字符
                            if (cNext == '*' && (j + 1) < this.statementLength) {
                            // bobo 是/,则判定注释已经结束
                            if (sql.charAt(j + 1) == '/') {
                            // bobo 跳到注释结束后的字符位置
                            i++;


                            // bobo 是否是最后一个字符的位置
                            if (i < this.statementLength) {
                            // bobo 获取注释结束后的第一个字符内容
                            c = sql.charAt(i);
                            }


                            break; // comment done 注释完成
                            }
                            }
                            }
                            }
                            } else if ((c == '\'') || (c == '"')) {
                            // bobo 是单引号或双引号
                            // bobo 在引号中
                            inQuotes = true;
                            // bobo 是哪个引号,即单引号还是双引号
                            quoteChar = c;
                            }
                            }
                            }


                            // bobo 是否在引号中
                            if (!inQuotes && !inQuotedId) {
                            // bobo 是否是?,即需要带入参数的位置
                            if ((c == '?')) {
                            // bobo 端点列表中增加一组端点值,即上次记录的位置为起始值,当前位置为结束值
                            endpointList.add(new int[] { lastParmEnd, i });
                            // bobo 上次结束位置
                            lastParmEnd = i + 1;


                            // bobo 是否是重复更新
                            if (this.isOnDuplicateKeyUpdate && i > this.locationOfOnDuplicateKeyUpdate) {
                            this.parametersInDuplicateKeyClause = true;
                            }
                            } else if (c == ';') {
                            // bobo 是分号;
                            // bobo 下一个字符的位置
                            int j = i + 1;
                            // bobo 是否是语句的结尾位置
                            if (j < this.statementLength) {
                            // bobo 从当前位置遍历
                            for (; j < this.statementLength; j++) {
                            // bobo 是否是空白字符,不是空白字符,跳出循环
                            if (!Character.isWhitespace(sql.charAt(j))) {
                            break;
                            }
                            }
                            // bobo 是否是语句的结尾位置
                            if (j < this.statementLength) {
                            // bobo 查询的数量+1,即多查询sql语句
                            this.numberOfQueries++;
                            }
                            // bobo 分号后的第一个非空白字符
                            i = j - 1;
                            }
                            }
                            }
                            }


                            // bobo sql语句的首字符是否是L
                            if (this.firstStmtChar == 'L') {
                            // bobo 是否是以Load data 开头的
                            if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) {
                            this.foundLoadData = true;
                            } else {
                            this.foundLoadData = false;
                            }
                            } else {
                            this.foundLoadData = false;
                            }


                            // bobo 端点列表增加最后一组数据,即上次结束的位置为起始值,sql语句的长度为结束值
                            endpointList.add(new int[] { lastParmEnd, this.statementLength });
                            // bobo 初始化静态的sql语句的字节数组,其实就是将sql语句按照占位符?进行裁剪,分成多个字节数组
                            this.staticSql = new byte[endpointList.size()][];
                            // bobo 是否有占位符,即? this.hasPlaceholders = this.staticSql.length > 1;


                            // bobo 遍历静态sql语句的字节数组,并设置其内容
                            for (i = 0; i < this.staticSql.length; i++) {
                            // bobo 获取端点列表的数据
                            int[] ep = endpointList.get(i);
                            // bobo 结束位置
                            int end = ep[1];
                            // bobo 起始位置
                            int begin = ep[0];
                            // bobo 字符长度
                            int len = end - begin;


                            // bobo 是否加载数据
                            if (this.foundLoadData) {
                            this.staticSql[i] = StringUtils.getBytes(sql, begin, len);
                            } else if (encoding == null) {
                            // bobo 编码格式为null
                            byte[] buf = new byte[len];


                            for (int j = 0; j < len; j++) {
                            buf[j] = (byte) sql.charAt(begin + j);
                            }


                            this.staticSql[i] = buf;
                            } else {
                            // bobo 截取sql语句的字符串,并转换为字节,存放到sataicSql字节数组中
                            this.staticSql[i] = StringUtils.getBytes(sql, begin, len, encoding);
                            }
                            }
                            } catch (StringIndexOutOfBoundsException oobEx) {
                            throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.62", new Object[] { sql }), oobEx,
                            session.getExceptionInterceptor());
                            }


                            // bobo 是否重新写入信息
                            if (buildRewriteInfo) {
                            // bobo 多值插入可以重新写入
                            this.canRewriteAsMultiValueInsert = this.numberOfQueries == 1 && !this.parametersInDuplicateKeyClause
                            && canRewrite(sql, this.isOnDuplicateKeyUpdate, this.locationOfOnDuplicateKeyUpdate, this.statementStartPos);
                            if (this.canRewriteAsMultiValueInsert && session.getPropertySet().getBooleanProperty(PropertyKey.rewriteBatchedStatements).getValue()) {
                            buildRewriteBatchedParams(sql, session, encoding);
                            }
                            }


                            }

                            执行语句 ClientPreparedStatement.java 的 initializeFromParseInfo();

                            ClientPreparedStatement.java

                              /**
                              * - 以ParseInfo对象为基础,初始化数据,即初始化sql语句中占位符的数据
                              */
                              @SuppressWarnings("unchecked")
                              private void initializeFromParseInfo() throws SQLException {
                              // bobo 同步代码块
                              synchronized (checkClosed().getConnectionMutex()) {


                              // bobo 预编译时参数的数量
                              int parameterCount = ((PreparedQuery<ClientPreparedQueryBindings>) this.query).getParseInfo().getStaticSql().length - 1;
                              // bobo 设置参数的数量
                              ((PreparedQuery<?>) this.query).setParameterCount(parameterCount);
                              // bobo 设置预编译的参数数据
                              ((PreparedQuery<ClientPreparedQueryBindings>) this.query).setQueryBindings(new ClientPreparedQueryBindings(parameterCount, this.session));
                              // bobo 设置加载数据查询
                              ((ClientPreparedQuery) this.query).getQueryBindings().setLoadDataQuery(((PreparedQuery<?>) this.query).getParseInfo().isFoundLoadData());


                              // bobo 清除参数内容
                              clearParameters();
                              }
                              }

                              执行语句 ClientPreparedStatement.java 的 ClientPreparedQueryBindings(int parameterCount, Session sess)

                              ClientPreparedQueryBindings.java

                                /**
                                * - 创建ClientPreparedQueryBindings对象
                                * - parameterCount - 参数数量
                                * - sess - 调用者
                                */
                                public ClientPreparedQueryBindings(int parameterCount, Session sess) {
                                // bobo 父类构造
                                super(parameterCount, sess);
                                // bobo 获取请求的编码格式,是否需要进行转换
                                if (((NativeSession) this.session).getRequiresEscapingEncoder()) {
                                this.charsetEncoder = Charset.forName(this.charEncoding).newEncoder();
                                }
                                }

                                此方法是初始化预编译参数bindValues数组,即以 "?" 占位符的参数数组bindValues,在之后的setString()等方法时,将参数直接赋值给bindValues 数组的对应位置处。

                                执行语句 ClientPreparedQueryBindings.java 的super(parameterCount, sess)

                                AbstractQueryBindings.java

                                  /**
                                  * - 创建AbstractQueryBindings对象
                                  * - parameterCount - 参数数量
                                  * - sess - 调用者
                                  */
                                  public AbstractQueryBindings(int parameterCount, Session sess) {
                                  // bobo 初始化数据
                                  // bobo 调用者
                                  this.session = sess;
                                  // bobo 编码格式 UTF-8
                                  this.charEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue();
                                  // bobo 发送小数的时间
                                  this.sendFractionalSeconds = this.session.getPropertySet().getBooleanProperty(PropertyKey.sendFractionalSeconds);
                                  // bobo 将UtilDate的时间格式视为时间戳格式的日期
                                  this.treatUtilDateAsTimestamp = this.session.getPropertySet().getBooleanProperty(PropertyKey.treatUtilDateAsTimestamp);
                                  // bobo 使用预编译的流的长度
                                  this.useStreamLengthsInPrepStmts = this.session.getPropertySet().getBooleanProperty(PropertyKey.useStreamLengthsInPrepStmts);


                                  // bobo 初始化bindValues的数据
                                  initBindValues(parameterCount);
                                  }

                                  执行语句 AbstractQueryBindings.java 的 initBindValues(parameterCount)

                                  ClientPreparedQueryBindings.java

                                    /**
                                    * - 初始化bindValues的数据
                                    * - parameterCount - 参数数量
                                    */
                                    @Override
                                    protected void initBindValues(int parameterCount) {
                                    // bobo 为单个字段绑定值
                                    // bobo 创建bindValues数据对象,初始化数据长度,
                                    this.bindValues = new ClientPreparedQueryBindValue[parameterCount];
                                    // bobo 初始化数组的数据
                                    for (int i = 0; i < parameterCount; i++) {
                                    this.bindValues[i] = new ClientPreparedQueryBindValue();
                                    }
                                    }

                                    执行语句 ClientPreparedStatement.java 的 clearParameters();

                                    ClientPreparedStatement.java

                                      /**
                                      * - 清除参数内容
                                      */
                                      @Override
                                      public void clearParameters() throws SQLException {
                                      // bobo 同步代码块,加锁
                                      synchronized (checkClosed().getConnectionMutex()) {
                                      // bobo 重置参数数据
                                      for (BindValue bv : ((PreparedQuery<?>) this.query).getQueryBindings().getBindValues()) {
                                      bv.reset();
                                      }
                                      }
                                      }


                                      文章转载自博博JAVA学习之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                      评论