Spring data jpa 使用MapStruct的Spring数据和JPA一对多

Spring data jpa 使用MapStruct的Spring数据和JPA一对多,spring-data-jpa,mapstruct,Spring Data Jpa,Mapstruct,我在Config和ConfigHeader之间有一对多关系。 以下是配置映射程序: @Mapper(componentModel = "spring", uses = {UserMapper.class, ConfigHeadersMapper.class}) public interface ConfigMapper extends EntityMapper<ConfigDTO, Config> { @Mapping(source = "user.id", target

我在Config和ConfigHeader之间有一对多关系。 以下是配置映射程序:

@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;
    }
}
保存配置和ConfigHeaders,但ConfigHeaders处的配置id(FK)为空

所以我尝试了以下代码:

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正确执行保存,您需要在
ConfigHeader
中设置指向配置的链接

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

第一种选择:

您可以使用
CollectionMappingStrategy#ADDER_PREFERRED
(请参阅更多)。为此,您需要在
配置中添加如下内容:

public void addHeader(ConfigHeader header) {
    this.headers.add(header);
    header.setConfig(this);
}
第二种选择:

ConfigMapper
中使用
@AfterMapping
,并在所有标题上设置配置

@AfterMapping
default void linkHeaders(@MappingTarget Config config) {
    config.getHeaders().stream().forEach(header -> header.setConfig(config));
}
@sjaak在中提出的第三个方案:

您可以使用
@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 );
    }
}

您还需要将
方法调整为实体
方法,以便它们也包括上下文。

正如您所注意到的,为了让JPA正确执行保存,您需要在
配置头
中设置到配置的链接

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

第一种选择:

您可以使用
CollectionMappingStrategy#ADDER_PREFERRED
(请参阅更多)。为此,您需要在
配置中添加如下内容:

public void addHeader(ConfigHeader header) {
    this.headers.add(header);
    header.setConfig(this);
}
第二种选择:

ConfigMapper
中使用
@AfterMapping
,并在所有标题上设置配置

@AfterMapping
default void linkHeaders(@MappingTarget Config config) {
    config.getHeaders().stream().forEach(header -> header.setConfig(config));
}
@sjaak在中提出的第三个方案:

您可以使用
@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 );
    }
}

您还需要将
方法调整为实体
方法,以便它们也包括上下文。

您也可以使用@context。这也为您提供了使用EntityManager进行操作的可能性。请看一个例子。

您也可以使用@Context。这也为您提供了使用EntityManager进行操作的可能性。请举出一个例子。

非常感谢您的宝贵意见。我采用了第二种方法。事实上,我已经看到了您所指出的文档,但是没有一个示例,它并不是真正有用的。你所指段落旁边的段落提供了一个例子。也许我们需要在文档中添加这个例子(加法器函数对我们来说是隐含的)。如果性能对您很重要,那么第一种方法或sjaak提出的方法会更好,因为您不会重复两次标题。谢谢你们的回答,我非常感谢。我将使用您的第一个解决方案。我也在尝试做同样的事情,但我在保存过程中遇到了以下异常-org.hibernate.PersistentObjectException:detached entity传递给persist:com.invenio.loyalty.rewardservice.domain.Item我使用的是spring data jpa。非常感谢您的有用输入。我采用了第二种方法。事实上,我已经看到了您所指出的文档,但是没有一个示例,它并不是真正有用的。你所指段落旁边的段落提供了一个例子。也许我们需要在文档中添加这个例子(加法器函数对我们来说是隐含的)。如果性能对您很重要,那么第一种方法或sjaak提出的方法会更好,因为您不会重复两次标题。谢谢你们的回答,我非常感谢。我将使用您的第一个解决方案。我也在尝试做同样的事情,但我在保存过程中遇到以下异常-org.hibernate.PersistentObjectException:distached entity传递给persist:com.invenio.loyalty.rewardservice.domain.Item我使用的是spring data jpa。谢谢您的输入。用你的方法也可以解决这个问题,但菲利普的解决方案对我来说似乎更好。这种方法实际上非常方便,在你无法更改实体时会有所帮助。它具有与加法器相同的性能,因为它避免了多次在报头上迭代。谢谢您的输入。用你的方法也可以解决这个问题,但菲利普的解决方案对我来说似乎更好。这种方法实际上非常方便,在你无法更改实体时会有所帮助。它具有与加法器相同的性能,因为它避免了在报头上多次迭代