Spring Boot
Capstone – Article 3: User Repository & Basic CRUD (Expense Tracker)
By Utility Zone · 2026-01-27T18:31:33.590865
1. Introduction
Now that the database and User entity are ready, it’s time to actually work with data.
In this article, you will:
- Create a JPA repository
- Perform basic CRUD operations
- Verify data persistence via H2 console
- Follow proper layering (Controller → Service → Repository)
This is the backbone of every backend application.
2. Why Repository Layer?
The repository layer:
- Handles all database operations
- Hides JPA/Hibernate complexity
- Keeps service and controller clean
Spring Data JPA generates implementations automatically.
3. Creating UserRepository
3.1 Create Repository Package
Create package:
com.example.expensetracker.repository
3.2 Create UserRepository Interface
package com.example.expensetracker.repository;
import com.example.expensetracker.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
Why findByEmail?
- Email is unique
- Used for login later
- Common real-world pattern
4. Creating UserService
4.1 Create Service Package
Create package:
com.example.expensetracker.service
4.2 Create UserService Class
package com.example.expensetracker.service;
import com.example.expensetracker.entity.User;
import com.example.expensetracker.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
return userRepository.save(user);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
Note:
- No controller logic here
- Pure business/data logic
5. Creating UserController
5.1 Create Controller Package
Create package:
com.example.expensetracker.controller
5.2 Create UserController
package com.example.expensetracker.controller;
import com.example.expensetracker.entity.User;
import com.example.expensetracker.service.UserService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
6. Testing CRUD Operations
6.1 Create User (POST)
Endpoint:
POST /users
Body:
{
"name": "Bharat",
"email": "bharat@email.com",
"password": "test123",
"role": "USER"
}
6.2 Get All Users (GET)
GET /users
6.3 Get User by ID (GET)
GET /users/1
7. Verify in H2 Console
Open:
/h2-console
Run:
SELECT * FROM USERS;
You should see the inserted data.
8. Important Design Notes
✔ Controller does NOT talk to repository
✔ Service handles business logic
✔ Repository handles DB operations
This separation is interview-critical.
9. Common Beginner Mistakes
❌ Direct repository access in controller
❌ Returning Optional to API
❌ No service layer
❌ Hardcoding IDs
10. Git Commit (Important)
Commit your changes:
git add .
git commit -m "Add User repository, service, and basic CRUD APIs"
11. What You Should Have Now
At this point:
- You can create users
- Fetch users
- Persist data
- Follow clean layering
This is a major real-world milestone.
12. What’s Next?
➡ Capstone – Article 4: DTOs, Validation & API Contracts
- Request/response DTOs
- Validation annotations
- Cleaner APIs
Type Next when you’re ready 🚀