Mapstruct:如何通过@MappingTarget 使用自定义映射器
Mapstruct: How to use custom mappers with @MappingTarget
我正在使用 Mapstruct,我需要使用 @MappingTarget
更新现有的 bean,但需要应用一些复杂的逻辑来设置目标中的正确字段。
假设我有一个看起来像这样的目标 bean。用户有一个帐户列表,其中一个帐户被标记为收藏。
UserDetails {
String name;
List<Account> accounts;
}
Account {
String id;
boolean favourite;
}
DTO class 包含他们最喜欢的帐户的帐户 ID。
UserDetialsDTO {
String name;
String favouriteAccountId;
List<String> accountIds;
}
我需要使用一些复杂的逻辑来更新帐户列表中的正确 Account
。
UserDetails fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
查找和更新正确的 Account
使其成为最爱的逻辑是这样的:
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
如何告诉 Mapstruct 在更新 @MapingTarget
时使用此自定义逻辑?
尝试:
@Mapper
public interface MyMapper {
@Mapping( target = "accounts", ignore = true )
void fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
@AfterMapping
default void handleAccounts(UserDetialsDTO dto, @MappingTarget UserDetails userDetails) {
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
}
}
您可以使用 decorator 来同时实现:
Account
列表的初始化
- 最喜欢的自定义逻辑
如:
@Mapper
@DecoratedWith(MyMapperDecorator.class)
public interface MyMapper {
void fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
}
public class MyMapperDecorator implements MyMapper{
@Override
public void fromDto(final UserDetialsDTO dto, final UserDetails userDetails) {
if(dto == null){
return;
}
dto.getAccountIds().forEach(a -> {
final Account account = new Account();
account.setId(a);
account.setFavourite(a.equals(dto.getFavouriteAccountId()));
userDetails.getAccounts().add(new Account());
});
}
}
为了避免重复,我建议使用 Set<Account>
而不是 List
并相应地实施 Account.equals()
。
另一种(稍微)更复杂的方法可能涉及定义一个 String
到 Account
映射器使用它:
@Mapper(uses = AccountMapper.class)
@DecoratedWith(MyMapperDecorator.class)
interface MyMapper {
MyMapper INSTANCE = Mappers.getMapper( MyMapper.class );
@Mapping(target = "accounts", source="accountIds")
abstract void update(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
}
public abstract class MyMapperDecorator implements MyMapper{
private final MyMapper delegate;
MyMapperDecorator(final MyMapper delegate){
this.delegate = delegate;
}
@Override
public void update(final UserDetialsDTO dto, final UserDetails userDetails) {
if(dto == null){
return;
}
delegate.update(dto, userDetails);
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
}
}
@Mapper
public interface AccountMapper {
Account fromString(String id);
void update(String id, @MappingTarget Account account);
}
我正在使用 Mapstruct,我需要使用 @MappingTarget
更新现有的 bean,但需要应用一些复杂的逻辑来设置目标中的正确字段。
假设我有一个看起来像这样的目标 bean。用户有一个帐户列表,其中一个帐户被标记为收藏。
UserDetails {
String name;
List<Account> accounts;
}
Account {
String id;
boolean favourite;
}
DTO class 包含他们最喜欢的帐户的帐户 ID。
UserDetialsDTO {
String name;
String favouriteAccountId;
List<String> accountIds;
}
我需要使用一些复杂的逻辑来更新帐户列表中的正确 Account
。
UserDetails fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
查找和更新正确的 Account
使其成为最爱的逻辑是这样的:
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
如何告诉 Mapstruct 在更新 @MapingTarget
时使用此自定义逻辑?
尝试:
@Mapper
public interface MyMapper {
@Mapping( target = "accounts", ignore = true )
void fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
@AfterMapping
default void handleAccounts(UserDetialsDTO dto, @MappingTarget UserDetails userDetails) {
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
}
}
您可以使用 decorator 来同时实现:
Account
列表的初始化- 最喜欢的自定义逻辑
如:
@Mapper
@DecoratedWith(MyMapperDecorator.class)
public interface MyMapper {
void fromDto(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
}
public class MyMapperDecorator implements MyMapper{
@Override
public void fromDto(final UserDetialsDTO dto, final UserDetails userDetails) {
if(dto == null){
return;
}
dto.getAccountIds().forEach(a -> {
final Account account = new Account();
account.setId(a);
account.setFavourite(a.equals(dto.getFavouriteAccountId()));
userDetails.getAccounts().add(new Account());
});
}
}
为了避免重复,我建议使用 Set<Account>
而不是 List
并相应地实施 Account.equals()
。
另一种(稍微)更复杂的方法可能涉及定义一个 String
到 Account
映射器使用它:
@Mapper(uses = AccountMapper.class)
@DecoratedWith(MyMapperDecorator.class)
interface MyMapper {
MyMapper INSTANCE = Mappers.getMapper( MyMapper.class );
@Mapping(target = "accounts", source="accountIds")
abstract void update(UserDetialsDTO dto, @MappingTarget UserDetails userDetails);
}
public abstract class MyMapperDecorator implements MyMapper{
private final MyMapper delegate;
MyMapperDecorator(final MyMapper delegate){
this.delegate = delegate;
}
@Override
public void update(final UserDetialsDTO dto, final UserDetails userDetails) {
if(dto == null){
return;
}
delegate.update(dto, userDetails);
userDetails.accounts
.stream()
.forEach(acct -> acct.setFavourite(dto.favouriteAccountId.equals(acct.id))) ;
}
}
@Mapper
public interface AccountMapper {
Account fromString(String id);
void update(String id, @MappingTarget Account account);
}