Skip to content

Latest commit

 

History

History
73 lines (62 loc) · 4.07 KB

File metadata and controls

73 lines (62 loc) · 4.07 KB

AGENTS.md — examples/database/

Read the root and examples/AGENTS.md first. Reference module for JPA, transactions, raw JDBC, Flyway migrations, and HikariCP.

Layout

entity/       Order, OrderItem (JPA entities), OrderStatus (enum)
repository/   OrderRepository — Spring Data JPA (derived, JPQL, native, @Modifying)
service/      OrderService — the transaction-boundary layer
jdbc/         JdbcOrderDao — JdbcTemplate/NamedParameterJdbcTemplate/SimpleJdbcInsert
config/       DataSourceConfig — programmatic HikariCP (@Primary, overrides autoconfig)
resources/db/migration/  V1__create_orders.sql (Flyway)

Patterns this module encodes — follow them

  • Entities: @Version optimistic locking; @PrePersist/@PreUpdate timestamps; bidirectional @OneToMany(cascade = ALL, orphanRemoval = true) maintained through helper methods (addItem/removeItem keep both sides in sync and call recalculateTotal()); no public setters for managed fields (id, version, timestamps, total); @Enumerated(EnumType.STRING) for enums. Entities are mutable (JPA needs it) — that is the only place mutability is the default.
  • Transactions: class-level @Transactional(readOnly = true) on the service; write methods override with @Transactional(rollbackFor = Exception.class) and explicit propagation/isolation where it matters. Transactions live in the service layer, never in controllers or repositories.
  • Queries: derived methods for simple cases; @Query JPQL with @Param for joins; LEFT JOIN FETCH + Optional<Order> return for eager loading (never return null from a repository); native SQL only when JPQL can't express it; @Modifying for bulk updates.
  • JDBC: JdbcOrderDao shows positional params, MapSqlParameterSource, SimpleJdbcInsert.executeAndReturnKey, batchUpdate, and an immutable record OrderSummary projection with a static RowMapper constant.
  • Migrations: Flyway, V{n}__snake_case_description.sql, ddl-auto: validate (Hibernate validates, Flyway owns the schema). Write portable, SQL-standard DDL (GENERATED BY DEFAULT AS IDENTITY, not AUTO_INCREMENT/BIGSERIAL) so the same migration runs on H2 and PostgreSQL. Never edit an applied migration — add a new versioned file.

Configuration profiles

  • Default: H2 in-memory (jdbc:h2:mem:templatedb), console at /h2-console, Flyway runs V1__create_orders.sql at startup. No external services needed.
  • production profile: PostgreSQL on localhost:5432/templatedb; requires DB_USERNAME/DB_PASSWORD env vars. Migrations are portable DDL and shared with the H2 path; if a vendor-specific migration ever becomes necessary, add it under db/migration/postgresql and append that location in the production profile's spring.flyway.locations (see the comment in application.yml).

Commands

./mvnw -pl examples/database spring-boot:run    # H2, Flyway migrates on boot
./mvnw -pl examples/database test               # 39 tests: entities, service, @DataJpaTest, @JdbcTest

Gotchas

  • Open Session in View is disabled (open-in-view: false). Touching a lazy association (Order.items, OrderItem.order) outside a transaction throws LazyInitializationException. Use findByIdWithItems (LEFT JOIN FETCH, returns Optional<Order>) or stay inside a @Transactional method.
  • DataSourceConfig is @Primary and replaces Boot's auto-configured pool; the spring.datasource.hikari.* yml block binds to it via @ConfigurationProperties.
  • Tests (39) run on embedded H2 with the real Flyway migration — no Docker needed: OrderTest/OrderItemTest/OrderStatusTest (entities), OrderServiceTest (Mockito), OrderRepositoryTest (@DataJpaTest), JdbcOrderDaoTest (@JdbcTest + @Import). Extend the matching class when you touch a layer; for real-Postgres coverage add a Testcontainers *IT modeled on examples/testing/PostgresContainerIT.
  • Service methods throw plain IllegalArgumentException/IllegalStateException (no web layer here); wrap in domain exceptions if you expose them over HTTP.