HIGH 80%+: new @Entity OR new repository OR new query OR schema migration
MEDIUM 40-79%: existing repo modification (e.g., add finder method) OR connection pool config
LOW 1-39%: service that uses repository, no DB code change
ZERO: project has no JPA / R2DBC (verify build.gradle)
Database Patterns
Stack Decision
Criterion
JPA / HikariCP
R2DBC / R2DBC Pool
Runtime
Spring MVC (blocking)
Spring WebFlux (reactive)
Repository
JpaRepository
ReactiveCrudRepository
Transaction
@Transactional
@Transactional + TransactionalOperator
PG driver
org.postgresql:postgresql
org.postgresql:r2dbc-postgresql
MySQL driver
com.mysql.cj.jdbc.Driver
io.asyncer:r2dbc-mysql
Engine Differences
Aspect
PostgreSQL
MySQL
PK strategy
GENERATED ALWAYS AS IDENTITY / SEQUENCE
BIGINT UNSIGNED AUTO_INCREMENT
JPA @GeneratedValue
SEQUENCE + @SequenceGenerator
IDENTITY
Strings
text (no storage diff vs varchar)
VARCHAR(n) + utf8mb4 required
Timestamps
timestamptz (always TZ-aware)
DATETIME(3) (store in UTC)
Covering index
INCLUDE (col)
List all cols in key (no INCLUDE)
Partial index
WHERE clause on index
Not supported (use composite)
Concurrent index
CREATE INDEX CONCURRENTLY
ALGORITHM=INPLACE, LOCK=NONE
Transactional DDL
Yes
No (implicit commit)
Default isolation
READ_COMMITTED
REPEATABLE_READ (gap locks)
Critical Rules (Both Engines)
@SQLRestriction not @Where — Hibernate 6+
FetchType.LAZY always — JOIN FETCH / @EntityGraph at call site
No .block() in reactive — use R2DBC on WebFlux
open-in-view: false — always disable
Parameterized queries only — never concatenate user input
DTO projections for read-only — skip entity lifecycle overhead
Migration Safety (Expand-Contract)
Expand — add nullable column; code writes both old+new
Migrate — backfill in batches (1000 rows), not single UPDATE
Contract — add NOT NULL / drop old column in separate deploy
Always safe: add nullable column, index (CONCURRENTLY/INPLACE), add table.
Never in prod: DROP without expand-contract, ALTER to smaller type, TRUNCATE.
Verification Checklist
No @Data on entities; use @Getter + @NoArgsConstructor(PROTECTED) + builder
@EntityGraph / JOIN FETCH for known N+1 paths
DTO projections for read-only queries
@Transactional(readOnly = true) on query methods
HikariCP leak detection enabled in non-prod
Migrations follow V{n}__{description}.sql naming
Large table changes use online DDL or batch strategy