Spring Data JPA Repositories - CRUD Without Boilerplate
Spring Data JPA Repositories - CRUD Without Boilerplate
Writing CRUD operations for every entity is repetitive work. Spring Data JPA solves this with the Repository abstraction. You define an interface, extend a base type, and Spring automatically provides the implementation - no code, no configuration, no boilerplate.
The Repository Hierarchy
Spring Data offers several repository interfaces. Each level adds more methods on top of the previous one. Most applications only need JpaRepository, which combines CrudRepository and PagingAndSortingRepository:
+------------------------------------------+
| Repository |
+------------------------------------------+
| CrudRepository |
| (save, findById, findAll, delete, etc.) |
+------------------------------------------+
| PagingAndSortingRepository |
| (findAll with Sort and Pageable) |
+------------------------------------------+
| JpaRepository |
| (flush, saveAndFlush, deleteInBatch) |
+------------------------------------------+
You extend JpaRepository<T, ID> where T is your entity type and ID is the type of its primary key. Spring generates the implementation at runtime using a JDK proxy.
Defining a Repository
Creating a repository is as simple as writing an interface. Spring scans for these at startup and creates the concrete implementation automatically:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
// That is it - Spring provides findAll, findById, save, delete, etc.
}
The @Repository annotation makes the interface a Spring-managed bean and adds exception translation. The methods findAll(), findById(Long id), save(Book book), deleteById(Long id), and count() all work out of the box.
Using the Repository
In a service class, inject the repository and call its methods. Spring handles the transaction, the EntityManager, and the SQL generation:
@Service
@Transactional
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public Book createBook(String title, String author) {
Book book = new Book(title, author);
return bookRepository.save(book);
}
public Book findById(Long id) {
return bookRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Book not found"));
}
public List<Book> getAllBooks() {
return bookRepository.findAll();
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
No JDBC code, no EntityManager calls, no SQL strings. The repository methods map directly to the underlying JPA operations. Pagination is also built in - pass a Pageable to findAll() and you get a Page<T> with total count and page data.
Real-World Scenario
A content management system manages thousands of articles, authors, and categories. Each entity gets its own repository interface. The entire CRUD layer is defined as a set of five-line interfaces while the actual persistence logic is handled by Spring Data at runtime. Developers spend their time on business logic, not data access plumbing.
Key Points
JpaRepository<T, ID>provides full CRUD plus pagination with zero boilerplate.- Spring generates the implementation at runtime using a JDK dynamic proxy.
- The
@Repositoryannotation enables exception translation and Spring management. - Pagination is built in - pass
Pageableto getPage<T>results. - You still have full control by injecting
EntityManagerwhen custom queries are needed.