Spring Data JPA repositories

Spring Boot · Essentials · 15 min

Most Spring Boot services persist data in a relational database through Spring Data JPA.

Add spring-boot-starter-data-jpa and a JDBC driver (PostgreSQL, MySQL, or H2 for experiments). Boot configures a connection pool (HikariCP), Hibernate as the JPA provider and transaction management from the spring.datasource.* properties.

An entity is a class annotated @Entity with an @Id (often @GeneratedValue). Fields map to columns; @Column, @Table and relationship annotations (@ManyToOne, @OneToMany) customise the mapping. Entities need a no-argument constructor (it can be protected), and they are mutable classes, not records.

A repository is just an interface: interface BookRepository extends JpaRepository<Book, Long> {}. Spring Data generates the implementation at startup with save, findById (returns Optional), findAll, deleteById, count, paging and sorting.

Derived queries come from method names: findByAuthor(String a), findByTitleContainingIgnoreCase(String s), countByPublishedAfter(LocalDate d), findTop5ByOrderByPublishedDesc(). A typo in the name fails at startup, not at runtime. For anything complex, write JPQL with @Query("select b from Book b where ...").

Schema management: spring.jpa.hibernate.ddl-auto defaults to create-drop for embedded databases only. For real databases manage the schema with Flyway or Liquibase migrations, and keep ddl-auto=validate or none.

Example

@Entity
class Book {
    @Id @GeneratedValue Long id;
    String title;
    String author;
    LocalDate published;
    protected Book() {}                            // required by JPA
    Book(String title, String author, LocalDate published) {
        this.title = title; this.author = author; this.published = published;
    }
}
interface BookRepository extends JpaRepository<Book, Long> {
    List<Book> findByAuthorIgnoreCase(String author);
    List<Book> findByTitleContainingIgnoreCase(String fragment);
    long countByPublishedAfter(LocalDate date);
    @Query("select b from Book b where b.published < :date order by b.published")
    List<Book> classicsBefore(@Param("date") LocalDate date);
}
@Service
class Catalog {
    private final BookRepository repo;
    Catalog(BookRepository repo) { this.repo = repo; }
    Book add(String t, String a, LocalDate d) { return repo.save(new Book(t, a, d)); }
    Optional<Book> find(long id) { return repo.findById(id); }
}

Remember

  • @Entity + @Id maps a class to a table
  • Extend JpaRepository<T, ID> and get CRUD, paging and sorting for free
  • Derived query methods are parsed from names and checked at startup
  • Use Flyway or Liquibase for schema changes, not ddl-auto

Watch out for

  • Using spring.jpa.hibernate.ddl-auto=update in production, which can't handle renames and drifts silently.

Check yourself

  1. What does Spring Data do with a method List<Book> findByAuthor(String author) in a repository interface?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html