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 示例。
我在 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 示例。