2013-05-29 3 views
1

У меня есть отношение ManyToMany между двумя таблицами, User и Ключевое слово. Пользователь является владельцем отношений. Если я удалю Пользователь, я удаляю сначала все Ключевые словаПользователь, а затем удаляет Пользователь. Это работает так, как ожидалось.Hibernate: ManyToMany inverse Удалить

Но я не знаю, как удалить слова и автоматически удалять отношения ко всем пользователей.

Вот мой код.

 

    @Entity 
    @Table(name = "user") 

    public class User { 

     @Id 
     @Column(name = "id") 
     @GeneratedValue 
     private Integer id; 

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

     @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) 
     @Fetch(value = FetchMode.SUBSELECT) 
     @JoinTable(name = "user_has_keyword", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "keyword_id")) 
     private List keywords = new ArrayList(); 

     // Getters and setters 
     ... 
    } 

 

    @Entity 
    @Table(name = "keyword") 
    public class Keyword { 

     @Id 
     @Column(name = "id") 
     @GeneratedValue 
     private Integer id; 

     @Column(name = "keyword") 
     private String keyword; 

     @ManyToMany(mappedBy = "keywords") 
     private List users = new ArrayList(); 

     // Getters and setters 
     ... 
    } 

 

    @Service("myService") 
    @Transactional("transactionManager") 
    public class MyService { 

     @Resource(name = "sessionFactory") 
     private SessionFactory sessionFactory; 

     @SuppressWarnings("unchecked") 
     public List getAllUsers() { 
     Session session = this.sessionFactory.getCurrentSession(); 

     Query query = session.createQuery("FROM User"); 

     return query.list(); 
     } 

     public User getUser(Integer id) { 
     Session session = this.sessionFactory.getCurrentSession(); 
     return (User) session.get(User.class, id); 
     } 

     public void addUser(User user) { 
     Session session = this.sessionFactory.getCurrentSession(); 
     session.save(user); 
     } 

     public void deleteUser(User user) { 
     Session session = this.sessionFactory.getCurrentSession(); 

     // 1st, delete relations 
     user.getKeywords().clear(); 
     session.update(user); 

     // 2nd, delete User object 
     session.delete(user); 
     } 

     public Keyword getKeyword(Integer id) { 
     Session session = this.sessionFactory.getCurrentSession(); 
     return (Keyword) session.get(Keyword.class, id); 
     } 

     public Keyword addKeyword(Keyword keyword) { 
     Session session = this.sessionFactory.getCurrentSession(); 
     session.save(keyword); 

     return keyword; 
     } 

     public void deleteKeyword(Keyword keyword) { 
     Session session = this.sessionFactory.getCurrentSession(); 

     // 1st, delete relations 
     keyword.getUsers().clear(); 
     session.update(keyword); 

     // 2nd, delete User object 
     keyword = getKeyword(keyword.getId()); 
     session.delete(keyword); 
     } 
    } 

 

    @Controller 
    public class MyController { 

     @Resource(name = "myService") 
     private MyService myService; 

     @RequestMapping(value = "/add", method = RequestMethod.GET) 
     public String add(Model model) { 

     Keyword k = new Keyword(); 
     k.setKeyword("yellow"); 
     k = myService.addKeyword(k); 

     User u1 = new User(); 
     u1.setName("Bart"); 
     u1.getKeywords().add(k); 
     myService.addUser(u1); 

     User u2 = new User(); 
     u2.setName("Lisa"); 
     u2.getKeywords().add(k); 
     myService.addUser(u2); 

     return "/"; 
     } 

     @RequestMapping(value = "/delete/user", method = RequestMethod.GET) 
     public String deleteUser(Model model) { 

     User u = myService.getUser(1); 
     myService.deleteUser(u); 

     return "/"; 
     } 

     @RequestMapping(value = "/delete/keyword", method = RequestMethod.GET) 
     public String deleteKeyword(Model model) { 

     Keyword k = myService.getKeyword(1); 
     myService.deleteKeyword(k); 

     return "/"; 
     } 

    } 

Если я просматриваю к /удалить/ключевое слово я получаю следующее исключение:

 

    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.blabla.prototype.Keyword.users, no session or session was closed 

Я гугл и попробовал много разных вещей, но ничего не работает.

Я ценю любую помощь.

Большое спасибо,

Marco

ответ

1

LazyInitializationException не имеет ничего общего с удалением. Вы загружаете ключевое слово в свой контроллер. Это заставляет сервис загружать ключевое слово без инициализации его ленивого списка пользователей. Это ключевое слово затем возвращается контроллеру, и транзакция фиксируется, и сеанс закрывается, что делает ключевое слово отключенным от сеанса.

Затем вы передаете это отделимое ключевое слово службе, чтобы удалить его. Таким образом, служба получает отдельное ключевое слово и пытается получить доступ к своему списку пользователей. Поскольку ключевое слово отключено, а список пользователей еще не загружен, это вызывает исключение LazyInitializationException.

Метод службы должен взять идентификатор ключевого слова для удаления в качестве аргумента, загрузить его и, следовательно, работать с прикрепленным ключевым словом, а затем продолжить удаление.

Теперь, чтобы ответить на ваш вопрос, вы получили право на удаление пользователя: вы удаляете все ключевые слова пользователя для удаления, поскольку пользователь является владельцем ассоциации. Примените ту же логику при удалении ключевого слова: удалить ключевое слово из всех пользователей ссылаться на него, и удалить ключевое слово:

public void deleteKeyword(Integer id) { 
    Keyword keyword = getKeyword(id); 
    for (User user : keyword.getUsers()) { 
     user.removeKeyword(keyword); 
    } 
    session.delete(keyword); 
} 

Обратите внимание, что вы не должны вызывать обновление() при работе с вложенными структурами. Состояние прикрепленных объектов автоматически и прозрачно сохраняется в базе данных.

+0

Благодарим вас за подробное объяснение и вашу помощь! В методе removeKeyword мне нужно проверить, имеет ли пользователь ключевое слово: this.keywords.contains (keyword). Для этого contains-check мне нужно перезаписать метод equals ?! Если у меня есть тысячи пользователей, метод contains не слишком медленный? Я думаю, что есть более элегантное решение.;) – devchzh

+0

Просто чтобы быть ясным: я не думаю, что ваше решение не изящно, только то, что я думаю, что я должен делать с методом «содержит», кажется, не изящно. Я думаю, вы знаете лучшее решение, чем мой подход «содержит». ;) – devchzh

+0

Нет, вам не нужно переопределять метод equals. И вам не нужно проверять, содержит ли список ключевое слово: вы * знаете *, он содержит его. Просто удалите его из списка. Мои тесты с Hibernate показывают, что удаление пользователя (владельца) автоматически удалит ассоциацию со всеми его ключевыми словами, но удаление ключевого слова (обратная сторона) автоматически не удалит ассоциацию со всеми его пользователями. Я даже не уверен, что это стандартное поведение. Альтернативой является использование собственного запроса, который удаляется из таблицы соединений. –

Смежные вопросы