Mapstruct двунаправленное отображение - PullRequest
0 голосов
/ 24 января 2020

У меня есть следующий пример, в котором у меня есть отдельный уровень домена и отдельный уровень персистентности. Я использую Mapstruct для отображения, и я получаю StackOverflow при отображении из домена в объект или из объекта в домен из-за двунаправленной ссылки, которая всегда вызывается -> бесконечный l oop сценарий. Как я могу использовать 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;
}

класс для картографии довольно простой c

@Mapper
interface UserMapper {

UserEntity mapToEntity(User user);

User mapToDomain(UserEntity userEntity);
}

1 Ответ

3 голосов
/ 24 января 2020

Ознакомьтесь с примером Mapstruct с циклами .

Решение вашей проблемы также продемонстрировано в документации по контекстной аннотации .

Пример

Полный пример: https://github.com/jannis-baratheon/stackoverflow--mapstruct-mapping-graph-with-cycles.

Ссылка

Mapper:

@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
public interface UserMapper {

    // (...)

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

    // (...)
}
...