0

0

Spring Data JPA 中查询嵌入式实体属性的正确姿势

心靈之曲

心靈之曲

发布时间:2025-09-26 13:40:01

|

631人浏览过

|

来源于php中文网

原创

Spring Data JPA 中查询嵌入式实体属性的正确姿势

本文旨在解决Spring Data JPA在使用existsBy等派生查询方法时,针对嵌入式实体(@Embedded)内部属性进行查询时遇到的PropertyReferenceException问题。核心内容是阐明Spring Data JPA的命名约定,即通过组合嵌入式实体字段名和其内部属性名来构建正确的查询方法,确保框架能够准确解析查询路径,从而实现对嵌入式实体属性的有效过滤。

理解Spring Data JPA与嵌入式实体

在spring data jpa应用中,我们经常使用@embeddable和@embedded注解来构建更清晰、更模块化的实体模型。@embeddable注解用于标记一个类可以被嵌入到其他实体中,而@embedded则用于在实体中引用这个可嵌入类的一个实例。这种设计模式有助于将一组相关属性封装起来,提高代码的内聚性。

例如,在一个牙医管理系统中,Person类可能包含姓名、身份证号(CPF)、生日等个人信息,而Address类则包含地址详情。这些信息可以作为嵌入式对象被Dentist实体引用:

Person.java (嵌入式类)

import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;

@Embeddable
@Data
public class Person {
    @Column(nullable = false, length = 11)
    private String cpf; // 身份证号

    @Column(name = "full_name", nullable = false, length = 60)
    private String fullName; // 姓名

    @Column(nullable = false)
    private String birthdate; // 出生日期

    @Column(name = "email", nullable = true, length = 30)
    private String emailAddress; // 邮箱地址

    @Column(name = "cellphone_number", nullable = true, length = 11)
    private String cellphoneNumber; // 手机号码

    @Embedded
    private Address address; // 嵌入式地址信息 (如果Address也是一个@Embeddable类)
}

Dentist.java (实体类)

import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.UUID;

@Data
@Entity
@Table(name = "tb_dentists")
public class Dentist implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "dentist_id")
    private UUID id;

    @Column
    private LocalDateTime registrationDate; // 注册日期

    @Column(nullable = false, unique = true, length = 6)
    private String croNumber; // 牙医注册号

    @Embedded // 嵌入Person信息
    private Person person;
}

Spring Data JPA 查询嵌入式实体时遇到的问题

当我们需要基于嵌入式实体Person中的某个属性(例如cpf)来查询Dentist实体时,Spring Data JPA的派生查询方法提供了一种便捷的方式。然而,如果命名不当,可能会遇到org.springframework.data.mapping.PropertyReferenceException错误。

例如,如果尝试在DentistRepository中定义如下方法:

// 错误的示例
@Repository
public interface DentistRepository extends JpaRepository {
    boolean existsByCroNumber(String croNumber); // 工作正常

    boolean existsByCpf(String cpf); // 导致 PropertyReferenceException
}

并在服务层调用:

// DentistService.java
public boolean existsByPerson_Cpf(String cpf) {
    return dentistRepository.existsByCpf(cpf); // 错误调用
}

Spring会抛出类似Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'cpf' found for type 'Dentist'的异常。这是因为Spring Data JPA在解析existsByCpf时,会直接在Dentist实体中查找名为cpf的属性,而不是person对象下的cpf属性。Dentist实体本身并没有直接的cpf属性,cpf是嵌入在person对象内部的。

解决方案:遵循命名约定查询嵌入式属性

Spring Data JPA为查询嵌入式实体提供了一套明确的命名约定。当需要查询嵌入式实体内部的属性时,查询方法的命名应遵循以下模式:By

Pixso AI
Pixso AI

Pixso AI是一款智能生成设计稿工具,通过AI一键实现文本输入到设计稿生成。

下载

在我们的例子中,Dentist实体中嵌入了Person类型的person字段,而Person实体中包含cpf属性。因此,正确的查询方法名应该是existsByPersonCpf。

DentistRepository.java (正确示例)

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;

@Repository
public interface DentistRepository extends JpaRepository {
    boolean existsByCroNumber(String croNumber);

    // 正确的查询方法:通过嵌入式实体字段名 'person' 和其内部属性名 'cpf' 组合
    boolean existsByPersonCpf(String cpf);
}

更新后的服务层和控制器层将调用这个正确命名的方法:

DentistService.java (更新)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DentistService {

    @Autowired
    DentistRepository dentistRepository;

    public boolean existsByCroNumber(String croNumber) {
        return dentistRepository.existsByCroNumber(croNumber);
    }

    public boolean existsByPersonCpf(String cpf) { // 调用正确命名的方法
        return dentistRepository.existsByPersonCpf(cpf);
    }

    // ... 其他业务方法
}

DentistController.java (更新)

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.time.ZoneId;

@RestController
@RequestMapping("/dentists")
public class DentistController {

    @Autowired
    DentistService dentistService;

    @PostMapping
    public ResponseEntity saveDentist(@RequestBody @Valid Dentist dentistDto) { // 接收Dentist对象,Person信息包含在内

        if(dentistService.existsByCroNumber(dentistDto.getCroNumber())) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body("CONFLICT: CRO number is already in use!");
        }

        // 从接收的dentistDto中获取嵌入的person对象的cpf
        if(dentistService.existsByPersonCpf(dentistDto.getPerson().getCpf())) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body("CONFLICT: CPF number is already in use!");
        }

        var dentistModel = new Dentist();
        BeanUtils.copyProperties(dentistDto, dentistModel);
        dentistModel.setRegistrationDate(LocalDateTime.now(ZoneId.of("UTC")));
        // 保存操作
        return ResponseEntity.status(HttpStatus.CREATED).body(dentistService.save(dentistModel));
    }
}

注意: 在DentistController中,Person对象应该作为Dentist对象的一部分通过@RequestBody接收,而不是单独作为方法参数。dentistDto.getPerson().getCpf()是获取嵌入式Person对象中cpf的正确方式。

注意事项与最佳实践

  1. 严格遵循命名约定: Spring Data JPA的强大之处在于其基于方法名的查询解析能力。对于嵌入式实体,务必遵循By的命名模式,否则会导致运行时错误。
  2. 多层嵌套嵌入式实体: 如果嵌入式实体也包含其他嵌入式实体,命名约定将进一步扩展。例如,如果Person中嵌入了Address,而Address有street属性,那么查询street的方法可能就是existsByPersonAddressStreet(String street)。
  3. 复杂查询的替代方案: 尽管派生查询方法非常方便,但对于过于复杂或涉及多表关联的查询,考虑使用@Query注解编写JPQL或原生SQL,或者使用Specification API,以提高查询的灵活性和可读性。
  4. 字段类型匹配: 确保查询方法参数的类型与实体中对应属性的类型一致,否则也会导致类型不匹配的错误。

总结

通过本文的讲解,我们深入理解了Spring Data JPA在处理嵌入式实体查询时,派生查询方法命名的重要性。正确地将嵌入式实体字段名与目标属性名组合起来,如existsByPersonCpf,是解决PropertyReferenceException的关键。掌握这一命名约定,能够帮助开发者更高效、更准确地利用Spring Data JPA的强大功能,构建健壮的持久层。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

749

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

328

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

350

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1283

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

361

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

861

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

581

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

423

2024.04.29

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 8万人学习

Java 教程
Java 教程

共578课时 | 53.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号