CRUD Operations
Introduction
CRUD (Create, Read, Update, Delete) is the set of fundamental data-management operations. Learn a complete CRUD implementation using controllers, services, and repositories.
Entity
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Product name is required")
@Size(max = 200)
private String name;
private String description;
@Min(value = 0, message = "Price cannot be negative")
private double price;
private int stock;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContaining(String keyword);
boolean existsByName(String name);
}
Service Layer
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// CREATE
public Product create(Product product) {
if (productRepository.existsByName(product.getName())) {
throw new BadRequestException("A product with that name already exists");
}
return productRepository.save(product);
}
// READ ALL
public List<Product> findAll() {
return productRepository.findAll();
}
// READ BY ID
public Product findById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product not found"));
}
// UPDATE
public Product update(Long id, Product updatedData) {
Product product = findById(id);
product.setName(updatedData.getName());
product.setDescription(updatedData.getDescription());
product.setPrice(updatedData.getPrice());
product.setStock(updatedData.getStock());
return productRepository.save(product);
}
// DELETE
public void delete(Long id) {
Product product = findById(id);
productRepository.delete(product);
}
}
Controller
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product create(@Valid @RequestBody Product product) {
return productService.create(product);
}
@GetMapping
public List<Product> findAll() {
return productService.findAll();
}
@GetMapping("/{id}")
public Product findById(@PathVariable Long id) {
return productService.findById(id);
}
@PutMapping("/{id}")
public Product update(@PathVariable Long id, @Valid @RequestBody Product product) {
return productService.update(id, product);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
productService.delete(id);
}
}
Endpoint Summary
| Method | Endpoint | Purpose | Status Code |
|---|---|---|---|
| POST | /api/products |
Create a new product | 201 |
| GET | /api/products |
Fetch all products | 200 |
| GET | /api/products/{id} |
Fetch a product by ID | 200 |
| PUT | /api/products/{id} |
Update a product | 200 |
| DELETE | /api/products/{id} |
Delete a product | 204 |
Request Flow
Client → Controller → Service → Repository → Database
↓
Input validation
↓
Error handling
Summary
- Separate the code: Controller (HTTP), Service (business), Repository (data).
- Use
@Validfor input validation. - The service layer handles business logic and business validation.
Next chapter: We'll cover Pagination & Sorting — how to manage large volumes of data with pagination and sorting.