Hibernate One to Many добавить новых детей

Прочитал много статей и ответов на подобные вопросы, но так и не могу понять.

Здесь мой родительский класс:

@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;
}

Здесь мы видим два набора - Предметы и Друзья пользователя.

Пример класса UserItemId:

@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);
    }
}

И теперь я пытаюсь добавить новый элемент пользователю:

/* 
'user' in this code i got from SecurityContext in controller 
and I give it as parameter from controller to method (service layer) which can add new Item to user. 
So user it is entity which already exists in database - it is logged user 
*/
UserItem userItem = new UserItem();
userItem.setUser(user);
userItem.setItemShop(ItemShop);

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

Но я получил ошибку:

org.hibernate.NonUniqueObjectException: другой объект с тем же значением идентификатора уже был связан с сеансом

Я понимаю, что основная проблема заключается в CascadeTypes - ВСЕ это не лучший вариант для этого, потому что спящий режим не может делать то, что я хочу - понять, что пользователь является постоянным объектом, и просто добавить в постоянный новый элемент и сохранить его в базе данных. Поэтому я хочу спросить вас, как лучше всего победить в этой ситуации, когда у меня есть родительский класс (пользователь) и дочерние элементы (элементы), и я хочу добавить новые элементы и удалить элементы из родительского и сохранить их в базу данных. Действительно, я нашел рабочий метод (используя итератор), но я понимаю, что на проекте с высокой нагрузкой это худший вариант make for loop для таких операций. Я действительно хочу найти лучшие практики для таких ситуаций.

ОБНОВЛЕНИЕ ВОПРОСА: я допустил ошибку. Когда я вызываю код, который выполняет добавление дочернего элемента в набор (код выше) - все хорошо - дочерний элемент добавляется в базу данных и в сущность, но когда я пытаюсь удалить дочерний элемент:

Set<UserItem> userItems = user.getUserItems();
UserItem userItem = userDAO.findUserItem(user,  itemShop);
userItems.remove(userItem);
session.saveOrUpdate(user);
session.flush();

Затем я получил исключение:

org.hibernate.NonUniqueObjectException: другой объект с тем же значением идентификатора уже был связан с сеансом

ЕЩЕ ОДНО ОБНОВЛЕНИЕ:

После многих экспериментов я получил рабочий код добавления элемента:

UserItem userItem = new UserItem();
userItem.setUser(user);
userItem.setItemShop(ItemShop);

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

Это код метода на уровне службы, который добавляет элемент пользователю. И это работает. И у меня есть полурабочий код для удаления элемента от пользователя:

       Set<UserItem> userItems = user.getuserItems();
UserItem userItem = UserDAO.findUserItem(user, itemShop);
userItems.remove(userItem);
userDAO.merge(user);

Это не правильно работает код. У нас есть ситуация - у пользователя есть 3 элемента. Удаляю один пункт - все нормально. Позже я пытаюсь удалить еще один элемент, и спящий режим говорит мне:

ObjectNotFoundException: строки с данным идентификатором не существует

Это означает, что в кеше все еще 3 элемента - тот, который я удаляю первым - все еще в кеше, и спящий режим пытается добавить его в базу данных, но он удаляется.

И у меня появилась еще одна мысль, которая может помочь нам ответить на этот вопрос. Пользователь объекта - где я пытаюсь удалить дочерний элемент - это зарегистрированный пользователь из SecurityContext - я получил его от контроллера - у контроллера есть аннотация, где пользователь получил от безопасности. Итак, спящий режим уже имеет в кеше зарегистрированного пользователя (объект пользователя). Есть идеи?


person dikkini    schedule 14.06.2014    source источник


Ответы (1)


OP должен вручную управлять тем, что фактически является таблицей соединений, потому что в таблице соединений есть дополнительные поля, которыми также необходимо управлять. Это вызывает проблемы при попытке удалить объект элемента у пользователя.

Это стандартный способ, которым я устанавливаю двунаправленную связь «многие ко многим», когда мне нужно явно управлять объединением или у меня есть дополнительная информация, прикрепленная к объединению. Если у меня нет дополнительной информации или мне не нужно самому управлять объединением, просто используйте аннотацию @JoinTable.

Новые объекты:

@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 userUUID;

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

    @OneToMany(mappedBy="user", cascade = CascadeType.ALL, orphanRemoval=true)
    private Set<UserItem> items = new HashSet<UserItem>();
}

Объект предмета:

@Entity
@Table(name="items")
    public class Item implements Serializable{
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
    @Column(name = "uuid", unique = true)
    protected String itemUUID;

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

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

    @OneToMany(mappedBy="item", cascade = CascadeType.ALL, orphanRemoval=true)
    private Set<UserItem> users = new HashSet<UserItem>();

Класс UserItem:

@Entity
@Table(name = "user_item")
public class UserItem implements Serializable {

   //Generate this like you are doing with your others.
   private String userItemUUID;

   @ManyToOne
   @JoinColumn(name="userUUID")
   private User user;

   @ManyToOne
   @JoinColumn(name="itemUUID")
   private Item item;

   private BigDecimal moneyCollect;

   private Date dateAdded = new Date();
}

Ваш сервис для удаления выглядит так:

UserItem userItem = userDAO.findUserItem(user, itemShop);
user.getUserItems().remove(userItem);
session.saveOrUpdate(user);
session.flush();

Вы можете сделать это и по-другому.

UserItem userItem = userDAO.findUserItem(user, itemShop);
item.getUserItems().remove(userItem);
session.saveOrUpdate(item);
session.flush();

Исключение, которое вы получаете, вызвано тем, что Hibernate находит два объекта UserItem в методе, которые являются постоянными и не знают, какой из них удалить.

//Create one UserItem instance here
Set<UserItem> userItems = user.getUserItems();
//Create another UserItem instance here
UserItem userItem = userDAO.findUserItem(user,  itemShop);
userItems.remove(userItem);

//Hibernate doesn't know which UserItem instance to remove, and throws an exception.
session.saveOrUpdate(user);
session.flush();

Если что-то из этого не имело смысла, дайте мне знать.

ДОБАВЛЕНИЕ: ОП получал постоянный экземпляр пользователя внутри своего контроллера и ничего с ним не делал. Это вызывало NonUniqueObjectException, который выбрасывал Hibernate.

person JamesENL    schedule 16.06.2014
comment
Я не пытался добавить элемент пользователя, а затем сразу же удалить другой. Я показываю вам код метода, который полностью добавляет элемент, и другой метод, который удаляет. Итак, я могу добавить элемент - код работает, но я не могу удалить элемент. Я обновлю свой вопрос. Проверь это. - person dikkini; 16.06.2014
comment
Вы пробовали вторую половину моего ответа, которая конкретно касается вашей проблемы с удалением? - person JamesENL; 16.06.2014
comment
Конечно, orphanDelete — это первое, что я проверил перед вопросом на StackOverFlow. - person dikkini; 16.06.2014
comment
не только orphanDelete, часть, где я говорю о том, почему вы получаете NonUniqueObjectException. - person JamesENL; 16.06.2014
comment
эмм, извини, но я действительно не понимаю, о какой именно части ты говоришь, пожалуйста, покажи мне это в ответе - person dikkini; 16.06.2014
comment
Смотрите мое обновление. Какая часть точно неясна, или она вызывает больше исключений. - person JamesENL; 16.06.2014
comment
Я вижу в вашем ответе код для добавления элемента (этот код работает правильно, я ошибся, когда пишу, что этот код дает мне исключение) и код для удаления элемента - и оба блока кода выглядят как мои (без изменений). Без изменений. Я понимаю, что Hibernate не может понять, какой экземпляр UserItem он должен удалить, но я не знаю, что мне делать. Если ты тебя понял & - person dikkini; 16.06.2014
comment
В своих блоках кода я не создаю экземпляры объектов коллекции напрямую. Я не объявляю отдельный объект Set<UserItem>. Я просто манипулирую тем, что уже существует внутри вашего объекта User. - person JamesENL; 16.06.2014
comment
О, я понимаю. Сейчас проверю, но я всегда думаю, что Java работает со ссылками на объекты, а не с объектами. Итак, когда я отдельно объявляю Set‹UserItem›, то это просто для удобства работы, я не стал создавать еще один экземпляр... я проверю. - person dikkini; 16.06.2014
comment
Но когда вы объявляете Set<UserItem> userItems = user.getuserItems();, вы создаете совершенно новый объект, который вы можете изменить, не затрагивая первый. - person JamesENL; 16.06.2014
comment
user.getSmartUserGifts().remove(smartUserGift); - не помогло... =( - person dikkini; 16.06.2014
comment
Присоединяйтесь ко мне здесь: chat.stackoverflow.com/rooms/ 44929/ - person JamesENL; 16.06.2014
comment
Все работает как шарм, но я забываю. В моем объекте UserItem есть дополнительные поля, когда вы спросили меня о полях, я дал вам не все поля... не знаю, почему я это сделал. - person dikkini; 16.06.2014