# 검사기가 무엇을 하는지, 그리고 무엇을 위한 것인지 7:/** 8: * Prefixes each statement with the registered query name that issued it (design §37). 9: * 10: *

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: *

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 = 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: *

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 {