我有以下实体:Question
有OneToOne Config
。 Config
有很多Option
个。所有都配置为CASCADE.ALL
(附录)
基于RequestDTO
(requestConfig
)我为新问题或现有问题创建了Option
个id=null
实体。
在这两种情况下我想访问新Options
生成的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
包含新创建的Option
及其创建的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;
}
// ...
}
答案 0 :(得分:1)
当您在新Question
上调用EntityManager.persist()
时,Spring Data会识别您正在尝试保存新实体,并在内部调用EntityManager.persist(entity)
。
questionRepo.save()
使作为参数持久性传递的实体。
但是,当您在现有Question
上调用EntityManager.merge()
时,Spring Data会在内部调用EntityManager.merge(entity)
。
requestConfig.getNewOptions()
会返回实体的持久副本。
问题是您在呼叫questionRepo.save()
之前致电question
。在您描述的第一种情况下,无关紧要,因为分配给Question question = new Question(...);
的原始实例(即使用Option
创建的实例)以及添加到{{1}的子实例使用行.forEach(o -> config.getOptions().add(o))
,成为持久,并获取自动生成的ID。
但是,确实在第二种情况下很重要,因为新的子实例使用行Option
添加到.forEach(o -> config.getOptions().add(o))
,不会变得持久。相反,只有questionRepo.save()
返回的引用的引用的子实体实例(后者返回EntityManager.merge()
的结果)才是持久的。
您应该在调用idMapping
之后构建questionRepo.save()
地图(使用question.getConfig().getNewOptions()
)。这应该处理两种情况。
答案 1 :(得分:0)
我检查了数据库并插入了它们!
这发生在entityManager只返回当前实体的新id而不是cascade保存的相关实体。保存问题后,请尝试通过问题ID重新找到。