{ "assetKey": "a05-f014-collection-fetch-pagination-probe", "kind": "terminal", "command": "mkdir -p /tmp/probe\ncat > /tmp/probe/cp.sh <<'CPSH'\n# 락파일의 한 구성에 실린 좌표 전부를 캐시 경로로 바꾼다. Gradle 실행 없이 재현된다.\nLOCK=${1:-adapter/outbound/persistence-jpa/gradle.lockfile}\nCONF=${2:-postgresqlIntegrationTestRuntimeClasspath}\nCACHE=/root/.gradle/caches/modules-2/files-2.1\ncp=\"\"\nwhile IFS= read -r line; do\n coord=${line%%=*}; confs=${line#*=}\n case \",$confs,\" in *\",$CONF,\"*) ;; *) continue ;; esac\n group=${coord%%:*}; rest=${coord#*:}; name=${rest%%:*}; ver=${rest##*:}\n jar=$(find \"$CACHE/$group/$name/$ver\" -name '*.jar' ! -name '*sources*' ! -name '*javadoc*' 2>/dev/null | head -1)\n [ -n \"$jar\" ] && cp=\"$cp:$jar\"\ndone < <(grep -E '^[a-zA-Z0-9._-]+:[^=]+=' \"$LOCK\")\necho \"${cp#:}\"\nCPSH\ncd src\nCP=$(bash /tmp/probe/cp.sh)\necho '# 락파일이 고정한 제공자'\necho \"$CP\" | tr : '\\n' | grep -E 'hibernate-core|/h2-' | sed 's|.*/||'\necho -n '# 저장소에서 fail_on_pagination_over_collection_fetch 를 켜는 곳: '\ngrep -rn 'fail_on_pagination' --include=*.java --include=*.yml --include=*.yaml --include=*.properties --include=*.gradle --exclude-dir=build .. | wc -l\n\necho\necho '# 게이트와 같은 모양의 조회를 그 제공자에게 직접 시킨다'\ncat > /tmp/probe/FetchPageProbe.java <<'JAVA'\nimport jakarta.persistence.CascadeType;\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.GenerationType;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.ManyToOne;\nimport jakarta.persistence.OneToMany;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.cfg.Configuration;\n\n/** 컬렉션 페치에 페이지 상한을 걸었을 때 상한이 SQL 로 가는지 본다. */\npublic class FetchPageProbe {\n\n @Entity(name = \"PagedParent\")\n public static class Parent {\n @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) Long id;\n String label;\n @OneToMany(mappedBy = \"parent\", cascade = CascadeType.ALL) List children = new ArrayList<>();\n }\n\n @Entity(name = \"PagedChild\")\n public static class Child {\n @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) Long id;\n String label;\n @ManyToOne Parent parent;\n }\n\n public static void main(String[] args) {\n Configuration cfg = new Configuration();\n cfg.setProperty(\"hibernate.connection.driver_class\", \"org.h2.Driver\");\n cfg.setProperty(\"hibernate.connection.url\", \"jdbc:h2:mem:fetch;DB_CLOSE_DELAY=-1\");\n cfg.setProperty(\"hibernate.connection.username\", \"sa\");\n cfg.setProperty(\"hibernate.hbm2ddl.auto\", \"create-drop\");\n cfg.setProperty(\"hibernate.generate_statistics\", \"true\");\n cfg.addAnnotatedClass(Parent.class);\n cfg.addAnnotatedClass(Child.class);\n\n try (SessionFactory factory = cfg.buildSessionFactory()) {\n factory.inTransaction(session -> {\n for (int p = 0; p < 50; p++) {\n Parent parent = new Parent();\n parent.label = \"parent-\" + p;\n for (int c = 0; c < 4; c++) {\n Child child = new Child();\n child.label = \"child-\" + p + '-' + c;\n child.parent = parent;\n parent.children.add(child);\n }\n session.persist(parent);\n }\n });\n\n long before = factory.getStatistics().getPrepareStatementCount();\n List page = factory.fromTransaction(session ->\n session.createQuery(\n \"select distinct p from PagedParent p left join fetch p.children order by p.id\",\n Parent.class)\n .setMaxResults(20)\n .getResultList());\n long statements = factory.getStatistics().getPrepareStatementCount() - before;\n\n System.out.println(\"반환된 부모 수 : \" + page.size());\n System.out.println(\"그 조회가 낸 프리페어드 스테이트먼트 : \" + statements);\n System.out.println(\"fail_on_pagination_over_collection_fetch : \"\n + factory.getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled());\n }\n }\n}\nJAVA\njavac -encoding UTF-8 -nowarn -cp \"$CP\" -d /tmp/probe /tmp/probe/FetchPageProbe.java 2>&1 | grep -v '^Note:'\njava -Dstdout.encoding=UTF-8 -cp \"$CP:/tmp/probe\" FetchPageProbe 2>&1 \\\n | grep -E 'HHH90003004|반환된|스테이트먼트|fail_on' | sed 's/^[0-9:.]* \\[main\\] //'\n\necho\necho '# 서버 없이 매 빌드마다 도는 레인에도 같은 형태가 있다'\nsed -n '11,19p' adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/testkit/fetch/FetchPaginationExpectationTest.java", "cwd": "/shared/codebase/clean-architecture-backend-template", "exitCode": 0, "executedAt": "2026-09-02T04:06:14+00:00", "sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916", "raw": "evidence/raw/a05-f014-collection-fetch-pagination-probe.txt", "svg": "evidence/rendered/a05-f014-collection-fetch-pagination-probe.svg", "rawSha256": "91a21e235440739eb729b0da52a4bac4a742c691b207551b59f36ec425a0a517", "lines": 21, "redaction": "none — 코드베이스 정적 검색" }