PgStatement的executeCachedSql(String sql, int flags, String @Nullable [] columnNames)方法的学习

方法代码如下:

private boolean executeCachedSql(String sql, int flags,
      String @Nullable [] columnNames) throws SQLException {
    //第一部分
    PreferQueryMode preferQueryMode = connection.getPreferQueryMode();
    boolean shouldUseParameterized = false;
    //第二部分
    QueryExecutor queryExecutor = connection.getQueryExecutor();
    //第三部分
    Object key = queryExecutor
        .createQueryKey(sql, replaceProcessingEnabled, shouldUseParameterized, columnNames);
    CachedQuery cachedQuery;
    //第四部分
    boolean shouldCache = preferQueryMode == PreferQueryMode.EXTENDED_CACHE_EVERYTHING;
    if (shouldCache) {
      cachedQuery = queryExecutor.borrowQueryByKey(key);
    } else {
      cachedQuery = queryExecutor.createQueryByKey(key);
    }
    //第五部分
    if (wantsGeneratedKeysOnce) {
      SqlCommand sqlCommand = cachedQuery.query.getSqlCommand();
      wantsGeneratedKeysOnce = sqlCommand != null && sqlCommand.isReturningKeywordPresent();
    }
    //第六部分
    boolean res;
    try {
      res = executeWithFlags(cachedQuery, flags);
    } finally {
      if (shouldCache) {
        queryExecutor.releaseQuery(cachedQuery);
      }
    }
    return res;
  }

对传入的参数进行分析,

  • sql显然是SQL语句;
  • flags根据名称只能推测是什么标志,待定;
  • columnNames为一个可为空的String数组,和列名有关,待定。

接下来,拟定分为六部分,对每一部分的代码进行分析。

第一部分

 PreferQueryMode preferQueryMode = connection.getPreferQueryMode();

该方法本质上为:

public PreferQueryMode getPreferQueryMode() {
    return queryExecutor.getPreferQueryMode();
  }

而queryExecutor为connection对象的一个属性

  /* Actual network handler */
  private final QueryExecutor queryExecutor;

查看QueryExecutor的注释

简单的总结:其抽象化了执行查询时与特定协议相关的细节,使得数据库连接可以通过单一的QueryExecutor实现来执行查询,而不必关心底层数据库协议或查询执行的具体细节。
而官方文档中有以下描述:

考虑到我们此次研究的方法,与之相关的点应该就是:提供创建Query对象的方法,并可以将该对象作为参数,执行查询语句。
查看Query类的注释

简单的总结:隐藏执行查询时所需的任何有关协议数据的细节,以便能够高效地执行查询。
不觉明历,看看官方文档

若有所得,在此基础上我们保持对该类的印象,回到第一部分的方法部分。
现在进度为queryExecutor.getPreferQueryMode(),按照当前的所得知的信息,Connection类中有一个QueryExecutor“类型”的属性,而QueryExecutor“类”中又有一个PreferQueryMode类的属性。
跳转到QueryExecutor接口的实现类QueryExecutorBase,找到preferQueryMode属性

我们发现,没有注释,继而跳到PreferQueryMode类的定义位置,查看其结构和注释

从其注释和明确的结构可知,PreferQueryMode类用于指定数据库的查询模式,Pgjdbc的查询模式如下:

  • SIMPLE("simple"):表示使用简单的查询执行模式,即直接以文本形式发送查询,不进行解析和绑定操作。
  • EXTENDED_FOR_PREPARED("extendedForPrepared"):表示仅对预处理语句使用扩展的查询执行模式,即使用bind/execute消息。
  • EXTENDED("extended"):表示对所有查询都使用扩展的查询执行模式。
  • EXTENDED_CACHE_EVERYTHING("extendedCacheEverything"):注释中未提到,从命名上看,它可能表示在扩展模式下缓存所有内容。

到此,我们已经知道了第一部分的作用,即获取当前连接指定的查询模式

第二部分

QueryExecutor queryExecutor = connection.getQueryExecutor();

该方法即获得该connection实例的queryExecutor属性,关于QueryExecutor在第一部分也有了提及,此处不再重复。
到此,我们知道了第二部分的作用,即获取当前连接的queryExecutor属性

第三部分

Object key = queryExecutor
        .createQueryKey(sql, replaceProcessingEnabled, shouldUseParameterized, columnNames);

首先,先分析一下传入的参数,sql、columnNames和shouldUseParameterized均在方法中提及,唯有replaceProcessingEnabled为方法所在类的属性,如下:

该属性未发现注释,字面意思理解为是否启用替换处理,结合上下文猜测应该为对sql语句是否启用替换处理,暂时如此推测,继续向下处理。
QueryExecutorBase类中找到该方法

@Override
  public final Object createQueryKey(String sql, boolean escapeProcessing,
      boolean isParameterized, String @Nullable ... columnNames) {
    Object key;
    if (columnNames == null || columnNames.length != 0) {
      // Null means "return whatever sensible columns are" (e.g. primary key, or serial, or something like that)
      key = new QueryWithReturningColumnsKey(sql, isParameterized, escapeProcessing, columnNames);
    } else if (isParameterized) {
      // If no generated columns requested, just use the SQL as a cache key
      key = sql;
    } else {
      key = new BaseQueryKey(sql, false, escapeProcessing);
    }
    return key;
  }

参数对应关系:sql同上,escapeProcessing对应是否启用替换处理,isParameterized对应是否启动参数化,columnNames未知。
结合注释推测,对传入的columnNames数组有了推测,其应该为select语句查询的字段名,根据本方法的判断逻辑,该数组应该是有三种状态:

null,表示全查
有限个字段名,查这些字段
String[0],表示该语句并非查询语句

因而,我们对该方法有了大致推测:

sql为SQL语句;
escapeProcessing对应是否启用替换处理(待定);
isParameterized对应是否启动参数化;
columnNames为若该sql为select语句时查询的字段名列表;
本方法应为:为该sql语句新建查询键。此处,将自学查询建的知识,对该部分进行补充。

对应判断逻辑为:

若columnNames为null或有值,则新建一个有返回值的查询建。
否则,若启动参数化,则使用该sql语句做查询键,
若未启动参数化,则新建一个Base查询键

总结,该部分的作用为为该sql语句新建查询键,该查询建默认启动参数化

第四部分

boolean shouldCache = preferQueryMode == PreferQueryMode.EXTENDED_CACHE_EVERYTHING;
    if (shouldCache) {
      cachedQuery = queryExecutor.borrowQueryByKey(key);
    } else {
      cachedQuery = queryExecutor.createQueryByKey(key);
    }

该部分的逻辑很容易理解,若该连接指定的查询模式为EXTENDED_CACHE_EVERYTHING,则根据查询键尝试从缓存中获得该查询,否则新建一个查询。
此部分对于该模式的运作不甚了解,猜测应该是在新建的缓存查询是共享的,能根据查询建找到就不要选择新建,不慎了解,不多言,待补充
通过查看该CachedQuery类的注释可知,其存储解析后的JDBC查询信息,其主要目的在于通过java.sql.Connection#prepareStatement(String)方法多次执行相同查询时,减少解析的开销。

总之,该部分的作用是:根据该查询键获得一个CachedQuery类的实例

第五部分

   if (wantsGeneratedKeysOnce) {
      SqlCommand sqlCommand = cachedQuery.query.getSqlCommand();
      wantsGeneratedKeysOnce = sqlCommand != null && sqlCommand.isReturningKeywordPresent();
    }

wantsGeneratedKeysOnce是PgStatement类的一个属性,其注释说明,其用来标记调用 execute 或 executeUpdate 方法时,调用者是否希望获取这次执行所生成的键。由此描述可知,该属性与执行的sql语句类型有关。
如果当前sql想要获得这次执行所生成的键,则调用cachedQuery.query.getSqlCommand()方法获得sqlCommand,若其非空且包含对应关键词,则更新wantsGeneratedKeysOnce。获得的qlCommand并未在之后的部分使用。
总之,该部分的作用为:更新wantsGeneratedKeysOnce属性

第六部分

该部分未到底层,需要进一步追踪
boolean res;
try {
res = executeWithFlags(cachedQuery, flags);
} finally {
if (shouldCache) {
queryExecutor.releaseQuery(cachedQuery);
}
}

该部分的主要部分为:res = executeWithFlags(cachedQuery, flags);,很明显该方法为执行查询,但至此我们对flags参数的含义和res的含义未加确定,因而追踪一步:

显然在此,flags的实际应用形式为二级制,每一位作为一个标志,每一位都能存储一条二元信息,其可以看作是一个二元数组的形式;res则为指示是否有返回值。因为该方法调用层次过多,故不在此详述
总结,本部分作用为:实现查询,并返回是否有返回结果的判断

总结

参数意义

String sql : sql语句
int flags:一个记录标志信息的二进制数组
String @Nullable [] columnNames :存储查询字段名的数组,如非查询语句,其为String[0],需要注意的是,null代表全体字段名,而不是异常。

结论

本文详细分析了PgStatement的executeCachedSql(String sql, int flags, String @Nullable [] columnNames)方法的作用,但对某些部分方法的并未追踪到底层,拟定另起文章对某些部分的底层方法进行学习分析。
随学随改

posted @ 2024-08-02 16:59  li-羡鱼  阅读(23)  评论(1编辑  收藏  举报