处理JPA异常: More than one row with the given id entifier was found

本文探讨了在使用Hibernate时遇到的Morethanonerowwiththegivenidentifierwasfound异常,该异常通常发生在使用@ManyToOne或@OneToOne注解时。文章提供了调试方法及解决方案,建议通过查看Hibernate生成的SQL日志来定位问题。
部署运行你感兴趣的模型镜像

在使用hibernate entitymanager的时候,我们会用 entityManager.createQuery("xxx").getResultList(); 来获取对象。在某些时候,就会产生如下的异常:

 

 More than one row with the given identifier was found

 这是由getResultList()抛出来的。由于所搜索出的数据会包含重复的id,同时在对象映射上使用@ManyToOne或者@OneToOne,因此hibernate产生异常。

 

由于hibernate会根据搜索语句和关联的对象自动产生很多sql语句,因此要调试的话首先把hibernate sql log打开,把报错的最后一个sql取出来单独执行,看看是否产生重复数据。

 

基本的解决方法就是把重复的数据处理掉,就不会产生异常了。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

你遇到的异常是: ``` org.springframework.orm.hibernate3.HibernateSystemException: More than one row with the given identifier was found: 50000001, for class: com.skoito.qms.business.iqc.entity.Invoice; nested exception is org.hibernate.HibernateException: More than one row with the given identifier was found: 50000001, for class: com.skoito.qms.business.iqc.entity.Invoice ``` --- ### ✅ 错误含义 这个错误的核心意思是: > Hibernate 尝试通过主键(ID)加载一个实体对象 `Invoice`,但数据库中存在 **多个记录具有相同的主键值 `50000001`**,这违反了唯一性约束。 Hibernate 假设主键是唯一的。当你调用如 `session.get(Invoice.class, 50000001)` 或使用 Spring Data JPA 的 `findById()` 时,它期望最多返回一条记录。如果有多条,就会抛出此异常。 --- ### 📌 根本原因分析 1. **数据库表中确实存在重复主键** - 虽然理论上主键应该是唯一的,但可能由于: - 没有为该字段设置 `PRIMARY KEY` 约束 - 手动插入数据导致重复 ID - 自增机制失效或手动指定 ID 造成冲突 2. **实体映射问题** - 实体类中的 `@Id` 字段映射到了非唯一字段。 - 或者复合主键未正确使用 `@EmbeddedId` / `@IdClass`。 3. **缓存污染(较少见)** - 在使用一级/二级缓存的情况下,若之前加载过不同对象却共享同一 ID,可能导致冲突(但通常不会报“found more than one row”)。 4. **查询逻辑误触懒加载触发加载** - 当访问某个延迟加载属性时,Hibernate 尝试根据 ID 加载关联对象,而此时数据库中有多个同 ID 记录。 --- ### ✅ 解决方案 #### ✅ 步骤 1:检查数据库是否存在重复主键 运行 SQL 查询确认是否有重复 ID: ```sql SELECT id, COUNT(*) FROM invoice WHERE id = 50000001 GROUP BY id HAVING COUNT(*) > 1; ``` 或者查看整个表是否存在重复主键: ```sql SELECT id, COUNT(*) as cnt FROM invoice GROUP BY id HAVING COUNT(*) > 1; ``` 👉 如果结果返回多行,则说明数据不一致。 ##### 🔧 修复方法: - 删除重复记录(保留一条) - 添加主键约束防止未来再发生: ```sql ALTER TABLE invoice ADD CONSTRAINT pk_invoice PRIMARY KEY (id); ``` --- #### ✅ 步骤 2:检查实体类映射是否正确 确保你的 `Invoice` 实体类正确标注了主键字段: ```java @Entity @Table(name = "invoice") public class Invoice { @Id @Column(name = "id") private Long id; // 其他字段... // getter/setter } ``` ⚠️ 注意: - 不要将 `@Id` 注解在业务编号、订单号等非唯一字段上。 - 如果主键是字符串类型,请确认类型和数据库一致。 - 如果是复合主键,必须使用 `@Embeddable` + `@EmbeddedId` 或 `@IdClass`。 --- #### ✅ 步骤 3:排查代码中如何触发加载 查找哪些代码触发了根据 ID 加载 `Invoice` 对象的操作,例如: ```java // 可能触发异常的地方 Invoice invoice = sessionFactory.getCurrentSession().get(Invoice.class, 50000001L); // 或使用 JPA Repository Optional<Invoice> opt = invoiceRepository.findById(50000001L); // <-- 抛异常 ``` 你可以加日志输出当前操作上下文,定位具体业务场景。 --- #### ✅ 步骤 4:临时绕过方式(仅用于诊断) 如果你无法立即修改数据,可以改用 HQL 查询来避免 `get()` 方法的严格校验: ```java String hql = "FROM Invoice WHERE id = :id"; List<Invoice> results = session.createQuery(hql) .setParameter("id", 50000001L) .setMaxResults(2) .list(); if (!results.isEmpty()) { Invoice first = results.get(0); // 处理第一条记录 } ``` 但这只是权宜之计,根本还是要解决数据重复问题。 --- #### ✅ 步骤 5:预防措施 | 措施 | 说明 | |------|------| | 数据库加主键约束 | `ALTER TABLE ... ADD PRIMARY KEY (id);` | | 使用自增主键或 UUID | 避免手动分配 ID 导致重复 | | 插入前做唯一性校验 | 应用层判断是否已存在该 ID | | 定期数据稽查脚本 | 扫描并报警重复主键 | --- ### ⚠️ 特别注意:Hibernate 3 已经非常老旧! 你使用的异常类是: ``` org.springframework.orm.hibernate3.HibernateSystemException ``` 这意味着你在使用 **Hibernate 3.x**,这是一个早已停止维护的版本(发布于 2008 年左右),存在大量已知 bug 和安全漏洞。 📌 建议尽快升级到: - Hibernate 5.6+(支持 Java 8 ~ 17) - 或 Hibernate 6+(Jakarta EE 9+, Java 11+) 配合 Spring Framework 5+ / Spring Boot 2.7+ 否则你会持续遇到兼容性、性能、安全性等问题。 --- ### ✅ 总结 | 项目 | 内容 | |------|------| | ❌ 根本原因 | 数据库中存在多个相同主键的记录(ID=50000001) | | ✅ 解决办法 | 删除重复数据 + 添加主键约束 + 检查实体映射 | | ✅ 编码建议 | 使用 HQL 替代 `get()` 临时规避(不推荐长期使用) | | 🔁 升级建议 | 升级 Hibernate 3 → 5.6+ 或 6+,提升稳定性和安全性 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值