Hibernate 6.2:解决ClassCastException后遇DataIntegrityViolationException问题
初始错误与映射修改
首先遇到Hibernate 6.2相关的类型转换错误:
Caused by: java.lang.ClassCastException: class org.hibernate.mapping.BasicValue cannot be cast to class org.hibernate.mapping.ToOne (org.hibernate.mapping.BasicValue and org.hibernate.mapping.ToOne are in unnamed module of loader 'app')
原单向关联映射错误使用了mappedBy,原代码:
@Entity @Table(name = "customer") class Customer( @Id val id: UUID = UUID.randomUUID(), @OneToMany(mappedBy = "customerPortfolioId", fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) var customerPortfolios: List<CustomerPortfolio> = mutableListOf() )
修改映射去掉mappedBy,改用@JoinColumn指定关联字段:
@Entity @Table(name = "customer") class Customer( @Id val id: UUID = UUID.randomUUID(), @OneToMany(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) @JoinColumn(name = "customerPortfolioId") var customerPortfolios: List<CustomerPortfolio> = mutableListOf() )
关联的CustomerPortfolio实体定义:
@Entity @Table(name = "customer_portfolio") data class CustomerPortfolio( @Id val id: UUID = UUID.randomUUID(), val customerPortfolioId: UUID )
新出现的非空约束错误
修改映射后,原类型转换错误解决,但出现数据完整性异常:
org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: null value in column "customer_portfolio_id" violates not-null constraint
Detail: Failing row contains (a338ef9f-6b9f-4741-b7b3-7fd1a4894db0, null).] [update customer_portfolio set customer_portfolio_id=null where customer_portfolio_id=?]; SQL [update customer_portfolio set customer_portfolio_id=null where customer_portfolio_id=?]; constraint [customer_portfolio_id]
错误原因
这条UPDATE语句是Hibernate自动生成的,问题出在映射逻辑:
Customer中的@JoinColumn(name = "customerPortfolioId")指定的字段,在CustomerPortfolio里是普通UUID类型字段,而非JPA规范的实体关联(@ManyToOne)。- Hibernate尝试维护关联关系时,会自动生成更新语句清空该字段,但
customer_portfolio_id列有非空约束,因此触发异常。
解决方法
方案一:规范实体关联(推荐)
调整CustomerPortfolio实体,将普通UUID字段改为关联Customer的外键,使用@ManyToOne映射:
@Entity @Table(name = "customer_portfolio") data class CustomerPortfolio( @Id val id: UUID = UUID.randomUUID(), @ManyToOne @JoinColumn(name = "customer_portfolio_id", nullable = false) // 对应数据库列名customer_portfolio_id val customer: Customer? = null )
同时修改Customer的映射,确保@JoinColumn列名与CustomerPortfolio一致:
@Entity @Table(name = "customer") class Customer( @Id val id: UUID = UUID.randomUUID(), @OneToMany(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) @JoinColumn(name = "customer_portfolio_id") var customerPortfolios: List<CustomerPortfolio> = mutableListOf() )
这样Hibernate能正确识别实体关联,不会生成错误的UPDATE语句,符合JPA规范。
方案二:手动维护关联字段(不推荐)
如果无法修改CustomerPortfolio结构,需避免Hibernate自动更新customerPortfolioId字段:
- 调整
Customer的级联类型,移除CascadeType.ALL,避免Hibernate自动维护关联更新。 - 在保存
CustomerPortfolio时,手动设置customerPortfolioId为对应的Customer的ID值。
但这种方式不符合JPA规范,容易引发其他关联维护问题,仅作为临时方案。
内容的提问来源于stack exchange,提问作者Maciaz

