Reputation: 59
I am currently implementing CRUD functions but when I try the update function, the database added new entity instead of editing the existing one.
My database consist of 2 models "Questions" and "Answers". One "question" can have multiple "answers" but one "answer" can only linked to one "question".
My test run was adding Question content and an array of Answer contents. Checking the database, both of them are linked together through a column called "question_id" in Answer table. But when I am update the body of the Question (both the content of Question and Answer inside Question body), the database doesn't know their original "question_id" anymore.
I felt like I did wrong somewhere. Any ideas?
POST Question and Answer to database
PUT Question and Answer to database
Question model
@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column
private String content;
@OneToMany(mappedBy = "question")
@Cascade(CascadeType.ALL)
@Fetch(FetchMode.JOIN)
private Set<Answer> answers;
Answer model
@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column
private String content;
@ManyToOne
@JsonIgnore
private Question question;
private boolean isCorrectAnswer;
QuestionService
@Transactional
public int addQuestion(Question question){
for (Answer answer: question.getAnswers()) {
answer.setQuestion(question);
}
int id = (Integer) sessionFactory.getCurrentSession().save(question);
return id;
}
@Transactional
public void updateQuestion(Question question){
sessionFactory.getCurrentSession().saveOrUpdate(question);
}
QuestionController
@RequestMapping(path = "/questions", method = RequestMethod.PUT)
public int updateQuestion(@RequestBody Question question){
questionService.updateQuestion(question);
return question.getId();
}
Upvotes: 2
Views: 167
Reputation: 59
Solved by myself by fixing in QuestionService
First
@Autowire
private QuestionService questionService;
Then instead of editing only Question like this
public Question updateQuestion(Question question){
sessionFactory.getCurrentSession().saveOrUpdate(question);
return question;
}
I am using updateAnswer from AnswerService into this update so everytime I wanted to update Question or Answer or both, Answer can known their updated content and their question_id. This will solve the problem as my updated Answer doesn't know their QuestionID
public Question updateQuestion(Question question){
Set<Answer> answers = question.getAnswers();
for (Answer a : answers) {
a.setQuestion(question);
questionService.updateAnswer(a);
}
question.setAnswers(answers);
sessionFactory.getCurrentSession().saveOrUpdate(question);
return question;
}
So overall, this update function update both Question and Answer, the naming scheme does not fit so I can improve this, something like QuestionAnswerService
Upvotes: 0
Reputation: 4532
When entering the update method you supply Question
as argument.
But this Question
is an unmanaged entity because you built it in the controller outside of any transaction, so when you call saveOrUpdate()
what happens is that hibernate thinks this is a new entity and save it accrodingly with a new autogenerated id.
what you need to do is retrieve the entity in the transactional block with a query, that way the entity is already managed and will be dirty checked, thus creating the correct update statement
@Transactional
public void updateQuestion(//Parameter for the query and updated field){
Query query = sessionFactory.getCurrentSession().createQuery("from Question where etc...);
Question question = query.uniqueResult();
question.setContent(//Your updated content);
sessionFactory.getCurrentSession().saveOrUpdate(question);
}
Upvotes: 1
Reputation: 19956
My advice is to separate persistent classes (used by Hibernate) and classes to serialize/deserialize JSON
. Better to use a separate DTO
class for each persistent class — Question -> QuestionDto
And to convert between them.
To PUT
Question
Use this JSON
{
"id": 1,
"answers": [
{
"id" : 100,
"content": "this is an updated answer"
},
{
"content": "this is a new answer"
}
]
}
Do this
for (Answer answer: question.getAnswers()) {
answer.setQuestion(question);
}
And use session.merge()
.
Upvotes: 1