MySQL Spring Data JPA – findById() 返回空结果,除非之前调用了 findAll()

MySQL Spring Data JPA – findById() 返回空结果,除非之前调用了 findAll()

在Spring Data JPA中,我们经常使用findById()方法来获取指定ID的实体。然而,在使用MySQL数据库时,我们可能会遇到一个奇怪的问题:调用findById()方法返回的结果是空值(null),除非我们之前先调用了findAll()方法。

阅读更多:MySQL 教程

问题描述

我们创建了一个实体类User,如下所示:

@Entity
public class User {
    @Id
    private Long id;

    private String name;
}
Java

我们还编写了一个 UserRepository 接口来对用户实体进行CRUD操作:

public interface UserRepository extends JpaRepository<User, Long> {
}
Java

现在我们想通过ID来获取一个用户实体对象:

User user = userRepository.findById(1L).orElse(null);
Java

然而,当我们运行代码时,我们会发现user对象是null值。

解决方案

为了解决这个问题,我们需要向findAll方法添加@Query注解。这样做可以强制JPA加载所有Entity(包括User)到Hibernate的持久化上下文中,从而可以正常使用findById()方法。

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select u from User u")
    List<User> findAllUsers();
}
Java

我们添加了一个名称为findAllUsers()的自定义查询,并使用JPQL从用户实体中选择所有User对象。

现在我们可以使用我们自定义的查询方法来提前加载所有实体:

userRepository.findAllUsers();
User user = userRepository.findById(1L).orElse(null);
Java

现在,通过findById()获取的user对象不再为空,而是用户ID为1的正确实体对象。

原因分析

MySQL使用了一个名为BaseRowMapperResultReader的默认结果读取器来在使用findAll()或find()方法获取结果集时读取查询结果。这个结果读取器的默认行为是通过每一行的ResultSet获取对象,然后将它添加到结果列表中。当使用findById()等单一查找方法时,SQL语句会被转化为一个等于查询,返回的结果集最多可能只包括一个对象。然而,由于使用了可以进行批量查询的标准ResultSet,结果读取器会尝试从结果集中读取多个行。

因此,在调用findById()方法之前,我们需要首先调用findAll()或自定义的查询方法,以确保所有实体对象都已在Hibernate的持久化上下文中。另一种解决方案是重写默认的结果读取器。

总结

在使用Spring Data JPA和MySQL时,当我们调用findById()方法返回空对象时,我们需要考虑调用findAll()或者自定义的查询方法,以确保所需实体对象已被加载到Hibernate的持久化上下文中。这是因为MySQL使用默认的结果读取器可能会试图从结果集中读取多个行,并以此导致findById()等方法出现问题。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

登录

注册