0

0

Spring Boot 中使用 LEFT JOIN 正确关联用户与地址数据

聖光之護

聖光之護

发布时间:2026-01-06 11:23:01

|

584人浏览过

|

来源于php中文网

原创

Spring Boot 中使用 LEFT JOIN 正确关联用户与地址数据

本文详解如何在 spring boot jpa 中通过 left join 加载关联的 address 实体,避免因外键缺失导致的 `nullpointerexception`,并提供实体映射、dto 转换及查询优化的完整实践方案。

在 Spring Boot 应用中,当使用 @OneToOne 关系映射 User 与 Address 时,若部分用户尚未绑定地址(即数据库中 address.id_user 无对应记录),直接使用 INNER JOIN 查询将导致该用户记录被完全排除——而更隐蔽的问题是:即使查询返回了 User 记录,JPA 默认不会自动填充其 address 字段(尤其在原生 SQL 查询下),从而造成 user.getAddress() 返回 null,最终在 UserDto.fromEntity() 中调用 address.getId() 时抛出 NullPointerException:

"Cannot invoke \"com.pastrycertified.cda.models.Address.getId()\" because \"address\" is null"

根本原因在于您当前 UserRepository.findUserById() 使用的是 INNER JOIN 原生 SQL:

@Query(value = "SELECT * FROM user INNER JOIN role ON user.role_id = role.id " +
               "INNER JOIN address ON user.id = address.id_user WHERE user.id = :id", nativeQuery = true)
Optional findUserById(Integer id);

INNER JOIN 要求所有关联表都存在匹配行,一旦某用户无地址记录,整条记录即被过滤,且即使有结果,JPA 也无法基于原生 SQL 自动组装 @OneToOne 关联对象(尤其是未配置 @JoinColumn 或 fetch = FetchType.EAGER 时)。

✅ 正确解法是改用 LEFT JOIN,确保无论地址是否存在,用户主记录均被查出,并配合 JPA 的关系映射机制完成懒加载或显式抓取。

✅ 推荐方案:使用 JPQL + FETCH JOIN(推荐,类型安全)

替代原生 SQL,改用 JPQL 并显式 FETCH JOIN,让 JPA 自动初始化关联:

public interface UserRepository extends JpaRepository {
    @Query("SELECT u FROM User u " +
           "LEFT JOIN FETCH u.role " +
           "LEFT JOIN FETCH u.address " +
           "WHERE u.id = :id")
    Optional findUserById(@Param("id") Integer id);

    // 或直接使用派生查询(更简洁)
    // Optional findById(Integer id); // 默认已含基础字段,但不加载关联 —— 需配合 @EntityGraph 或 @Fetch
}
⚠️ 注意:LEFT JOIN FETCH 是 JPQL 特性,不可用于原生 SQL;它能强制在单次查询中获取关联实体,避免 N+1 查询和 null 关联问题。

✅ 备选方案:修正原生 SQL + 显式处理空值

若必须使用原生 SQL(如复杂统计场景),请务必改为 LEFT JOIN,并在 DTO 构建逻辑中增加空值防护

public interface UserRepository extends JpaRepository {
    @Query(value = "SELECT * FROM user " +
                   "LEFT JOIN role ON user.role_id = role.id " +
                   "LEFT JOIN address ON user.id = address.id_user " +
                   "WHERE user.id = :id", nativeQuery = true)
    Optional findUserById(@Param("id") Integer id);
}

同时,在 UserDto.fromEntity() 中安全处理 user.getAddress() 可能为 null 的情况:

动态WEB网站中的PHP和MySQL:直观的QuickPro指南第2版
动态WEB网站中的PHP和MySQL:直观的QuickPro指南第2版

动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包

下载
public static UserDto fromEntity(User user) {
    return UserDto.builder()
            .id(user.getId())
            .civility(user.getCivility())
            .lastname(user.getLastname())
            .firstname(user.getFirstname())
            .birth_day(user.getBirth_day())
            .email(user.getEmail())
            .password(user.getPassword())
            .phone(user.getPhone())
            .role_name(user.getRole() != null ? user.getRole().getName() : null)
            .address(user.getAddress() != null 
                    ? AddressDto.fromEntity(user.getAddress()) 
                    : null) // ← 关键:允许 address 为 null,避免 NPE
            .build();
}

此外,请检查实体关系定义是否完备:

  • User.address 字段应添加 @JoinColumn 指明外键列(当前缺失):
    @OneToOne(fetch = FetchType.LAZY) // 推荐 LAZY,按需加载
    @JoinColumn(name = "idAddress") // ← 对应 User 表中的 idAddress 字段
    private Address address;
  • Address.user 已正确定义 @JoinColumn(name = "id_user"),符合您的数据库设计。

? 额外建议

  • 启用 SQL 日志:在 application.yml 中添加:

    spring:
      jpa:
        show-sql: true
        properties:
          hibernate:
            format_sql: true
    logging:
      level:
        org.hibernate.SQL: DEBUG
        org.hibernate.type.descriptor.sql.BasicBinder: TRACE

    可直观验证实际执行的 SQL 及参数绑定。

  • DTO 构建防御性编程:所有 fromEntity() 方法均应校验关联对象非空,而非依赖数据库约束。

  • 考虑使用 @EntityGraph(更优雅的声明式抓取):

    @EntityGraph(attributePaths = {"role", "address"})
    Optional findById(Integer id);

通过以上调整,您将彻底解决 address is null 导致的空指针异常,确保用户数据与地址数据在查询层稳定、安全地协同加载。

相关专题

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

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

676

2023.10.12

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

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

320

2023.10.27

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

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

346

2024.02.23

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

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

1092

2024.03.06

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

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

356

2024.03.06

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

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

674

2024.04.07

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

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

571

2024.04.29

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

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

412

2024.04.29

PPT动态图表制作教程大全
PPT动态图表制作教程大全

本专题整合了PPT动态图表制作相关教程,阅读专题下面的文章了解更多详细内容。

13

2026.01.07

热门下载

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

精品课程

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

共578课时 | 43.1万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

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

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