hibernate双向ManyToMany映射
工作需要一个双向多对多映射,照着李刚的书做了映射,碰到了一些问题,现就问题及解决方案进行总结归纳。
1、首先奉上最初代码
Person5.java
@Entity @Table(name = "person_manyandmany") public class Person5 { @Id @GeneratedValue private Long id; private String name; private int age; @ManyToMany(targetEntity = Address5.class) @JoinTable(name = "person_and_address", joinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id", nullable = false, updatable = false), inverseJoinColumns = @JoinColumn(name = "address_id", referencedColumnName = "id", nullable = false, updatable = false)) private Set<Address5> address5s = new HashSet<Address5>(); //get、set方法省略 }
Address5.java
@Entity @Table(name = "address_manyandmany") public class Address5 { @Id @GeneratedValue private Long id; private String address; @ManyToMany(targetEntity = Person5.class) @JoinTable(name = "person_and_address", joinColumns = @JoinColumn(name = "address_id", referencedColumnName = "id", nullable = false), inverseJoinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id", nullable = false)) private Set<Person5> person5s = new HashSet<Person5>(); //get、set方法省略 }
问题1:
因为我做了<property name="hbm2ddl.auto">update</property>配置,所以项目启动时报关联表重名错误。经分析时因为实体类两边都有@JoinTable注解,所以就会产生两张关联表。
解决方案1:
将Address5中name改为address_and_person,重新启动项目,项目启动不会报错。但是这种方式会创建两张关联表,是不被提倡的。
解决方案二:
Address5放弃控制权,将控制权交给Person5,代码如下(只修改Address5中代码即可):
@ManyToMany(targetEntity = Person5.class, mappedBy = "address5s") private Set<Person5> person5s = new HashSet<Person5>();
增加mappedBy = "address5s"属性,此事Address5放弃控制权交给Person5。这时启动项目不会报错,并只创建了一张关联表,另外两边都可以获取到关联对方的信息(这才是关联的重点,减少代码逻辑)。到此关联的问题解决了,添加、获取和更新信息都没有问题了。
添加信息代码
Person5 p1 = new Person5(); p1.setName("many and many 5"); Person5 p2 = new Person5(); p2.setName("many and many 6"); Address5 a1 = new Address5(); a1.setAddress("many and many address 1"); a1.getPerson5s().add(p1); Address5 a2 = new Address5(); a2.setAddress("many and many address 2"); a1.getPerson5s().add(p2); p1.getAddress5s().add(a1); p1.getAddress5s().add(a2); p2.getAddress5s().add(a2); session.save(p1); session.save(p2); session.save(a1); session.save(a2);
获取信息代码
Person5 person5 = (Person5) session.get(Person5.class, 1l); Set<Address5> address5s = person5.getAddress5s(); for(Address5 address5 : address5s) { System.out.println("person : " + person5.getName() + " | " + address5.getAddress()); } Address5 address5 = (Address5) session.get(Address5.class, 2l); System.out.println("address5 : " + address5.getAddress()); Set<Person5> person5s = address5.getPerson5s(); for(Person5 person51 : person5s) { System.out.println("address : " + address5.getAddress() + " | " + person51.getName()); }
问题二:
删除问题,我们需要的删除规则是主控方和被控方删除信息时删除关联表中的对应信息,而不会删除对方的信息。代码测试,主控方的删除是没有问题的,在删除被控一方(Address5)信息时报错不能删除关联信息(就是关联表中信息没法删除)。
解决方案:
在Address5中增加cascade = CascadeType.REMOVE或ALL,这时确实可以删除关联表中信息,但同时也将主控方(Person5)信息也删除了,这不是我想要的。到此发现通过代码注解已无法解决这个问题,网上查找获得这么一条建议”去数据库中设置外键中间表的关联关系为delete cascade就可以了“,按照此法,在数据库中手动设置关联表中address外键的删除级联为CASCADE,这时测试问题解决了。表结构如下图
CREATE TABLE `t_master_mastertype` (
`master_id` bigint(20) NOT NULL,
`type_id` bigint(20) NOT NULL,
PRIMARY KEY (`master_id`,`type_id`),
KEY `type_id` (`type_id`),
CONSTRAINT `master` FOREIGN KEY (`master_id`) REFERENCES `t_master` (`id`),
CONSTRAINT `type` FOREIGN KEY (`type_id`) REFERENCES `t_master_type` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异