通常,使用MapStruct映射集合的方式与使用简单类型的方式相同。
基本上,必须创建一个简单的接口或抽象类并声明映射方法。 根据声明,MapStruct将自动生成映射代码。 通常,生成的代码将遍历源集合,将每个元素转换为目标类型,并将每个元素包括在目标集合中。
创建简单的映射类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String firstName;
private String lastName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
private String firstName;
private String lastName;
}
定义映射
@Mapper(componentModel = "spring")
public interface UserMapper {
List map(List users);
}
下面为mapstruct生成的实现类
@Component
public class UserMapperImpl implements UserMapper {
@Override
public List<UserDTO> map(List<User> users) {
if ( users == null ) {
return null;
}
List<UserDTO> list = new ArrayList<UserDTO>( users.size() );
for ( User user : users ) {
list.add( userToUserDTO( user ) );
}
return list;
}
protected UserDTO userToUserDTO(User user) {
if ( user == null ) {
return null;
}
UserDTO userDTO = new UserDTO();
userDTO.setFirstName( user.getFirstName() );
userDTO.setLastName( user.getLastName() );
return userDTO;
}
}
有一件重要的事情要注意。 具体来说,MapStruct为自动生成了从User到UserDTO的映射。
在某些情况下,这是不可能的。 例如,假设要将User模型映射到以下模型:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserFullNameDTO {
private String fullName;
}
在这种情况下,如果仅声明从User列表到UserFullNameDTO列表的映射方法,将收到编译时错误或警告,例如:
E:\koal_code\demo\src\main\java\com\example\demo\mapstruct\UserMapper.java
Warning:(14, 21) java: Unmapped target property: "fullName".
基本上,这意味着MapStruct无法在这种情况下为自动生成映射。 因此,需要手动定义User和UserFullNameDTO之间的映射。
手动定义:
@Mapper(componentModel = "spring")
public interface UserMapper {
List<UserDTO> map(List<User> users);
List<UserFullNameDTO> mapToFullList(List<User> users);
default UserFullNameDTO mapUserFullName(User user) {
UserFullNameDTO employeeInfoDTO = new UserFullNameDTO();
employeeInfoDTO.setFullName(user.getFirstName() + " " + user.getLastName());
return employeeInfoDTO;
}
}
MapStruct的映射集的工作方式与列表相同。 例如,假设要将User实例集映射到UserDTO实例集。
和以前一样,需要一个映射器:
@Mapper(componentModel = "spring")
public interface UserMapper {
Set<UserDTO> map(Set<User> users);
}
然后MapStruct将生成适当的代码:
@Component
public class UserMapperImpl implements UserMapper {
@Override
public Set map(Set users) {
if ( users == null ) {
return null;
}
Set set = new HashSet( Math.max( (int) ( users.size() / .75f ) + 1, 16 ) );
for ( User user : users ) {
set.add( userToUserDTO( user ) );
}
return set;
}
protected UserDTO userToUserDTO(User user) {
if ( user == null ) {
return null;
}
UserDTO userDTO = new UserDTO();
userDTO.setFirstName( user.getFirstName() );
userDTO.setLastName( user.getLastName() );
return userDTO;
}
}
Map也是如此。将Map
@Mapper(componentModel = "spring")
public interface UserMapper {
Map<String, UserDTO> map(Map<String, User> idUserMap);
}
MapStruct将生成适当的代码:
@Component
public class UserMapperImpl implements UserMapper {
@Override
public Map<String, UserDTO> map(Map<String, User> idUserMap) {
if ( idUserMap == null ) {
return null;
}
Map<String, UserDTO> map = new HashMap<String, UserDTO>( Math.max( (int) ( idUserMap.size() / .75f ) + 1, 16 ) );
for ( java.util.Map.Entry<String, User> entry : idUserMap.entrySet() ) {
String key = entry.getKey();
UserDTO value = userToUserDTO( entry.getValue() );
map.put( key, value );
}
return map;
}
protected UserDTO userToUserDTO(User user) {
if ( user == null ) {
return null;
}
UserDTO userDTO = new UserDTO();
userDTO.setFirstName( user.getFirstName() );
userDTO.setLastName( user.getLastName() );
return userDTO;
}
}
需要映射具有父子关系的数据类型。 通常,有一个数据类型(父级),该字段具有另一个数据类型(子级)的Collection作为字段。
对于此类情况,MapStruct提供了一种选择如何设置子代或将子代添加到父代类型的方法。 特别地,@Mapper批注具有collectionMappingStrategy属性,该属性可以是ACCESSOR_ONLY,SETTER_PREFERRED,ADDER_PREFERRED或TARGET_IMMUTABLE。
所有这些值都涉及应设置子代或将其添加到父代类型的方式。 默认值为ACCESSOR_ONLY,这意味着只能使用访问器来设置子级的Collection。
举个例子来更好地理解是如何工作的。
创建一个Company类作为映射源
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
private List<User> users;
}
请注意,既有setter setUsers,又有加法器addUser。 同样,对于加法器,负责集合的初始化。
public class CompanyDTO {
private List<UserDTO> users;
public List<UserDTO> getUsers() {
return users;
}
public void setUsers(List<UserDTO> users) {
this.users = users;
}
public void addEmployee(UserDTO userDTO) {
if (users == null) {
users = new ArrayList<>();
}
users.add(userDTO);
}
}
映射器
@Mapper(componentModel = "spring",uses = UserMapper.class)
public interface CompanyMapper {
CompanyDTO map(Company company);
}
请注意,使用UserMapper和默认的collectionMappingStrategy。
现在,看一下MapStruct生成的代码:
@Component
public class CompanyMapperImpl implements CompanyMapper {
@Autowired
private UserMapper userMapper;
@Override
public CompanyDTO map(Company company) {
if ( company == null ) {
return null;
}
CompanyDTO companyDTO = new CompanyDTO();
companyDTO.setUsers( userMapper.map( company.getUsers() ) );
return companyDTO;
}
}
可以看出,MapStruct使用setUsers来设置UserDTO实例列表。 这里使用默认的collectionMappingStrategy ACCESSOR_ONLY。
此外,MapStruct在UserMapper中找到了一种将List 映射到List 的方法,然后重新使用它。
@Mapper(componentModel = "spring",collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,uses = UserMapper.class)
public interface CompanyMapper {
CompanyDTO map(Company company);
}
要重用UserMapper.需要显式添加一个首先可以将单个User转换为UserDTO的方法:
@Mapper(componentModel = "spring")
public interface UserMapper { UserDTO map(User user);}
这是因为MapStruct将使用加法器将UserDTO实例一一添加到目标CompanyDTO实例,生成的实现类
@Component
public class CompanyMapperImpl implements CompanyMapper {
@Autowired
private UserMapper userMapper;
@Override
public CompanyDTO map(Company company) {
if ( company == null ) {
return null;
}
CompanyDTO companyDTO = new CompanyDTO();
if ( company.getUsers() != null ) {
for ( User user : company.getUsers() ) {
companyDTO.addEmployee( userMapper.map( user ) );
}
}
return companyDTO;
}
}