Skip to main content

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:

LayerRoleExample
ControllerHandles HTTP requests@RestController
ServiceBusiness logicUserService
RepositoryData accessUserRepository
DatabaseStoragePostgreSQL, 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​

AnnotationRoleExample
@EntityMarks the class as a JPA entityCreates a table
@TableSpecifies the table namename = "users"
@IdIdentifies the primary keyField id
@GeneratedValueAutomatic ID generationAuto-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 type
    • Long: 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​

MethodActionParameterReturnSQL
findById(id)Read oneIDOptional<User>SELECT ... WHERE id = ?
findAll()Read allNoneList<User>SELECT *
save(user)Create/UpdateEntityUserINSERT or UPDATE
deleteById(id)DeleteIDvoidDELETE ... 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]

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:

MethodSQL
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:

MethodSQL
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:

MethodSQL
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:

  1. Create a Product entity:
    • Long id
    • String name
    • Double price
    • int quantity
  2. Create ProductRepository extending JpaRepository
  3. 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:

  1. In ProductRepository, add:
    • Search by name
    • Search by price
    • Search by name AND price
  2. Create a ProductService
  3. 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:

  1. Create a Student entity:
    • Long id
    • String firstName
    • String lastName
    • String email
    • int grade
  2. Create StudentRepository with:
    • Search by email
    • Search by grade
    • Search by firstName and lastName
  3. Create StudentService with all CRUD operations
  4. Create StudentController with all endpoints

Review Questions​

  1. What is a Repository in Spring Data JPA?

  2. What are the 4 methods automatically inherited from JpaRepository?

  3. What is the difference between save() for creation and for update?

  4. How does Spring know which SQL query to generate for findByName?

  5. What is the difference between And and Or in method names?

  6. Why do we use Optional<User> for findById()?

  7. What happens if you call findById() with a non-existent ID?

  8. Can you combine more than 2 criteria with And? Provide an example.


Chapter Summary​

Key Points​

  • A Repository is an interface for data access
  • JpaRepository provides 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:

To go further (next modules):

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

Back to Module 2