You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

报错原因与映射配置问题

  1. @JoinColumn配置错误
    你在Portfolio类的@OneToOne关联中,将@JoinColumn(name = "PortfolioKey")配置在了Portfolio实体上,但实际PortfolioKey字段属于InvestmentAllocation表,JPA会按照这个配置去Portfolio表中查找该列,自然触发“无效列名”错误。

  2. 一对一映射方向搞反
    当前表结构是InvestmentAllocation通过PortfolioKey外键关联Portfolio的ID,属于单向一对一关联(由InvestmentAllocation指向Portfolio)。若要在Portfolio中反向关联InvestmentAllocation,必须使用@OneToOne的mappedBy属性指定关联的维护端。

  3. 数据类型不匹配
    数据库中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:28:14