Chapter 5: Introduction to Spring Data JPA Repositories
Introduction
Now that you have mastered the basics of Spring Boot (structure, configuration, annotations), it is time to learn how to persist data in a database. Spring Data JPA greatly simplifies this task by allowing you to interact with the database without writing SQL code.
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand the role of a Spring Data JPA Repository
- Use the 4 basic methods for CRUD
- Create simple search methods with naming conventions
- Understand how Spring automatically generates queries
What is a Repository?
Definition
A Repository is an interface that allows you to interact with the database without writing SQL code. Spring Data JPA automatically generates the implementation.
Analogy: A repository is like a librarian who manages your books (data) for you.
Complete Architecture
Responsibilities:
| Layer | Role | Example |
|---|---|---|
| Controller | Handles HTTP requests | @RestController |
| Service | Business logic | UserService |
| Repository | Data access | UserRepository |
| Database | Storage | PostgreSQL, H2 |
Create a Simple Entity
Example: User Entity
Before creating a repository, we need an entity - a Java class that represents a table in the database.
package com.example.demo.model;
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;
// Constructors
public User() {
}
public User(String name, String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Essential Annotations
| Annotation | Role | Example |
|---|---|---|
@Entity | Marks the class as a JPA entity | Creates a table |
@Table | Specifies the table name | name = "users" |
@Id | Identifies the primary key | Field id |
@GeneratedValue | Automatic ID generation | Auto-increment |
The 4 Basic Methods
JpaRepository Interface
Spring Data JPA provides the JpaRepository interface which already contains all the basic CRUD methods.
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// No need to write code!
// Methods are inherited automatically
}
Explanation:
JpaRepository<User, Long>:User: Entity typeLong: ID type
Automatically Inherited Methods
Here are the 4 essential methods you can use immediately:
1. findById() - Find by ID
Signature:
Optional<User> findById(Long id)
Usage:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
Generated SQL:
SELECT * FROM users WHERE id = ?
2. findAll() - Find all
Signature:
List<User> findAll()
Usage:
public List<User> getAllUsers() {
return userRepository.findAll();
}
Generated SQL:
SELECT * FROM users
3. save() - Create or Update
Signature:
User save(User user)
Usage (Creation):
public User createUser(User user) {
return userRepository.save(user);
}
Generated SQL (if ID is null):
INSERT INTO users (name, email, age) VALUES (?, ?, ?)
Generated SQL (if ID exists):
UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?
4. deleteById() - Delete by ID
Signature:
void deleteById(Long id)
Usage:
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
Generated SQL:
DELETE FROM users WHERE id = ?
Summary Table
| Method | Action | Parameter | Return | SQL |
|---|---|---|---|---|
findById(id) | Read one | ID | Optional<User> | SELECT ... WHERE id = ? |
findAll() | Read all | None | List<User> | SELECT * |
save(user) | Create/Update | Entity | User | INSERT or UPDATE |
deleteById(id) | Delete | ID | void | DELETE ... WHERE id = ? |
Naming Conventions: The 3 Basics
General Principle
Spring Data JPA can automatically generate search methods based on the method name.
Format:
findBy[Attribute]
1. findBy (Simple Search)
Syntax:
findBy + AttributeName
Examples:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Search by name
User findByName(String name);
// Search by email
User findByEmail(String email);
// Search by age
List<User> findByAge(int age);
}
Generated SQL:
| Method | SQL |
|---|---|
findByName("John") | SELECT * FROM users WHERE name = 'John' |
findByEmail("john@example.com") | SELECT * FROM users WHERE email = 'john@example.com' |
findByAge(25) | SELECT * FROM users WHERE age = 25 |
2. And (Combine two criteria)
Syntax:
findBy + Attribute1 + And + Attribute2
Examples:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Search by name AND age
List<User> findByNameAndAge(String name, int age);
// Search by email AND age
User findByEmailAndAge(String email, int age);
}
Generated SQL:
| Method | SQL |
|---|---|
findByNameAndAge("John", 25) | SELECT * FROM users WHERE name = 'John' AND age = 25 |
findByEmailAndAge("john@example.com", 25) | SELECT * FROM users WHERE email = 'john@example.com' AND age = 25 |
3. Or (Alternative criterion)
Syntax:
findBy + Attribute1 + Or + Attribute2
Examples:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Search by name OR email
List<User> findByNameOrEmail(String name, String email);
// Search by age 20 OR 30
List<User> findByAgeOrAge(int age1, int age2);
}
Generated SQL:
| Method | SQL |
|---|---|
findByNameOrEmail("John", "john@example.com") | SELECT * FROM users WHERE name = 'John' OR email = 'john@example.com' |
Complete Example: 3-Layer Architecture
1. Entity (Model)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;
// Constructors, Getters, Setters
}
2. Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Automatically inherited methods:
// - findById(Long id)
// - findAll()
// - save(User user)
// - deleteById(Long id)
// Custom methods:
User findByEmail(String email);
List<User> findByAge(int age);
List<User> findByNameAndAge(String name, int age);
}
3. Service
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// CREATE
public User createUser(User user) {
return userRepository.save(user);
}
// READ - all
public List<User> getAllUsers() {
return userRepository.findAll();
}
// READ - by ID
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with id: " + id));
}
// READ - by email
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
// READ - by age
public List<User> getUsersByAge(int age) {
return userRepository.findByAge(age);
}
// UPDATE
public User updateUser(Long id, User userDetails) {
User user = getUserById(id);
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
user.setAge(userDetails.getAge());
return userRepository.save(user);
}
// DELETE
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
4. Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
// GET /api/users/123
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
// GET /api/users/email/john@example.com
@GetMapping("/email/{email}")
public User getUserByEmail(@PathVariable String email) {
return userService.getUserByEmail(email);
}
// GET /api/users/age/25
@GetMapping("/age/{age}")
public List<User> getUsersByAge(@PathVariable int age) {
return userService.getUsersByAge(age);
}
// PUT /api/users/123
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}
// DELETE /api/users/123
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
H2 Configuration for Testing
application.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
Access to H2 console:
http://localhost:8080/h2-console
Practical Exercises
Exercise 1: Basic Repository
Objective: Create a simple repository
Instructions:
- Create a
Productentity:Long idString nameDouble priceint quantity
- Create
ProductRepositoryextendingJpaRepository - Test the 4 basic methods
Questions:
- Which method to use to create a product?
- How to retrieve all products?
- How to delete a product by its ID?
Exercise 2: Search Methods
Objective: Use naming conventions
Instructions:
- In
ProductRepository, add:- Search by name
- Search by price
- Search by name AND price
- Create a
ProductService - Test in a controller
Questions:
- What SQL is generated for
findByName? - How to search for products with a given price?
Exercise 3: Complete Service
Objective: Create a complete architecture
Instructions:
- Create a
Studententity:Long idString firstNameString lastNameString emailint grade
- Create
StudentRepositorywith:- Search by email
- Search by grade
- Search by firstName and lastName
- Create
StudentServicewith all CRUD operations - Create
StudentControllerwith all endpoints
Review Questions
-
What is a Repository in Spring Data JPA?
-
What are the 4 methods automatically inherited from JpaRepository?
-
What is the difference between
save()for creation and for update? -
How does Spring know which SQL query to generate for
findByName? -
What is the difference between
AndandOrin method names? -
Why do we use
Optional<User>forfindById()? -
What happens if you call
findById()with a non-existent ID? -
Can you combine more than 2 criteria with
And? Provide an example.
Chapter Summary
Key Points
- A Repository is an interface for data access
JpaRepositoryprovides 4 basic methods:findById,findAll,save,deleteById- Naming conventions allow automatic method creation
- 3 essential keywords:
findBy,And,Or - Spring automatically generates SQL based on the method name
- Architecture: Controller → Service → Repository → Database
What You Now Know
✅ Create a JPA entity
✅ Create a Spring Data JPA repository
✅ Use the 4 basic CRUD methods
✅ Create simple search methods (findBy, And, Or)
✅ Build a complete 3-layer architecture
What You Will Learn Later (Module 6)
🔜 Advanced naming conventions (15+ keywords)
🔜 Joins between entities
🔜 Custom JPQL queries
🔜 Performance and optimization
🔜 Transactions
Best Practices
DO
✅ Use constructor injection for repositories
✅ Name methods according to Spring Data JPA conventions
✅ Handle cases where findById() finds nothing with Optional
✅ Separate business logic (Service) from data access (Repository)
DON'T
❌ Put business logic in repositories
❌ Call the repository directly from the controller
❌ Ignore the Optional returned by findById()
❌ Create method names that do not follow conventions
Next Steps
Practice
Now that you know the basics of repositories, it's time to practice!
Proceed to Module 2 Labs:
- Lab 2.2: H2 Database - Use H2 and the 4 basic methods
To go further (next modules):
- Module 3: Lab 3.1: CRUD Architecture - Complete CRUD application with validation
- Module 4: Lab 4.1: PostgreSQL - Migration to PostgreSQL in production
To Go Further
Interested in more advanced queries? Check out:
Module 6: Advanced Spring Data JPA (coming soon)
- 15+ search keywords
- Joins between tables
- JPQL and custom queries
- Performance optimization