Quang Huy
Quang Huy

Reputation: 59

Column lost their value and turned into null after using update function

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 POST Question and Answer to database

PUT 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

Answers (3)

Quang Huy
Quang Huy

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

Zeromus
Zeromus

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

v.ladynev
v.ladynev

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

Related Questions