본문으로 건너뛰기
cccskills
무료GitHub에 공개

java-persistence

Use when a Java service reads or writes the database - JPA/Hibernate and Panache/Spring Data mapping, transaction boundaries, pagination and avoiding N+1 and entity-over-the-wire leaks

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구PostgreSQL
설치 방법 보기

포함된 파일(1)

  • SKILL.md5.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Java Persistence (JPA / Panache / Spring Data)

Overview

Persistence is where Java services most often leak — lazy-loading exceptions, N+1 queries, entities serialized over HTTP, pagination applied in memory. Keep persistence at the repository boundary and mapped deliberately.

Core principle: Entities are a persistence detail. They never cross the HTTP boundary and they never carry business logic that belongs in the domain. Every list query is bounded AND the bound is honored by the SQL, not by Java code truncating a full result set.

Mapping surface

ConcernQuarkus (Panache)Spring Data
Entity@Entity + PanacheEntityBase or plain @Entity@Entity
Repositoryimplements PanacheRepository<Task>extends JpaRepository<Task, UUID>
Queryfind("status", s) / HQLderived methods / @Query
Transaction@Transactional on the service@Transactional on the service
Pagination.page(Page.of(n, size))Pageable / Page<T>
Optimistic lock@Version@Version
Schema management (non-test)quarkus.hibernate-orm.schema-management.strategy=none (or validate; property renamed from database.generation in 3.23)spring.jpa.hibernate.ddl-auto=validate

Rules

  • Transaction boundary = the service method, never the repository or the resource. One use-case = one transaction.
  • Never return entities from a controller/resource. Map to a DTO inside the service (or a JPA projection). Returning entities causes lazy-init serialization errors and leaks the schema.
  • Fetch deliberately to avoid N+1. A loop that touches a lazy collection per row issues a query per row. Use a JOIN FETCH / entity graph / @BatchSize or hibernate.default_batch_fetch_size, and verify with a query-count assertion in a test.
  • Never paginate a collection JOIN FETCH. Hibernate applies LIMIT/OFFSET in memory when the query also fetches a *-to-many collection (warning HHH90003004) — it loads the whole table to paginate it. Set hibernate.query.fail_on_pagination_over_collection_fetch=true in the test profile so this fails loudly instead of shipping slow. Page the root entity's ids first (plain query, no fetch join), then fetch the page by id with the collection join, or rely on @BatchSize/hibernate.default_batch_fetch_size instead of a fetch join at all.
  • Bound every list query. Page (Page/Pageable, Panache .page(...)) — never findAll()/listAll() on a growing table. Default and maximum page size live with the endpoint (api-design-conventions), not just the repository call.
  • Migrations own the schema, not hibernate.hbm2ddl=update/Quarkus generate-schema. Set validate/none in non-test envs; schema changes go through a migration (postgres-migrations), never auto-DDL in production.
  • Optimistic locking (@Version) on entities with concurrent updates; map OptimisticLockException/ObjectOptimisticLockingFailureException to 409 in the central error mapper (api-design-conventions), never let it surface as 500.
  • spring.jpa.open-in-view=false (Spring) — Open Session In View hides N+1s in the view layer and that lazy access outside a clear transaction boundary is exactly the mistake this skill exists to prevent.

Worked Example — killing an N+1 without breaking pagination

// ❌ N+1: one query for projects, then one per project for its tasks
List<Project> projects = repo.findAll(pageable).getContent();
projects.forEach(p -> total += p.getTasks().size());   // lazy hit per row

// ❌ "fixed" with a fetch join but now unbounded: Hibernate pages in memory
@Query("select p from Project p left join fetch p.tasks")
Page<Project> findAllWithTasks(Pageable pageable);   // HHH90003004

// ✅ page the roots first (no collection fetch), then fetch by id with the join
Page<UUID> ids = repo.findIds(pageable);
List<Project> page = repo.findByIdInWithTasks(ids.getContent());

Add a query-count assertion (Hibernate Statistics, see java-testing-junit-mockito) so the N+1 can't creep back in, and a test on the test profile with hibernate.query.fail_on_pagination_over_collection_fetch=true so an accidental collection-fetch-plus-page regresses loudly instead of just getting slow.

Common Mistakes

  • @Transactional on the repository or resource instead of the service.
  • Serializing entities in the HTTP response.
  • findAll()/listAll() with no paging.
  • Paginating a query that also does a collection JOIN FETCH.
  • hbm2ddl=update / Quarkus schema generation in a real environment instead of migrations.
  • Testing on H2 where the SQL dialect differs from Postgres (java-testing-junit-mockito: use Testcontainers).
  • Letting OptimisticLockException surface as an unmapped 500.

Red Flags

  • LazyInitializationException in an HTTP response → you serialized an entity outside the transaction.
  • Query count scales with row count → N+1.
  • HHH90003004 in the logs, or a pagination query that also lists fetch in its JPQL.
  • Schema changes with no migration file in the diff.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

makifbaysal의 스킬 모두 보기

이 스킬의 문제 신고