Services call repositories to get or save data. This keeps code organized and easy to manage.
Service calling repository in Spring Boot
Start learning this pattern below
Jump into concepts and practice - no test required
public class ServiceName { private final RepositoryName repository; public ServiceName(RepositoryName repository) { this.repository = repository; } public ReturnType someMethod() { return repository.someRepositoryMethod(); } }
Service classes use constructor injection to get repository instances.
Repository methods handle database operations like find, save, delete.
public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User findUserById(Long id) { return userRepository.findById(id).orElse(null); } }
public class ProductService { private final ProductRepository productRepository; public ProductService(ProductRepository productRepository) { this.productRepository = productRepository; } public List<Product> getAllProducts() { return productRepository.findAll(); } }
This example shows a BookService calling BookRepository to get a book by its ID. The repository handles database access, and the service uses it to get the book data.
import org.springframework.stereotype.Service; import java.util.Optional; @Service public class BookService { private final BookRepository bookRepository; public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } public Book getBookById(Long id) { return bookRepository.findById(id).orElse(null); } } import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface BookRepository extends JpaRepository<Book, Long> { } import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity public class Book { @Id private Long id; private String title; public Book() {} public Book(Long id, String title) { this.id = id; this.title = title; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
Always use constructor injection for better testability and immutability.
Services should not contain database code directly; repositories handle that.
Use Optional from repository methods to handle missing data safely.
Services call repositories to separate business logic from data access.
Use constructor injection to provide repositories to services.
Repositories handle database operations; services use them to get or save data.
Practice
Service class in Spring Boot when it calls a Repository?Solution
Step 1: Understand the role of Service
The Service layer contains business logic and does not directly access the database.Step 2: Understand the role of Repository
The Repository handles data access and database operations.Final Answer:
To handle business logic and use the repository for data access -> Option DQuick Check:
Service handles logic, Repository handles data [OK]
- Thinking Service manages database connections
- Confusing Repository with Service responsibilities
- Assuming Service runs SQL queries directly
Solution
Step 1: Understand dependency injection in Spring Boot
Spring Boot recommends constructor injection with@Autowiredfor better testability and immutability.Step 2: Check options for repository injection
Creating new instances manually or static variables break Spring's management and are not recommended.Final Answer:
Use@Autowiredon a constructor parameter -> Option AQuick Check:
Constructor injection with @Autowired [OK]
- Manually creating repository instances
- Using static variables for repository
- Not using Spring's dependency injection
getUserName(1) return if the repository finds a user with name "Alice"?
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public String getUserName(int id) {
return userRepository.findById(id).map(User::getName).orElse("Unknown");
}
}Solution
Step 1: Understand repository method behavior
findById(id)returns an Optional containing the User if found.Step 2: Analyze service method logic
The method maps the User to its name or returns "Unknown" if no user is found.Final Answer:
"Alice" -> Option CQuick Check:
User found returns name, else "Unknown" [OK]
- Assuming null is returned instead of default
- Expecting exception when user not found
- Confusing Optional usage
@Service
public class ProductService {
private ProductRepository productRepository;
public void saveProduct(Product product) {
productRepository.save(product);
}
}Solution
Step 1: Check repository injection
The repository field is declared but not injected or initialized.Step 2: Understand consequence of missing injection
Callingsaveon a null repository causes NullPointerException at runtime.Final Answer:
The repository is not injected, so it will cause a NullPointerException -> Option BQuick Check:
Missing injection causes null pointer error [OK]
- Forgetting @Autowired or constructor injection
- Assuming repository auto-initializes
- Confusing entity annotation with parameter
List<User> findByActiveTrue(). How should the service method call the repository and return the list?Solution
Step 1: Identify repository method for active users
The repository methodfindByActiveTrue()returns users with active = true.Step 2: Use repository method in service
The service should call this method and return its result directly.Final Answer:
public List<User> getActiveUsers() { return userRepository.findByActiveTrue(); } -> Option AQuick Check:
Call matching repository method for active users [OK]
- Calling findAll() returns all users, not filtered
- Using findByActiveFalse() returns inactive users
- Returning null instead of data
