Using MapStruct for Object Mapping in Java: Basics, Advanced Features and Spring Integration
This article explains how MapStruct, a Java annotation‑processor library, can automatically generate mapper implementations to convert between entities and DTOs, covering basic usage, default methods, abstract classes, multiple source parameters, update operations, handling fields without getters/setters, Spring injection and custom type conversions.
MapStruct is a compile‑time code generator that creates mapper implementations for converting between Java objects (e.g., entities and DTOs), eliminating repetitive getter/setter copying.
First, simple domain classes are defined:
@AllArgsConstructor
@Data
public class User {
private Long id;
private String username;
private String password;
private String phoneNum;
private String email;
private Role role;
}
@AllArgsConstructor
@Data
public class Role {
private Long id;
private String roleName;
private String description;
}
@Data
public class UserRoleDto {
/** user id */
private Long userId;
/** username */
private String name;
/** role name */
private String roleName;
}A manual test shows the boiler‑plate required to copy values:
public class MainTest {
User user = null;
@Before
public void before() {
Role role = new Role(2L, "administrator", "超级管理员");
user = new User(1L, "zhangsan", "12345", "17677778888", "[email protected]", role);
}
@Test
public void test1() {
UserRoleDto dto = new UserRoleDto();
dto.setUserId(user.getId());
dto.setName(user.getUsername());
dto.setRoleName(user.getRole().getRoleName());
System.out.println(dto);
}
}MapStruct removes this boiler‑plate by defining a mapper interface with mapping rules:
@Mapper
public interface UserRoleMapper {
UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
UserRoleDto toUserRoleDto(User user);
}The generated implementation automatically copies the three fields, as demonstrated in a test that simply calls UserRoleMapper.INSTANCES.toUserRoleDto(user).
Advanced features include adding default methods to the mapper:
@Mapper
public interface UserRoleMapper {
// ... previous method
default UserRoleDto defaultConvert() {
UserRoleDto dto = new UserRoleDto();
dto.setUserId(0L);
dto.setName("None");
dto.setRoleName("None");
return dto;
}
}MapStruct can also be expressed as an abstract class instead of an interface:
@Mapper
public abstract class UserRoleMapper {
public static final UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
public abstract UserRoleDto toUserRoleDto(User user);
public UserRoleDto defaultConvert() { /* same as above */ }
}Multiple source parameters are supported, allowing values from two objects to be merged into one DTO:
@Mappings({
@Mapping(source = "user.id", target = "userId"),
@Mapping(source = "user.username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
UserRoleDto toUserRoleDto(User user, Role role);Updating an existing target object is possible with @MappingTarget:
@Mappings({
@Mapping(source = "userId", target = "id"),
@Mapping(source = "name", target = "username"),
@Mapping(source = "roleName", target = "role.roleName")
})
void updateDto(UserRoleDto dto, @MappingTarget User user);Even fields without getters/setters can be mapped by using public fields in the target class and the @InheritInverseConfiguration annotation:
@Mapper
public interface CustomerMapper {
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto dto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}Spring integration is achieved by setting componentModel = "spring" on the mapper, which registers the generated implementation as a Spring bean:
@Mapper(componentModel = "spring")
public interface CustomerMapper {
CustomerDto toCustomerDto(Customer customer);
}
// Usage in a Spring component
@Autowired
private CustomerMapper mapper;Custom type conversions can be added via the uses attribute. The example converts a Boolean field to a String ("Y"/"N") using a helper class:
public class BooleanStrFormat {
public String toStr(Boolean b) { return b ? "Y" : "N"; }
public Boolean toBoolean(String s) { return "Y".equals(s); }
}
@Mapper(uses = { BooleanStrFormat.class })
public interface CustomerMapper {
@Mappings({
@Mapping(source = "name", target = "customerName"),
@Mapping(source = "isDisable", target = "disable")
})
CustomerDto toCustomerDto(Customer customer);
}When the mapper is configured for Spring, the helper class is also injected automatically.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
