Beginner
Beginner

Reputation: 1740

JPA - update entity with one to many relationship array list

Supposed I'm updating my user entity which has a one to many relationship with post

this is the code for update

public User updateUser(Long userid, Userdto userdto) {
    User user = findByTemplatesFooterNo(userid);
    User mappedUser = modelmapper.map(UserDto, User.class);

    user.setName(mappedUser.getName());
    user.setAge(mappedUser.getAge());

    user.getPosts().clear();
    user.getPosts().addAll(mappedUser.getPost());

    userrepository.save(user);
}

this is the current data of User

{
  "user_id" : 1,
  "name" : "testname",
  "age" : "testage",
  "posts" : [
       {
            "post_id" : 1,
            "title" : "titlepost",
            "content" : "content"
       },
       {
            "post_id" : 2,
            "title" : "titlepost",
            "content" : "content"
       },
  ]
}

this is what I pass on request body to update the user with an Id of 1

{
  "name" : "testname",
  "age" : "testage",
  "posts" : [
       {
            "title" : "titlepost",
            "content" : "content"
       }    
  ]
}

I can successfully update the user and the response is

{
  "user_id" : 1,
  "name" : "testname",
  "age" : "testage",
  "posts" : [
       {
            "post_id" : 3,
            "title" : "titlepost",
            "content" : "content"
       }    
  ]
}

It replaces all the current post. What I want is just to append the new so that the response will become

{
      "user_id" : 1,
      "name" : "testname",
      "age" : "testage",
      "posts" : [
           {
                "post_id" : 1,
                "title" : "titlepost",
                "content" : "content"
           },
           {
                "post_id" : 2,
                "title" : "titlepost",
                "content" : "content"
           },
           {
                "post_id" : 3,
                "title" : "titlepost",
                "content" : "content"
           },
      ]
}

User entity

 @OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            fetch = FetchType.LAZY
 )
 @JoinColumn(
            name = "user_id",
            foreignKey = @ForeignKey(name = "fk_post_id"),
            referencedColumnName = "userId"
 )
 private List<Post> post = new ArrayList<>();

Upvotes: 1

Views: 14247

Answers (2)

Alex Mig
Alex Mig

Reputation: 1

user.getPosts().addAll(mappedUser.getPost());

As far I know Hibernate recommends using Collection interface for one to many relations. in this way method, addAll will not merge existing posts elements with the news. As a result, you will have duplicate posts in the collection. addAll will work only in case if you 100% sure that mappedUser contains only new elements. But if it is not true then you will need to merge manually or clear previous collection to avoid duplication. Clear collection is not recommended. Because clear means that hibernate will delete all records and then create it again.

Only in case you choose Set like collection your way will works. But it is not recommended as well, because of set require to calculate hashcode. To do it hibernate will need to get all records from a database. Imagine a situation you have 3000 posts for one user. And you add a new one to set... Hibernate will need to get all 3000 posts to be sure that new one is unique.

Upvotes: 0

Alan Hay
Alan Hay

Reputation: 23226

You explicitly call user.getPosts().clear() and yet you are surprised that only the new post exists?

You also need to be aware that userrepository.save(user) returns the merged result and you need to return that from your method to have an up-to-date representation serialized to JSON.

https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html?is-external=true#save-S-

Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.

Changes:

public User updateUser(Long userid, Userdto userdto) {
    User user = findByTemplatesFooterNo(userid);
    User mappedUser = modelmapper.map(UserDto, User.class);

    user.setName(mappedUser.getName());
    user.setAge(mappedUser.getAge());

    //user.getPosts().clear(); <-- WHY?
    user.getPosts().addAll(mappedUser.getPost());

    return userrepository.save(user); // <-- return the result of the merge
}

Upvotes: 2

Related Questions