Mapstruct双向映射

12

我有一个示例,其中我有一个单独的领域层和一个单独的持久层。 我正在使用Mapstruct进行映射,当从领域到实体或从实体到领域进行映射时,由于始终调用的双向引用->无限循环情况,我会遇到StackOverflow。 如何在这种情况下使用Mapstruct?

class User {
  private UserProfile userProfile;
}

class UserProfile {
 private User user;
}

@Entity
class UserEntity {
  @OneToOne
  @PrimaryKeyJoinColumn
  private UserProfileEntity userProfile;
}

@Entity
class UserProfileEntity {
  @OneToOne(mappedBy = "userProfile")
  private UserEntity userEntity;
}

映射的类非常基本。

@Mapper
interface UserMapper {

UserEntity mapToEntity(User user);

User mapToDomain(UserEntity userEntity);
}
1个回答

19

请查看Mapstruct映射循环的示例

文档中Context注解也提供了解决方案。

示例

完整的示例:https://github.com/jannis-baratheon/stackoverflow--mapstruct-mapping-graph-with-cycles

参考

映射器:

@Mapper
public interface UserMapper {

    @Mapping(target = "userProfileEntity", source = "userProfile")
    UserEntity mapToEntity(User user,
                           @Context CycleAvoidingMappingContext cycleAvoidingMappingContext);

    @InheritInverseConfiguration
    User mapToDomain(UserEntity userEntity,
                     @Context CycleAvoidingMappingContext cycleAvoidingMappingContext);

    @Mapping(target = "userEntity", source = "user")
    UserProfileEntity mapToEntity(UserProfile userProfile,
                                  @Context CycleAvoidingMappingContext cycleAvoidingMappingContext);

    @InheritInverseConfiguration
    UserProfile mapToDomain(UserProfileEntity userProfileEntity,
                            @Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
}

CycleAvoidingMappingContext 用于跟踪已映射的对象并重复使用它们,从而避免堆栈溢出。

public class CycleAvoidingMappingContext {
    private final Map<Object, Object> knownInstances = new IdentityHashMap<>();

    @BeforeMapping
    public <T> T getMappedInstance(Object source,
                                   @TargetType Class<T> targetType) {
        return targetType.cast(knownInstances.get(source));
    }

    @BeforeMapping
    public void storeMappedInstance(Object source,
                                    @MappingTarget Object target) {
        knownInstances.put(source, target);
    }
}

使用Mapper(映射单个对象):

UserEntity mappedUserEntity = mapper.mapToEntity(user, new CycleAvoidingMappingContext());

你可以在mapper上添加一个默认方法:

您还可以在mapper上添加一个默认方法:

@Mapper
public interface UserMapper {

    // (...)

    default UserEntity mapToEntity(User user) {
        return mapToEntity(user, new CycleAvoidingMappingContext());
    }

    // (...)
}

1
我喜欢使用return mapToEntity(user, new CycleAvoidingMappingContext());的默认方法。然而,这导致了一个模棱两可的错误消息。我不得不像这里所描述的那样添加了一个@Qualifier。我在所有新的默认方法中添加了@DoIgnore。之后一切都没问题了。 - Avec
将映射器声明为抽象类,其中包含“knownInstances”字段和另外两个方法,有助于消除每个映射器方法调用中对CycleAvoidingMappingContext类实例化的需求。 CycleAvoidingMappingContext的方法是通用的,并且可以在继承的基类中定义。 - Jamali
关于默认方法的更多信息,来自@Avec的完美答案更加完美,谢谢! - Implermine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接