Hibernate: ManyToMany inverse Delete

I have a ManyToMany relationship between two tables, User and Keyword. The User is the owner of the relationship. If I delete a User, I remove first all Keywords from this User and then delete the User. This works as expected.

But I don't know how to delete a Keyword and automatically delete the relations to all Users.

Here is my code so far.


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

    }

If I browse to /delete/keyword I get the following exception:


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

I've googled and tried many different things, but nothing works.

I appreciate any help.

Thank you very much,

Marco


The LazyInitializationException has nothing to do with deletion. You're loading a Keyword in your controller. This makes the service load the keyword, without initializing its lazy list of users. This keyword is then returned to the controller, and the transaction is committed, and the session is closed, making the keyword detached from the session.

Then you pass this detached keyword to the service to delete it. The service thus receives a detached keyword, and tries to access its list of users. Since the keyword is detached and the list of users hasn't been loaded yet, this causes a LazyInitializationException.

The service method should take the ID of the keyword to delete as argument, load it, and thus work with an attached keyword, and then proceed with the deletion.

Now to answer your question, you got it right for the user deletion: you remove all the keywords from the user to delete, because the user is the owner of the association. Apply the same logic when deleting a keyword : remove the keyword from all the users referencing it, and delete the keyword:

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

Note that you don't have to call update() when working with attached entities. The state of attached entities is automatically and transparently saved to the database.

链接地址: http://www.djcxy.com/p/48832.html

上一篇: Spring JPA审计为空createdBy

下一篇: 休眠:ManyToMany反删除