Spring Boot cookbook · Beginner
Map entities to DTOs with MapStruct
Stop hand-writing repetitive mapping code between JPA entities and API records.
Approach
MapStruct generates type-safe mapping code at compile time from an annotated interface. Declare a mapper with componentModel = "spring", map records/entities, customize field names with @Mapping, and use @MappingTarget for updates. Configure unmappedTargetPolicy = ERROR so missing fields fail the build.
Solution
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface ProductMapper {
@Mapping(target = "price", expression = "java(Money.format(product.getPriceCents(), product.getCurrency()))")
ProductResponse toResponse(Product product);
List<ProductResponse> toResponses(List<Product> products);
@Mapping(target = "id", ignore = true)
@Mapping(target = "version", ignore = true)
@Mapping(target = "createdAt", ignore = true)
void update(UpdateProductRequest request, @MappingTarget Product product);
}
@Service
class ProductService {
private final ProductRepository repo;
private final ProductMapper mapper;
ProductService(ProductRepository repo, ProductMapper mapper) { this.repo = repo; this.mapper = mapper; }
@Transactional
public ProductResponse update(long id, UpdateProductRequest request) {
Product product = repo.findById(id).orElseThrow(() -> new NotFoundException("Product " + id));
mapper.update(request, product);
return mapper.toResponse(product);
}
}
Pitfalls
- Mapping lazy collections outside a transaction (LazyInitializationException).
- Silently unmapped fields (set the ERROR policy).
- Putting business logic in mappers.
Variations
- Hand-written static
from()factories on records for small apps.
Further reading: MapStruct reference guide
Reference: https://mapstruct.org/documentation/stable/reference/html/
More Spring Boot recipes
- Add correlation IDs to Spring Boot logs
- Add retries and a circuit breaker with Resilience4j
- Authenticate machine clients with API keys in Spring Security
- Cache service results in Redis with @Cacheable
- Consume Kafka messages with retries and a dead-letter topic
- Handle concurrent updates with optimistic locking
- Implement soft delete with Hibernate
- Make POST endpoints idempotent in Spring Boot