用例:用户可以使用以JavaScript编写的单页网页应用程序来回答多项选择题。
201 CREATED
包含地图临时ID - >后端ID 更新其ID。我们应该如何在后端实现最后一部分(5-7添加选项)的对应部分?
我试试这个,但是在坚持不懈之后我无法得到孩子们。
实体
@Entity
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy = "config", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Option> options = new ArrayList<>();
// ...
}
@Entity
public class Option {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne
@JoinColumn(name = "question_id", nullable = false)
private Question question;
public Option(Long id, Config config) {
this.id = id;
this.question = question;
}
// ...
}
控制器的
@RestController
@RequestMapping("/questions")
public class AdminQuestionsController {
@Autowired
private QuestionRepository questionRepo;
@Autowired
private OptionRepository optionRepo;
@PutMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public QuestionDTO updateQuestion(@PathVariable("id") String id, @RequestBody QuestionDTO requestDTO) {
Question question = questionRepo.findOneById(Long.parseLong(id));
// will hold a mapping of the temporary id to the newly created Options.
Map<String, Option> newOptions = new HashMap<>();
// update the options
question.getOptions().clear();
requestDTO.getOptions().stream()
.map(o -> {
try { // to find the existing option
Option theOption = question.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.put(o.getId(), newOption);
return newOption;
})
.forEach(o -> question.getOptions().add(o));
question = questionRepo.save(question);
// create the id mapping
Map<String, String> idMap = new HashMap<>();
for (Entry<String, Option> e : newOptions.entrySet()) {
idMap.put(e.getKey(), e.getValue().getId());
// PROBLEM: e.getValue().getId() is null
}
return QuestionDTO result = QuestionDTO.from(question, idMap);
}
}
在控制器中我标记了问题:e.getValue()。getId()为空
这样的控制器应该如何创建idMap?
答案 0 :(得分:1)
最好是单独保存每个选项,然后将生成的ID保存在地图上。
我在下面进行了测试,效果很好。
@Autowired
void printServiceInstance(QuestionRepository questions, OptionRepository options) {
Question question = new Question();
questions.save(question);
question.add(new Option(-1L, question));
question.add(new Option(-2L, question));
question.add(new Option(-3L, question));
question.add(new Option(-4L, question));
Map<Long, Long> idMap = new HashMap<>();
question.getOptions().stream()
.filter(option -> option.getId() < 0)
.forEach(option -> idMap.put(option.getId(), options.save(option).getId()));
System.out.println(idMap);
}
控制台输出: {-1 = 2,-2 = 3,-3 = 4,-4 = 5}
<强>更新:强> 或者如果前端只控制选项的顺序,并根据未保存选项的顺序获取新的ID,则将是更好的代码样式。
方法强>
@Column(name = "order_num")
private Integer order;
public Option(Long id, Integer order, Question question) {
this.id = id;
this.question = question;
this.order = order;
}
更新示例:
@Autowired
void printServiceInstance(QuestionRepository questions, OptionRepository options) {
Question question = new Question();
Question merged = questions.save(question);
merged.add(new Option(-1L, 1, merged));
merged.add(new Option(-2L, 2, merged));
merged.add(new Option(-3L, 3, merged));
merged.add(new Option(-4L, 4, merged));
questions.save(merged);
System.out.println(questions.findById(merged.getId()).get().getOptions());//
}
控制台输出: [选项[id = 2,订单= 1],选项[id = 3,订单= 2],选项[id = 4,订单= 3],选项[id = 5,订单= 4]]
请注意,不需要地图来控制新的ID,前端应该通过选项的顺序来获取它。
答案 1 :(得分:0)
您可以在Question
和Option
课程中创建其他字段,并将其标记为@Transient
,以确保其不会保留。
class Question {
....
private String id; // actual data field
@Transient
private String tempId;
// getter & setter
}
最初,当UI发送数据时,请设置tmpId
并保留您的对象。成功操作后,id
将具有实际的id值。现在,让我们创建映射(tmpId - &gt; actualId)。
Map<String, String> mapping = question.getOptions().stream()
.collect(Collectors.toMap(Option::getTmpId, Option::getId, (first, second) -> second));
mapping.put(question.getTmpId(), question.getId());
由于您只想要新创建的对象,我们可以通过两种方式实现。在创建映射时添加过滤器或稍后删除。
如上所述,在第一次保存后,UI将使用实际Id更新tmpId,在下次更新时,您将得到一个混合(实际已经保存,而tempId用于新创建)。如果已经保存,tmpId和actualId将是相同的。
mapping.entrySet().removeIf(entry -> entry.getKey().equals(entry.getValue()));
关于您的控制器代码,您在添加新选项之前清除所有现有选项。如果您正在获取已填充了id(实际)字段的Question Object,则可以直接保留它。它不会影响任何事情。如果它有一些变化,那将是持久的。
关于您的控制器代码,正在清除
question.getOptions().clear();
在此之后,您只需添加新选项。
question.setOptions(requestDTO.getOptions());
question = questionRepo.save(question);
我希望现在有所帮助。
答案 2 :(得分:-1)
那么,您需要区分BE生成的FE生成的ID吗? 你可以
在任何情况下,在混合两个ID生成器时要小心碰撞。