# 검사기가 무엇을 하는지, 그리고 무엇을 위한 것인지
7:/**
8: * Prefixes each statement with the registered query name that issued it (design §37).
9: *
10: * <p>The comment travels with the SQL into {@code pg_stat_activity}, {@code auto_explain} output,
11: * and the slow-query log, which is the only place a DBA can connect a statement back to the use
12: * case that produced it. Without it, "which endpoint issues this query" is answered by grepping the
13: * codebase for fragments of SQL.
14: *
15: * <p>Only the registered name is emitted — never a parameter value, an id, or anything from the
16: * request. The name is already validated as bounded and low-cardinality, and this class
17: * additionally rejects the comment terminator so a name can never close the comment and continue as
18: * SQL.
20:public final class NamedStatementInspector implements StatementInspector {
21:
22:  private static final long serialVersionUID = 1L;
23:
24:  /**
25:   * The comment terminator; a name containing it would end the comment and start statement text.
26:   */
27:  private static final String COMMENT_TERMINATOR = "*/";
28:
29:  @Override
30:  public String inspect(String sql) {
31:    if (sql == null) {
32:      return null;
33:    }
34:    Optional<QueryName> queryName = QueryNameContext.current();
35:    if (queryName.isEmpty()) {
36:      return sql;
37:    }
38:    String value = queryName.get().value();
39:    if (value.contains(COMMENT_TERMINATOR)) {
40:      return sql;
41:    }
42:    return "/* " + value + " */ " + sql;
43:  }
#   이름 형식이 이미 * 와 / 를 막는다
16:  private static final Pattern FORMAT = Pattern.compile("[a-z][a-z0-9.-]{2,95}");

# 하이버네이트가 이 구현을 설치하는 경로 — 저장소 루트, 확장자 무제한
#   statement_inspector 설정 키: 0
#   HibernatePropertiesCustomizer / SessionFactoryBuilder / ServiceRegistry / Integrator: 0
#   persistence.xml: 0
#   src 리소스와 자동설정 등록 파일의 FQCN: 0
#   그 클래스의 단위 테스트: 0

# 이름을 컨텍스트에 넣는 세 곳
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaStreamExecutor.java:63:    return QueryNameContext.with(
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaKeysetQuerySupport.java:49:    return QueryNameContext.with(
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaRepositoryFragmentSupport.java:72:    return QueryNameContext.with(name, work);
#   그 컨텍스트를 읽는 곳: 1

# 이름을 SQL 로 옮기는 두 번째 경로
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/api/query/QueryName.java:8: * <p>The name is what appears in metrics, traces, and the {@code org.hibernate.comment} hint, so it
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaRepositoryFragmentSupport.java:27:  private static final String COMMENT_HINT = "org.hibernate.comment";
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupport.java:24:  private static final String COMMENT_HINT = "org.hibernate.comment";
JpaRepositoryFragmentSupport.java:45:    return entityManager.createQuery(jpql, resultType).setHint(COMMENT_HINT, name.value());
JpaRepositoryFragmentSupport.java:62:    return entityManager.createNativeQuery(sql).setHint(COMMENT_HINT, name.value());
QuerydslJpaSupport.java:48:    return query.offset(page.offset()).limit(page.size()).setHint(COMMENT_HINT, name.value());
#   그 힌트가 SQL 로 나오게 하는 use_sql_comments 를 켜는 곳: 0

# 이름을 넣는 세 클래스를 누가 쓰는가
#   JpaStreamExecutor  그 타입을 쓰는 파일 전부: JpaStreamExecutorTest.java JpaStreamExecutor.java 
#              상속(전체): 
#   JpaKeysetQuerySupport  그 타입을 쓰는 파일 전부: JpaKeysetQuerySupportTest.java JpaKeysetQuerySupport.java 
#              상속(전체): 
#   JpaRepositoryFragmentSupport  그 타입을 쓰는 파일 전부: JpaRepositoryFragmentSupportTest.java JpaRepositoryFragmentSupport.java 
#              상속(전체): JpaRepositoryFragmentSupportTest.java:74:  private static final class Fragment extends JpaRepositoryFragmentSupport { 
