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
相关产品推荐
相关产品推荐

