Spring Data 和 JPA 一对多与 MapStruct

Inv*_*tor 1 spring-data-jpa mapstruct

我在 Config 和 ConfigHeaders 之间有一对多的关系。这是配置映射器:

@Mapper(componentModel = "spring", uses = {UserMapper.class, ConfigHeadersMapper.class})
public interface ConfigMapper extends EntityMapper<ConfigDTO, Config> {

    @Mapping(source = "user.id", target = "userId")
    ConfigDTO toDto(Config config);

    @Mapping(source = "userId", target = "user")
    @Mapping(target = "messages", ignore = true)
    Config toEntity(ConfigDTO configDTO);

    default Config fromId(Long id) {
        if (id == null) {
            return null;
        }
        Config config = new Config();
        config.setId(id);
        return config;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是 ConfigHeadersMapper:

@Mapper(componentModel = "spring", uses = {ConfigMapper.class})
public interface ConfigHeadersMapper extends EntityMapper<ConfigHeadersDTO, ConfigHeaders> {

    @Mapping(source = "config.id", target = "configId")
    ConfigHeadersDTO toDto(ConfigHeaders configHeaders);

    @Mapping(source = "configId", target = "config")
    ConfigHeaders toEntity(ConfigHeadersDTO configHeadersDTO);

    default ConfigHeaders fromId(Long id) {
        if (id == null) {
            return null;
        }
        ConfigHeaders configHeaders = new ConfigHeaders();
        configHeaders.setId(id);
        return configHeaders;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试保存一个新实体(对于 Config 和 ConfigHeaders 的 id 等于 null)时,这段代码:

final Config config = configMapper.toEntity(configDTO);
Config newConfig = configRepository.save(config);
Run Code Online (Sandbox Code Playgroud)

保存 Config 和 ConfigHeaders 但 ConfigHeaders 处的 config_id (FK) 为 NULL。

所以我尝试了这个代码:

final Config config = configMapper.toEntity(configDTO);
config.getHeaders().stream().forEach(header -> header.setConfig(config));
Config newConfig = configRepository.save(config);
Run Code Online (Sandbox Code Playgroud)

并且确实使用自动生成的父 ID (config_id) 保存子项 (ConfigHeaders)。

你能告诉我我在 MapStruct 上做错了什么吗?我对这个工具很陌生,我不相信这是正确的解决方案。我认为以前的代码现在只是一种解决方法。

实际上,我已经检查了生成的 MapStruct 实现代码,我注意到它正确设置了父 ID(对于新的 ID 为空),但它没有设置与父实体的向后关系。我怎样才能通过 MapStruct 实现这一点?

先感谢您

Fil*_*lip 5

正如您所注意到的,为了让 JPA 正确执行保存,您需要在ConfigHeader.

有两种方法可以实现这一点:

第一个选项:

您可以使用CollectionMappingStrategy#ADDER_PREFERRED在此处查看更多信息)。为此,您需要添加以下内容Config

public void addHeader(ConfigHeader header) {
    this.headers.add(header);
    header.setConfig(this);
}
Run Code Online (Sandbox Code Playgroud)

第二种选择:

@AfterMappingConfigMapper那里使用并在所有标题上设置配置。

@AfterMapping
default void linkHeaders(@MappingTarget Config config) {
    config.getHeaders().stream().forEach(header -> header.setConfig(config));
}
Run Code Online (Sandbox Code Playgroud)

@sjaak 在/sf/answers/3428188361/ 中提出的第三个选项:

您可以使用mapstruct-jpa-child-parent示例中@Context所示的属性。这与第一个选项具有相同的性能,因为您不必重复标题两次。这样做的另一个好处是,您可以在无法向实体添加内容时使用它。

看起来像:

public class ConfigContext {
    private Config config;
    @BeforeMapping
    public void setConfig(@MappingTarget Config config) {
       this.config = config;
    }

    @AfterMapping
    public void establishRelation(@MappingTarget ConfigHeader header) {
        header.setConfig( header );
    }
}
Run Code Online (Sandbox Code Playgroud)

您还需要调整您的toEntity方法,以便它们也包括上下文。