Hibernate:ManyToMany 反向删除
Posted
技术标签:
【中文标题】Hibernate:ManyToMany 反向删除【英文标题】:Hibernate: ManyToMany inverse Delete 【发布时间】:2013-05-24 21:44:02 【问题描述】:我在 User 和 Keyword 这两个表之间存在多对多关系。用户是关系的所有者。如果我删除一个用户,我首先从这个用户中删除所有关键字,然后删除用户。这按预期工作。
但我不知道如何删除一个关键字并自动删除与所有用户的关系。
这是我目前的代码。
@实体 @Table(name = "用户") 公共类用户 @ID @Column(name = "id") @GeneratedValue 私人整数 id; @Column(名称 = "名称") 私有字符串名称; @ManyToMany(级联 = CascadeType.ALL,获取 = FetchType.EAGER) @Fetch(值 = FetchMode.SUBSELECT) @JoinTable(name = "user_has_keyword", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "keyword_id")) 私有列表关键字 = new ArrayList(); // getter 和 setter ... @实体 @Table(name = "关键字") 公共类关键字 @ID @Column(name = "id") @GeneratedValue 私人整数 id; @Column(name = "关键字") 私有字符串关键字; @ManyToMany(mappedBy = "keywords") 私有列表用户 = new ArrayList(); // getter 和 setter ... @Service("我的服务") @Transactional("事务管理器") 公共类我的服务 @Resource(name = "sessionFactory") 私人 SessionFactory 会话工厂; @SuppressWarnings("未选中") 公共列表 getAllUsers() 会话会话 = this.sessionFactory.getCurrentSession(); 查询查询 = session.createQuery("FROM User"); 返回查询.list(); 公共用户getUser(整数id) 会话会话 = this.sessionFactory.getCurrentSession(); return (User) session.get(User.class, id); 公共无效添加用户(用户用户) 会话会话 = this.sessionFactory.getCurrentSession(); 会话.保存(用户); 公共无效删除用户(用户用户) 会话会话 = this.sessionFactory.getCurrentSession(); // 1、删除关系 user.getKeywords().clear(); 会话.更新(用户); // 2、删除用户对象 会话.删除(用户); 公共关键字getKeyword(整数id) 会话会话 = this.sessionFactory.getCurrentSession(); return (Keyword) session.get(Keyword.class, id); 公共关键字addKeyword(关键字关键字) 会话会话 = this.sessionFactory.getCurrentSession(); session.save(关键字); 返回关键字; 公共无效删除关键字(关键字关键字) 会话会话 = this.sessionFactory.getCurrentSession(); // 1、删除关系 关键字.getUsers().clear(); session.update(关键字); // 2、删除用户对象 关键字 = getKeyword(keyword.getId()); session.delete(关键字); @控制器 公共类 MyController @Resource(name = "我的服务") 私人的我的服务我的服务; @RequestMapping(value = "/add", method = RequestMethod.GET) 公共字符串添加(模型模型) 关键字 k = new Keyword(); k.setKeyword("黄色"); k = myService.addKeyword(k); 用户 u1 = 新用户(); u1.setName("巴特"); u1.getKeywords().add(k); myService.addUser(u1); 用户 u2 = 新用户(); u2.setName("丽莎"); u2.getKeywords().add(k); myService.addUser(u2); 返回 ”/”; @RequestMapping(value = "/delete/user", method = RequestMethod.GET) 公共字符串删除用户(模型模型) 用户 u = myService.getUser(1); myService.deleteUser(u); 返回 ”/”; @RequestMapping(value = "/delete/keyword", method = RequestMethod.GET) 公共字符串删除关键字(模型模型) 关键字 k = myService.getKeyword(1); myService.deleteKeyword(k); 返回 ”/”;如果我浏览到 /delete/keyword,我会收到以下异常:
org.hibernate.LazyInitializationException:未能延迟初始化角色集合:com.blabla.prototype.Keyword.users,没有会话或会话已关闭我用谷歌搜索并尝试了许多不同的方法,但没有任何效果。
感谢您的帮助。
非常感谢,
马可
【问题讨论】:
【参考方案1】:LazyInitializationException 与删除无关。您正在控制器中加载关键字。这使服务加载关键字,而无需初始化其惰性用户列表。然后将该关键字返回给控制器,并提交事务,并关闭会话,使关键字与会话分离。
然后您将这个分离的关键字传递给服务以将其删除。该服务因此收到一个分离的关键字,并尝试访问其用户列表。由于关键字已分离且尚未加载用户列表,因此会导致 LazyInitializationException。
服务方法应该将要删除的关键字的ID作为参数,加载它,从而使用附加的关键字,然后继续删除。
现在回答您的问题,您对用户删除是正确的:您从要删除的用户中删除所有关键字,因为用户是关联的所有者。删除关键字时应用相同的逻辑:从所有引用它的用户中删除该关键字,然后删除该关键字:
public void deleteKeyword(Integer id)
Keyword keyword = getKeyword(id);
for (User user : keyword.getUsers())
user.removeKeyword(keyword);
session.delete(keyword);
请注意,在处理附加实体时,您不必调用 update()。附加实体的状态会自动透明地保存到数据库中。
【讨论】:
非常感谢您的详细解释和帮助!在 removeKeyword 方法中,我需要检查用户是否持有关键字:this.keywords.contains(keyword)。对于这个包含检查,我需要覆盖 equals 方法?!如果我有数千个用户,包含方法是不是太慢了?我想有一个更优雅的解决方案。 ;) 要明确一点:我不认为,您的解决方案并不优雅,只是我认为与“包含”方法有关的事情似乎并不优雅。我想,您知道比我的“包含”方法更好的解决方案。 ;) 不,您不必重写 equals 方法。而且您也不必检查列表是否包含关键字:您知道它包含它。只需将其从列表中删除即可。我对 Hibernate 的测试表明,删除用户(所有者方)将自动删除与其所有关键字的关联,但删除关键字(反方)不会自动删除与其所有用户的关联。我什至不确定这是标准行为。另一种方法是使用从连接表中删除的本机查询。 好的。是的,这是真的,我不必检查它。非常感谢您的帮助和详细信息!以上是关于Hibernate:ManyToMany 反向删除的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate @ManyToMany 单向关系[重复]
Spring Boot ManyToMany - *** - JPA,Hibernate