PrepZone Logo
PrepZone

Entities, Repositories, and Queries

JPA entities, Spring Data repositories, and derived query methods.

Why this matters

  • JPA is the default persistence layer for BookStore — understanding entities and repositories is prerequisite for transactions, relationships, and migrations.
  • Derived query methods eliminate boilerplate SQL for common lookups.
  • Hibernate manages the object-relational mapping so you work with Java objects, not JDBC rows.

Entity mapping

Java
@Entity
@Table(name = "books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 255)
    private String title;

    @Column(nullable = false, unique = true, length = 13)
    private String isbn;

    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal price;

    @Column(nullable = false)
    private String genre;

    @Column(name = "created_at", updatable = false)
    private Instant createdAt;

    @PrePersist
    void onCreate() {
        this.createdAt = Instant.now();
    }

    protected Book() {}

    public Book(String title, String isbn, BigDecimal price, String genre) {
        this.title = title;
        this.isbn = isbn;
        this.price = price;
        this.genre = genre;
    }

    // getters and setters
}

Essential JPA annotations

  • @Entity — Marks a class as a persistent domain object.
  • @Table — Maps to a specific database table name.
  • @Id + @GeneratedValue — Primary key with auto-increment strategy.
  • @Column — Column constraints: nullable, length, unique, precision.
  • @PrePersist / @PreUpdate — Lifecycle callbacks before insert/update.
Author1
BookN
Order1
Author has many Books. Order has many OrderItems referencing Books.

Repository interface

Java
public interface BookRepository extends JpaRepository<Book, Long> {
    List<Book> findByGenre(String genre);
    Optional<Book> findByIsbn(String isbn);
    List<Book> findByTitleContainingIgnoreCase(String keyword);
    List<Book> findByPriceBetween(BigDecimal min, BigDecimal max);
    boolean existsByIsbn(String isbn);
    long countByGenre(String genre);
}

Spring Data parses method names into queries:

Method name → SQL mapping

  • findByGenre → WHERE genre = ?
  • findByTitleContainingIgnoreCase → WHERE LOWER(title) LIKE LOWER(?%
  • findByPriceBetween → WHERE price BETWEEN ? AND ?

Custom JPQL queries

When derived methods are not expressive enough:

Java
public interface BookRepository extends JpaRepository<Book, Long> {

    @Query("SELECT b FROM Book b WHERE b.genre = :genre ORDER BY b.price DESC")
    List<Book> findTopPricedByGenre(@Param("genre") String genre, Pageable pageable);

    @Query("SELECT b FROM Book b JOIN b.author a WHERE a.name = :authorName")
    List<Book> findByAuthorName(@Param("authorName") String authorName);
}

JPQL operates on entity names and fields, not table/column names.

Pagination

Java
@GetMapping
public Page<BookSummary> list(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size) {
    Pageable pageable = PageRequest.of(page, size, Sort.by("title"));
    return bookService.findAll(pageable)
        .map(BookSummary::from);
}
Java
public Page<Book> findAll(Pageable pageable) {
    return repository.findAll(pageable);
}

Page includes total count, page number, and content — no manual LIMIT/OFFSET.

Configuration

Java
spring:
  datasource:
    url: jdbc:postgresql://127.0.0.1:5432/bookstore
    username: bookstore
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.PostgreSQLDialect

ddl-auto: validate checks the schema matches entities without modifying it — pair with Flyway for schema management.

Auditing

Java
@EntityListeners(AuditingEntityListener.class)
public class Book {
    @CreatedDate
    @Column(updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    private Instant updatedAt;
}

Enable with @EnableJpaAuditing on a configuration class.

Quick recall

Everything you need if you only revisit this box.

  • @Entity classes map to database tables; @Column controls column constraints.
  • JpaRepository<Entity, Id> provides save, findById, findAll, deleteById.
  • Derived query methods generate SQL from method naming conventions.
  • @Query writes custom JPQL when derived methods are insufficient.
  • Pageable enables paginated queries with sort and page size.
  • Use ddl-auto: validate with Flyway — never update or create-drop in production.

Test yourself

Answer these before moving on — recall is what makes it stick.