Spring 数据和 JPA 使用 MapStruct 一对多

Spring Data and JPA One to many with 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;
    }
}

这是 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;
    }
}

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

final Config config = configMapper.toEntity(configDTO);
Config newConfig = configRepository.save(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);

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

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

实际上我已经检查了已生成的 MapStruct 实现代码,我注意到它正确设置了父 ID(对于新 ID 为 null)但它没有设置与父实体的向后关系。我如何通过 MapStruct 完成此操作?

提前致谢

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

中的配置

您可以通过两种方式实现此目的:

第一个选项:

您可以使用 CollectionMappingStrategy#ADDER_PREFERRED(查看更多 here)。为此,您需要在 Config 中添加如下内容:

public void addHeader(ConfigHeader header) {
    this.headers.add(header);
    header.setConfig(this);
}

第二个选项:

您在 ConfigMapper 中使用 @AfterMapping 并在那里设置所有 headers 的配置。

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

@sjaak 在 https://whosebug.com/a/48974119/1115491 中提出的第三个选项:

您可以使用 @Context 属性,如 mapstruct-jpa-child-parent 示例所示。这与第一个选项具有相同的性能,因为您不必对 headers 进行两次迭代。这样做的另一个好处是,当您无法向实体添加内容时,您可以使用它。

看起来像:

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 );
    }
}

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

您也可以使用@Context。这也让您可以使用 EntityManager 做一些事情。查看 here 示例。