Stage 10: Put data access behind a repository
Learning path / Stage 10 of 37
mvn spring-boot:run. Use the port specified for this stage and stop with Ctrl+C.Download spring-products-file-repository.zip · Complete file-by-file guide
Define a small contract
ProductRepository is our interface. It declares findAll() and findById(int id), without deciding how data is obtained. The interface itself cannot be instantiated with new.
Implement the contract for this file example
FileProductRepository implements ProductRepository and delegates to ProductFileReader. This concrete class supplies the method bodies. @Repository is a specialized component annotation for the data-access role. It does not automatically implement an arbitrary interface.
Inject by interface
ProductService accepts ProductRepository in its constructor. Spring finds the single implementation bean, FileProductRepository, and supplies that object. The field’s declared type is an interface; the actual referenced object is the implementation.
Keep the service independent of file-reading details
The service still applies the stock rule. The controller still supplies Model and view names. Only the data-access dependency has changed. This prepares us to replace file storage in Stage 11.
Be precise about storage
This stage reads the file into memory once at startup. There are no create, update or delete operations and nothing writes back to products.txt. A product repository here means a data-access object; it is not a Git source-code repository.
Run this stage
Use JDK 21+ and Maven 3.9+. Extract spring-products-file-repository.zip and open a terminal in the spring-products-file-repository folder containing pom.xml. Keep the source files in their supplied paths.
mvn spring-boot:runOpen http://localhost:8088/products. Keep the terminal open. Stop with Ctrl+C. If the port is occupied, change server.port in src/main/resources/application.properties, restart and use the new port in the browser.
# Choose another available port if this port is busy.
server.port=8088
Expected output and checks
The pages behave just like Stage 9: 30 total rows, 29 in-stock rows and a 404 for an unknown ID. Follow the constructor types to identify the interface and the object Spring supplies.
Read the changed Java files
The downloadable project includes every file, the 30-product text file, templates, CSS, pom.xml and a standalone README.html with all source files. These are the key changes from the preceding example.
src/main/java/com/edupoly/products/ProductRepository.java
package com.edupoly.products;
import java.util.List;
import java.util.Optional;
public interface ProductRepository {
List<Product> findAll();
Optional<Product> findById(int id);
}
src/main/java/com/edupoly/products/FileProductRepository.java
package com.edupoly.products;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
public class FileProductRepository implements ProductRepository {
private final ProductFileReader reader;
public FileProductRepository(ProductFileReader reader) {
this.reader = reader;
}
@Override
public List<Product> findAll() {
return Arrays.asList(reader.getProducts());
}
@Override
public Optional<Product> findById(int id) {
for (Product product : reader.getProducts()) {
if (product.getId() == id) {
return Optional.of(product);
}
}
return Optional.empty();
}
}
src/main/java/com/edupoly/products/ProductService.java
package com.edupoly.products;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public List<Product> findAll(boolean inStockOnly) {
List<Product> selected = new ArrayList<>();
for (Product product : repository.findAll()) {
if (!inStockOnly || product.getStock() > 0) {
selected.add(product);
}
}
return selected;
}
public Optional<Product> findById(int id) {
return repository.findById(id);
}
}
Practice
Trace a details request through controller, service, interface and implementation. Identify which class would change if the storage format changed.
References: Spring constructor injection, Spring Data MongoDB repositories.
After Stage 10: the Spring words to recognize
| Term | Our concrete example | Its place in Spring |
|---|---|---|
| Repository pattern | ProductRepository | A boundary through which the service obtains stored products. |
| @Repository | FileProductRepository | Marks our concrete data-access class as a Spring component. |
| Interface and implementation | ProductRepository / FileProductRepository | The contract lists operations; the implementation contains their code. |
One small check: Point to the class, constructor or annotation that demonstrates each term before continuing.
Concepts used in this stage
Use the right column for a quick reminder, or open a concept above for its full explanation.