我有以下实体:问题有OneToOne配置.而Config有很多选项.全部配置为CASCADE.ALL(附录)
基于RequestDTO(requestConfig),我为新问题或现有问题创建id = null的新Option实体.
在这两种情况下,我都想访问新选项的生成ID.但是,它确实适用于新问题,但不适用于现有问题:
新问题(好)
// RequestDTO requestConfig is a controller parameter
Question question = new Question(...);
Config config = requestDTO.createConfig(Optional.empty());
question.setConfig(config);
LinkedHashMap<String, Option> idMapping = requestConfig.getNewOptions();
idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null
question = questionRepo.save(question);
idMapping.forEach((foo, option) -> System.out.println(option.getId())); // 675, 676, ... etc
现有问题(破碎,见最后一行,ids为空)
// RequestDTO requestConfig is a controller parameter
Question question = questionRepo.find(...);
Config config = requestDTO.getConfig(Optional.of(question.getConfig()));
question.setConfig(config);
LinkedHashMap<String, Option> idMapping = requestConfig.getNewOptions();
idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null
question = questionRepo.save(question);
idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null
为什么会这样?我希望LinkedHashMap idMapping包含新创建的选项及其创建的ID,因为它们是从问题保存操作级联的.我检查了数据库并插入了它们!
附录
作为参考,这是我的RequestDTO和实体:
public class RequestDTO {
private LinkedHashMap<String, OptionDTO> optionDTOs;
@JsonIgnore
private LinkedHashMap<String, Option> newOptions = new LinkedHashMap<>();
public Config getConfig(Optional<Config> oldConfig) {
Config config = new Config();
if (oldConfig.isPresent()) {
config = oldConfig.get();
}
// update the options based on our OptionDTOs
config.getOptions().clear();
optionDTOs.stream()
.map(o -> {
try { // to find the existing option
Option theOption = config.getOptions().stream()
// try to find in given config
.filter(existing -> o.getId().equals(existing.getId()))
.findAny()
// fallback to db
.orElse(optionRepo.findOne(Long.parseLong(o.getId())));
if (null != theOption) {
return theOption;
}
} catch (Exception e) {
}
// handle as new one by creating a new one with id=null
Option newOption = new Option(null, config);
newOptions.add(newOption);
return newOption;
})
.forEach(o -> config.getOptions().add(o));
return config;
}
// getters
}
实体:问题
@Entity
public class Question {
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "config_id", nullable = false)
private Config config;
// ...
}
实体:配置
@Entity
public class Config {
@OneToOne(mappedBy = "config")
@JoinColumn(name = "question_id", nullable = true)
private Question question;
@OneToMany(mappedBy = "config", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Option> options = new ArrayList<>();
// ...
}
实体:选项
@Entity
public class Option {
@ManyToOne
@JoinColumn(name = "config_id", nullable = false)
private Config config;
public Option(Long id, Config config) {
super();
this.id = id;
this.config = config;
}
// ...
}
最佳答案 当您在新问题上调用questionRepo.save()时,Spring Data会识别您正在尝试保存新实体,并在内部调用EntityManager.persist().
EntityManager.persist(entity)使实体作为参数持久化传递.
但是,当您在现有问题上调用questionRepo.save()时,Spring Data会在内部调用EntityManager.merge().
EntityManager.merge(entity)返回实体的持久副本.
问题是你在调用questionRepo.save()之前调用requestConfig.getNewOptions().在您描述的第一种情况下无关紧要,因为分配给问题的原始实例(即使用Question question = new Question(…)创建的实例;),以及使用添加到Option的子实例.forEach(o – > config.getOptions().add(o))行,变为持久化,并获得自动生成的id.
但是,它在第二种情况下很重要,因为使用行.forEach(o – > config.getOptions().add(o))添加到Option的新子实例不会变为持久化.相反,只有由questionRepo.save()返回的副本引用的子实体instaces(它反过来返回EntityManager.merge()的结果)是持久的.
您应该在调用questionRepo.save()之后构造idMapping映射(使用question.getConfig().getNewOptions()).这应该处理两种情况.