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

JPA Criteria查询无法识别复合键属性surveyYear报错求助

解决JPA Criteria查询复合键属性报错的问题

我之前碰到过一模一样的情况!你遇到的IllegalArgumentException: unable to find local attribute with the given name surveyYear错误,根源在于**surveyYear是复合主键的组成字段**,JPA Criteria API不能像访问普通实体属性那样直接通过root.get("surveyYear")来获取它。

下面给你两种解决方案,优先推荐第一种(符合JPA规范,长期维护更省心):

方案一:创建显式的复合主键类(推荐)

JPA规范里,复合主键需要通过一个实现Serializable的类来封装,这样Criteria API就能正确识别其中的属性了。

1. 编写复合主键类

创建一个UniversityBasicInfoId类,用来封装aisheCode和surveyYear两个复合键字段:

import java.io.Serializable;
import java.util.Objects;

public class UniversityBasicInfoId implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String aisheCode;
    private Integer surveyYear;

    // 必须提供无参构造器(JPA要求)
    public UniversityBasicInfoId() {}

    // 可选:带参构造器,方便快速创建主键对象
    public UniversityBasicInfoId(String aisheCode, Integer surveyYear) {
        this.aisheCode = aisheCode;
        this.surveyYear = surveyYear;
    }

    // 必须重写equals和hashCode方法(复合主键的核心要求)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UniversityBasicInfoId that = (UniversityBasicInfoId) o;
        return Objects.equals(aisheCode, that.aisheCode) && Objects.equals(surveyYear, that.surveyYear);
    }

    @Override
    public int hashCode() {
        return Objects.hash(aisheCode, surveyYear);
    }

    // getter和setter方法
    public String getAisheCode() { return aisheCode; }
    public void setAisheCode(String aisheCode) { this.aisheCode = aisheCode; }
    public Integer getSurveyYear() { return surveyYear; }
    public void setSurveyYear(Integer surveyYear) { this.surveyYear = surveyYear; }
}

2. 修改实体类,关联复合主键类

把原来实体类里的aisheCode和surveyYear字段替换成复合主键对象,用@EmbeddedId注解标记:

import javax.persistence.EmbeddedId;
import java.io.Serializable;

public class UniversityBasicInformation implements Serializable{
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private UniversityBasicInfoId id; // 复合主键对象

    @JsonProperty
    private String state;
    @JsonProperty
    private String name;
    @JsonProperty
    private String district;
    @JsonProperty
    private String type;

    // 可选:添加便捷方法,直接访问复合键里的字段(避免每次都写id.getXXX())
    public String getAisheCode() { return id.getAisheCode(); }
    public void setAisheCode(String aisheCode) { id.setAisheCode(aisheCode); }
    public Integer getSurveyYear() { return id.getSurveyYear(); }
    public void setSurveyYear(Integer surveyYear) { id.setSurveyYear(surveyYear); }

    // 其他字段的getter和setter方法...
}

3. 更新Hibernate映射文件

调整hbm.xml里的复合主键映射,指向新的主键类:

<hibernate-mapping>
    <class name="gov.nic.aishe.read.pojo.UniversityBasicInformation" table="university_basic_information" schema="readonly">
        <composite-id name="id" class="gov.nic.aishe.read.pojo.UniversityBasicInfoId">
            <key-property name="aisheCode" type="java.lang.String">
                <column name="aishe_code" />
            </key-property>
            <key-property name="surveyYear" type="java.lang.Integer">
                <column name="survey_year" />
            </key-property>
        </composite-id>
        <!-- 其他属性映射保持不变 -->
        <property name="state" type="java.lang.String">
            <column name="state" />
        </property>
        <property name="district" type="java.lang.String">
            <column name="district" />
        </property>
        <property name="type" type="java.lang.String">
            <column name="type" />
        </property>
    </class>
</hibernate-mapping>

4. 调整Criteria查询代码

现在可以通过复合主键对象来访问surveyYear了:

// 替换原来报错的那行代码
predicates.add(builder.equal(root.get("id").get("surveyYear"), surveyYear));

方案二:临时解决方案(不推荐,依赖Hibernate实现)

如果不想修改实体结构,可以尝试用Hibernate的特定API来绕过这个限制,但这种方式不符合JPA规范,后续换JPA实现会有问题:

// 直接用Hibernate的Criteria API替代JPA Criteria(不推荐)
org.hibernate.Criteria hibernateCriteria = sessionFactory.getCurrentSession().createCriteria(UniversityBasicInformation.class);
hibernateCriteria.add(Restrictions.eq("surveyYear", surveyYear));

为什么会报错?

在你的原代码里,surveyYear是复合主键的一部分,JPA的元数据会把复合键的所有字段视为一个整体(而不是实体的独立属性),所以直接用root.get("surveyYear")会找不到对应的属性,必须通过复合主键的容器来访问。

内容的提问来源于stack exchange,提问作者Bhanu Prakash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:38:45