DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Spring Data JPA 派生查询报 No property found

Repository 派生查询按实体属性解析。核对 Java 名与数据库列、继承的 findById 例外,以及嵌套属性的下划线路径分隔。

Repository 增加了 findUserRoleById(),应用启动却报 PropertyReferenceException: No property 'id' found for type 'UserRole'。实体里明明有 @Id,数据库也有 role_id 列,继续改建表语句通常解决不了这个错误。

派生查询解析的是 Java 实体属性和属性路径。@Column(name = "role_id") 负责映射数据库列,不会把 Java 属性 roleId 变成 id。以下示例采用 Spring Boot 3.x、Spring Data JPA 3.x 的写法,持久化注解使用 jakarta.persistence。

从最里面的 PropertyReferenceException 找到属性和类型

不要只看最外层 BeanCreationException。沿异常链找到 No property ... found for type ...,再对照异常指向的方法和实体。示例实体片段如下,字段上的 @Id 表示采用字段访问;无参构造和业务访问方法应保留:

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class UserRole {
    @Id
    @Column(name = "role_id")
    private Long roleId;

    @Column(name = "role_title")
    private String roleTitle;

    protected UserRole() {
    }
}

派生方法中的 ById 会尝试查找 id 属性。这个实体只有 roleId,可以将 Repository 写成:

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRoleRepository
        extends JpaRepository<UserRole, Long> {
    Optional<UserRole> findByRoleId(Long roleId);
}

@Param("role_id") 不能把错误的方法名改成正确属性。它用于命名参数绑定,不会重写派生查询的属性解析。采用 getter 上的持久化注解时,也应按实际映射的 Java 属性核对,不只盯着私有字段的拼写。

主键查询有一个容易混淆的例外

如果需求就是按主键查找,可以直接使用 JpaRepository 继承的 findById():

Optional<UserRole> role = repository.findById(roleId);

这个保留方法针对 @Id 标识的主键,即使主键属性实际叫 roleId,也可以使用。它与自定义的 findUserRoleById() 不同:中间添加了描述性词语后,后者按派生规则解析 id 属性。

因此不要看到 ById 就统一替换。先分清是在调用继承的主键方法,还是自己声明的属性查询。若实体同时存在主键属性和一个普通的 id 属性,混用还可能查到另一种含义的数据。

下划线划分属性路径,不等于数据库列名

数据库列为 municipal_id 时,Java 字段可以保持 municipalId,通过 @Column(name = "municipal_id") 映射。对应方法使用 findByMunicipalId()。不需要为了查询方法改数据库列名。

方法中的 _ 可明确嵌套路径。例如实体有 address,其内部有 zipCode,可以写:

List<Person> findByAddress_ZipCode(String zipCode);

这表示 address.zipCode。如果外层同时有 addressZip,findByAddressZipCode() 的自动拆分可能先匹配它,再尝试查找不存在的 code;用下划线指定遍历边界可以消除这种歧义。这里假定实际实体的 zipCode 类型为 String,参数类型必须跟项目模型对应。

不要把数据库的 snake_case 列名原样塞进派生方法,再尝试通过增加下划线碰运气。改 Java 属性时同步 getter、setter 和调用位置,保留列映射;若接口 JSON 字段也受影响,应另外核对已有序列化契约。

显式 JPQL 仍然使用实体属性

属性名复杂、不适合继续拼长方法名时,可以写明确的查询。下面放在同一个 Repository 中:

@Query("select r from UserRole r where r.roleId = :roleId")
Optional<UserRole> findRole(@Param("roleId") Long roleId);

这里的 UserRole 是实体名,roleId 是实体属性,需要导入 Spring Data JPA 的 Query 和 Param。只有明确声明为原生 SQL 的查询才按数据库表和列书写;不要为了绕过一次拼写错误就切换查询语言。

修复后确认 Repository 能正常创建,再覆盖有结果、无结果和嵌套属性查询。若同样的错误出现在请求执行时,还要检查传入的 Sort 或 Pageable:排序通常也应针对实体属性,数据库列名不能直接代替。