MySQL Spring Data JPA – findById() 返回空结果,除非之前调用了 findAll()
在Spring Data JPA中,我们经常使用findById()方法来获取指定ID的实体。然而,在使用MySQL数据库时,我们可能会遇到一个奇怪的问题:调用findById()方法返回的结果是空值(null),除非我们之前先调用了findAll()方法。
阅读更多:MySQL 教程
问题描述
我们创建了一个实体类User,如下所示:
我们还编写了一个 UserRepository 接口来对用户实体进行CRUD操作:
现在我们想通过ID来获取一个用户实体对象:
然而,当我们运行代码时,我们会发现user对象是null值。
解决方案
为了解决这个问题,我们需要向findAll方法添加@Query注解。这样做可以强制JPA加载所有Entity(包括User)到Hibernate的持久化上下文中,从而可以正常使用findById()方法。
我们添加了一个名称为findAllUsers()的自定义查询,并使用JPQL从用户实体中选择所有User对象。
现在我们可以使用我们自定义的查询方法来提前加载所有实体:
现在,通过findById()获取的user对象不再为空,而是用户ID为1的正确实体对象。
原因分析
MySQL使用了一个名为BaseRowMapperResultReader的默认结果读取器来在使用findAll()或find()方法获取结果集时读取查询结果。这个结果读取器的默认行为是通过每一行的ResultSet获取对象,然后将它添加到结果列表中。当使用findById()等单一查找方法时,SQL语句会被转化为一个等于查询,返回的结果集最多可能只包括一个对象。然而,由于使用了可以进行批量查询的标准ResultSet,结果读取器会尝试从结果集中读取多个行。
因此,在调用findById()方法之前,我们需要首先调用findAll()或自定义的查询方法,以确保所有实体对象都已在Hibernate的持久化上下文中。另一种解决方案是重写默认的结果读取器。
总结
在使用Spring Data JPA和MySQL时,当我们调用findById()方法返回空对象时,我们需要考虑调用findAll()或者自定义的查询方法,以确保所需实体对象已被加载到Hibernate的持久化上下文中。这是因为MySQL使用默认的结果读取器可能会试图从结果集中读取多个行,并以此导致findById()等方法出现问题。