Farid327
Farid327

Reputation: 98

Spring Data Update Many To One Property

I have an problem with saving Many-To-One Field in Spring Data JPA. Consider Entity User and Group as shortly described below :

    @Entity
    @Table(name = "Users")
    public class User {
        @Basic(optional = false)
        @Column(name = "username")
        private String username;

        @JoinColumn(name = "group_id", referencedColumnName = "id")
        @ManyToOne
        private Group group;
    }

    @Entity
    @Table(name = "groups")
    public class Group {
       @Basic(optional = false)
       @Column(name = "name")
       private String name;

       @OneToMany(mappedBy = "group",cascade=CascadeType.PERSIST)
       private Collection<User> userCollection;
    }

when i want to Update User Entity and change its group with CrudRepository save method following exception occured : org.hibernate.HibernateException: identifier of an instance of Group was altered from 2 to 1 and that shows spring data want to edit Group's id field while it is not corrent and i want to change reference of it not its id.

For Update i get view data in a DTO form and after that i convert dto object to entity object using Dozer Mapper as described below:

DozerBeanMapper mapper = new DozerBeanMapper();
// get user by id from database for editing
User user = this.userService.findByIdAndDeletedFalse(form.getId());
// merge view data and entity data using Dozer
mapper.map(form, user);
// save User entity
this.userService.save(user);

userService is a bean that call crudRepository save method only.

Any Solution?

Thanks

Upvotes: 4

Views: 6099

Answers (3)

ksadjad
ksadjad

Reputation: 603

It's because you are using a transient Group. You need to fetch your group and then assign the group to your user object.

Upvotes: 0

alayor
alayor

Reputation: 5025

I think you should tell Dozer to exclude group field using the following xml.

<field-exclude> 
  <a>group</a> 
</field-exclude>   

And the set the group manually.

mapper.map(form, user);
user.setGroup(this.groupService.finfById(form.group.getId()));
// save User entity
this.userService.save(user);

If you don't want to map the group in your controller. You could use a DozerConverter class where you can fetch the group object and set it there.

<mapping>
  <class-a>org.mypackage.Form</class-a>
  <class-b>org.mypackage.User</class-b>    
  <field custom-converter-id="org.mypackage.UserGroupCustomConverter">
    <a>groupId</a>
    <b>group</b>
  </field>
</mapping>  

public class UserGroupCustomConverter extends DozerConverter<Form, User> {

  public NewDozerConverter() {
    super(Form.class, User.class);
  }

  public Boolean convertTo(Form form, User user) {
     user.setGroup(this.groupService.finfById(form.group.getId()));
  }
}

Upvotes: 0

crizzis
crizzis

Reputation: 10716

The error occurs because you are trying to change the id of a tracked entity, which is not allowed in JPA. You need to obtain a reference to the new Group and assign it to User.group.

In short, instead of this:

user.getGroup().setId(newId);

Try this:

user.setGroup(entityManager.getReference(Group.class, newId)); 

Upvotes: 2

Related Questions