Hibernate一对一映射报错:java.sql.SQLException:无效列名PortfolioKey
我有Portfolio和InvestmentAllocation两个实体类,希望Portfolio关联InvestmentAllocation以实现便捷的对象设置与查询,同时InvestmentAllocation仅保留@Column(name = "PortfolioKey") private Long portfolioId字段。配置映射后运行代码出现错误:
Caused by: java.sql.SQLException: Invalid column name 'PortfolioKey'
实体类代码
@Entity @Getter @Setter @Table(name = "Portfolio") public class Portfolio extends MutableEntity { private static final long serialVersionUID = -6668016124280143515L; @Column(name = "Name") private String name; @Column(name = "Amount") private BigDecimal amount; @OneToOne( fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true ) @JoinColumn( name = "PortfolioKey", nullable = false, insertable = false, updatable = false ) @Cascade({org.hibernate.annotations.CascadeType.ALL}) private InvestmentAllocation investmentAllocation; @PostPersist protected void postPersist() { if (this.investmentAllocation != null) { if (this.investmentAllocation.getPortfolioId() == null) { this.investmentAllocation.setPortfolioId(this.getId()); } } } } @Entity @Getter @Setter @Table(name = "InvestmentAllocation") public class InvestmentAllocation extends MutableEntity{ private static final long serialVersionUID = -5974811107021515065L; @Column(name = "PortfolioKey") private Long portfolioId; @Column(name = "Cash") private double cash; @Column(name = "Bond") private double bond; }
建表语句
CREATE TABLE [dbo].[Portfolio]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](255) NOT NULL, [Amount] [money] NOT NULL, CONSTRAINT [PK_PortfolioGeneric] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 85) ON [PRIMARY] ) ON [PRIMARY] GO CREATE TABLE [dbo].[InvestmentAllocation]( [ID] [int] IDENTITY(1,1) NOT NULL, [PortfolioKey] [int] NOT NULL, [Bond] [decimal](8, 4) NOT NULL, [Cash] [decimal](8, 4) NOT NULL, CONSTRAINT [PK_InvestmentAllocation] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 85) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[InvestmentAllocation] WITH CHECK ADD CONSTRAINT [PK_InvestmentAllocation_Portfolio] FOREIGN KEY([PortfolioKey]) REFERENCES [dbo].[Portfolio] ([ID]) GO ALTER TABLE [dbo].[InvestmentAllocation] CHECK CONSTRAINT [PK_InvestmentAllocation_Portfolio] GO
报错原因与映射配置问题
@JoinColumn配置错误
你在Portfolio类的@OneToOne关联中,将@JoinColumn(name = "PortfolioKey")配置在了Portfolio实体上,但实际PortfolioKey字段属于InvestmentAllocation表,JPA会按照这个配置去Portfolio表中查找该列,自然触发“无效列名”错误。一对一映射方向搞反
当前表结构是InvestmentAllocation通过PortfolioKey外键关联Portfolio的ID,属于单向一对一关联(由InvestmentAllocation指向Portfolio)。若要在Portfolio中反向关联InvestmentAllocation,必须使用@OneToOne的mappedBy属性指定关联的维护端。数据类型不匹配
数据库中Portfolio.ID和InvestmentAllocation.PortfolioKey都是int类型,但实体类中InvestmentAllocation.portfolioId用了Long类型,虽框架通常能自动转换,但建议保持类型一致避免潜在问题。
修正后的实体类配置
方案1:保留portfolioId字段的双向关联
Portfolio类
@Entity @Getter @Setter @Table(name = "Portfolio") public class Portfolio extends MutableEntity { private static final long serialVersionUID = -6668016124280143515L; @Column(name = "Name") private String name; @Column(name = "Amount") private BigDecimal amount; // 反向关联,指定mappedBy表示由InvestmentAllocation维护关联 @OneToOne( fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true, mappedBy = "portfolio" ) private InvestmentAllocation investmentAllocation; @PostPersist protected void postPersist() { if (this.investmentAllocation != null) { if (this.investmentAllocation.getPortfolioId() == null) { this.investmentAllocation.setPortfolioId(this.getId()); } } } }
InvestmentAllocation类
@Entity @Getter @Setter @Table(name = "InvestmentAllocation") public class InvestmentAllocation extends MutableEntity{ private static final long serialVersionUID = -5974811107021515065L; @Column(name = "PortfolioKey") private Integer portfolioId; // 改为Integer与数据库int类型匹配 // 作为关联维护端,关联Portfolio实体 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "PortfolioKey", insertable = false, updatable = false) private Portfolio portfolio; @Column(name = "Cash") private double cash; @Column(name = "Bond") private double bond; }
方案2:移除portfolioId字段,直接用实体关联(更符合JPA规范)
InvestmentAllocation类
@Entity @Getter @Setter @Table(name = "InvestmentAllocation") public class InvestmentAllocation extends MutableEntity{ private static final long serialVersionUID = -5974811107021515065L; // 直接关联Portfolio,作为关联维护端 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "PortfolioKey", nullable = false) private Portfolio portfolio; @Column(name = "Cash") private double cash; @Column(name = "Bond") private double bond; }
此方案下Portfolio类的PostPersist方法可简化,JPA会自动维护外键关系。
内容的提问来源于stack exchange,提问作者Enchantres

