D
D
dikkini2014-06-15 17:45:31
Java
dikkini, 2014-06-15 17:45:31

Hibernate, OneToMany Cascading adding/removing children

There is a User class:

@Entity
@Table(name = "users")
public class User implements Serializable, Annotation {

    @Id
    @GeneratedValue(generator = "system-uuid", strategy = GenerationType.IDENTITY)
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
    @Column(name = "uuid", unique = true)
    protected String uuid;

    @Column(name = "username")
    protected String username;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user", cascade = CascadeType.ALL)
    private Set<UserItem> userItems;
}

He has Set children.
UserItem class:
@Entity
@Table(name = "user_item")
@AssociationOverrides({
        @AssociationOverride(name = "pk.user",
                joinColumns = @JoinColumn(name = "user_uuid")),
        @AssociationOverride(name = "pk.ItemShop",
                joinColumns = @JoinColumn(name = "item_shop_uuid")) })
public class UserItem implements Serializable {

    @EmbeddedId
    protected UserItemId pk = new UserItemId();

    @Transient
    public User getUser() {
        return getPk().getUser();
    }

    public void setUser(User user) {
        getPk().setUser(User);
    }

    @Transient
    public ItemShop getItemShop() {
        return getPk().getItemShop();
    }

    public void setItemShop(ItemShop ItemShop) {
        getPk().setItemShop(ItemShop);
    }
}

The useritem set has the type of cascading operations "ALL" - that is, all changes to the fields of the User class will also be reflected in its children. HIbernate will go through them and apply all changes.
We execute the code for adding a baby:
UserItem userItem = new UserItem();
userItem.setUser(user);
userItem.setItemShop(ItemShop);

user.getUserItems().add(userItem);
session.saveOrUpdate(user);
session.flush();

The baby will be added to both the user object and the database. If you execute merge - the baby will be added only to the entity - which is correct.
Removing a baby:
Set<UserItem> userItems = user.getUserItems();
UserItem userItem = userDAO.findUserItem(user,  itemShop);
userItems.remove(userItem);
session.saveOrUpdate(user);
session.flush();

We get an exception:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.test.User#2d0767d8-effb-41f2-8910-5729a460527d]

What am I doing wrong? Where is the exception from?
UPDATED:
Got the following working code to remove:
User user = UserDetails.getUser();
        Set<UserItem> userItems = user.getUserItems();
        UserItem userItem = UserDAO.findUserItem(user, itemShop);
        UserItems.remove(UserItem);
        session.merge(user);
        session.flush();
        session.clear();

Yes, but the code does not work cleanly, but the essence is the following, the user has 3 Item, I delete the first - merge - everything is OK, I delete the second - merge - the second is deleted and an exception falls out, they say I can’t add a row - there is no such entry, that is, where then in the cache there was a record about the first item that I deleted the first one and hibernate tried to write it to the database - which is not correct in principle, and in general merge is not a solution to my problem.
Also I got a little more understanding on the first Exception'y:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.test.User#2d0767d8-effb-41f2-8910-5729a460527d]

The whole point is that the user object is a logged in user obtained using my own annotation, which I hung on the controller. That is, in the controller there is an annotation that is obtained by the logged in user, it is natural that it receives it using hibernate (not explicitly, of course, it will take a long time to explain, and the code to be shown will deviate from the topic of the question), and so, I pass the received user object from the controller to method of removing a child from a parent. That is, it turns out that hibernate sees the same object with 3 children in the persistent session, and here I slip it another object with 2 children already, and for some wild reason they are different !!! I tried once again to pull the user out of the database and delete the baby from him - it was deleted, but only the entity that represents the logged user remained unchanged.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vladimir Smirnov, 2014-06-16
@bobzer

It seems that the error occurs due to unnecessary manipulations with entities. Look at the contents of the session under debugging, it collects a list of all objects to be saved when flush / commit is called. Most likely, in addition to the user entity that is clearly visible in the code, at some point (perhaps userDAO.findUserItem ?) another instance of User appears in the session, for example, in UserItemId. You need to ensure that all relationships refer to the same object instance. To do this, try to perform all operations in one session, i.e., if one session is already open in the code execution thread, do not open other sessions (in userDAO.findUserItem ?) until the current session is closed.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question