Use Case: A user can CRUD multiple choice questions using a single page web application written in JavaScript.
201 CREATED
containing a map temporary id -> backend id to update its ids.How should we implement the counterpart for the last part (5-7 adding an option) on the backend side?
I try this, but I cannot get the child ids after persistence.
Entities
@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;
}
// ...
}
Controller
@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);
}
}
In the controller I marked the Problem: e.getValue().getId() is null
How should such a controller create the idMap?
It would be best if you save each option individually and then save the generated Id on the map.
I did the test below and it works perfectly.
@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);
}
Console out: {-1=2, -2=3, -3=4, -4=5}
UPDATED: Or will be a better code style if the front end just control de order of the options, and get the new ids based on the order of the unsaved options.
Option:
@Column(name = "order_num")
private Integer order;
public Option(Long id, Integer order, Question question) {
this.id = id;
this.question = question;
this.order = order;
}
Update example:
@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());//
}
Console out: [Option [id=2, order=1], Option [id=3, order=2], Option [id=4, order=3], Option [id=5, order=4]]
Note there is no need of a map to control the new ids, the front-end should know by get it by the order of the options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With