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
@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
Repository interface
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:
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
@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);
}
public Page<Book> findAll(Pageable pageable) {
return repository.findAll(pageable);
}
Page includes total count, page number, and content — no manual LIMIT/OFFSET.
Configuration
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
@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.
@Entityclasses map to database tables;@Columncontrols column constraints.JpaRepository<Entity, Id>provides save, findById, findAll, deleteById.- Derived query methods generate SQL from method naming conventions.
@Querywrites custom JPQL when derived methods are insufficient.Pageableenables paginated queries with sort and page size.- Use
ddl-auto: validatewith Flyway — neverupdateorcreate-dropin production.
Test yourself
Answer these before moving on — recall is what makes it stick.