Careeroza
Manual Mapping
A Hand-Written Mapper
The simplest mapper is a class with static (or instance) methods. It is plain Java, easy to read and easy to debug. This program runs: it shows the entity with internal fields, and the DTO that the client actually sees.
Example
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
public class Main {
public static void main(String[] args) {
var request = new CreateProductRequest("Wireless Mouse", "2.4 GHz, silent clicks",
new BigDecimal("799.00"), 120, "Electronics");
Product entity = ProductMapper.toEntity(request);
entity.id = 1L; // set by the database
entity.costPrice = new BigDecimal("520.00"); // internal only
entity.createdOn = LocalDate.of(2026, 10, 12);
System.out.println("Entity: " + entity);
System.out.println("Response: " + ProductMapper.toResponse(entity));
Product soldOut = new Product();
soldOut.id = 2L; soldOut.name = "Clean Code"; soldOut.price = new BigDecimal("450.00");
soldOut.stock = 0; soldOut.category = "Books";
List<ProductResponse> page = List.of(entity, soldOut).stream().map(ProductMapper::toResponse).toList();
page.forEach(r -> System.out.println("List item: " + r.name() + ", inStock=" + r.inStock()));
}
}
class Product { // JPA entity in a real app
Long id;
String name, description, category;
BigDecimal price, costPrice;
int stock;
LocalDate createdOn;
public String toString() {
return "Product[id=" + id + ", name=" + name + ", price=" + price + ", costPrice=" + costPrice
+ ", stock=" + stock + ", createdOn=" + createdOn + "]";
}
}
record CreateProductRequest(String name, String description, BigDecimal price, int stock, String category) {}
record ProductResponse(Long id, String name, BigDecimal price, int stock, String category, boolean inStock) {}
final class ProductMapper {
private ProductMapper() {}
static Product toEntity(CreateProductRequest r) {
Product p = new Product();
p.name = r.name().trim();
p.description = r.description();
p.price = r.price();
p.stock = r.stock();
p.category = r.category();
return p;
}
static ProductResponse toResponse(Product p) {
return new ProductResponse(p.id, p.name, p.price, p.stock, p.category, p.stock > 0);
}
}Output
Entity: Product[id=1, name=Wireless Mouse, price=799.00, costPrice=520.00, stock=120, createdOn=2026-10-12] Response: ProductResponse[id=1, name=Wireless Mouse, price=799.00, stock=120, category=Electronics, inStock=true] List item: Wireless Mouse, inStock=true List item: Clean Code, inStock=false
Mapping an Update onto an Existing Entity
For PUT/PATCH you usually load the entity, then copy the request onto it, instead of creating a new object. That keeps fields the client does not control (id, createdOn, costPrice).
static void updateEntity(UpdateProductRequest r, Product target) {
target.setName(r.name());
target.setDescription(r.description());
target.setPrice(r.price());
target.setStock(r.stock());
target.setCategory(r.category());
// id, costPrice, createdOn are NOT touched
}Exercise: Write toResponse for an entity that has firstName and lastName, where the DTO has a single fullName field.
Show answer
static UserResponse toResponse(User u) {
return new UserResponse(u.getId(), u.getFirstName() + " " + u.getLastName(), u.getEmail());
}