Java JPA:非原生查询下如何仅预加载子属性列表首个元素?
解决方案
方法一:JPQL投影查询(更高效推荐)
不用加载完整的books集合,直接通过JPQL精准查询作者+对应最后一本书的出版商:
假设Book有publishDate字段用来判断书籍新旧,编写如下查询:
@Query("SELECT a, p FROM Author a " + "JOIN a.books b " + "JOIN b.publisher p " + "WHERE b = (SELECT b2 FROM Book b2 WHERE b2.author = a ORDER BY b2.publishDate DESC LIMIT 1)") List<Object[]> findAuthorsWithLatestBookPublisher();
返回的数组里,第一个元素是Author实例,第二个是对应的Publisher实例。
嫌数组返回格式麻烦的话,可以定义一个DTO类来封装结果:
public class AuthorWithPublisherDTO { private Author author; private Publisher publisher; public AuthorWithPublisherDTO(Author author, Publisher publisher) { this.author = author; this.publisher = publisher; } // 按需添加getter/setter方法 }
然后修改JPQL语句:
@Query("SELECT new com.yourpackage.AuthorWithPublisherDTO(a, p) FROM Author a " + "JOIN a.books b " + "JOIN b.publisher p " + "WHERE b = (SELECT b2 FROM Book b2 WHERE b2.author = a ORDER BY b2.publishDate DESC LIMIT 1)") List<AuthorWithPublisherDTO> findAuthorsWithLatestBookPublisher();
方法二:实体关联排序+限定加载逻辑
如果坚持要通过Author实体直接获取最新书籍,可以先给Author的books字段添加排序注解,让集合默认按出版时间倒序排列:
public class Author { @OneToMany @OrderBy("publishDate DESC") List<Book> books; }
之后在业务代码里直接调用books.get(0)就能拿到最后一本书,再获取其对应的出版商。但要注意:这种方式默认会加载该作者的全部书籍到内存中,除非额外做加载限制。
想要避免加载全部书籍,可以使用带FETCH JOIN的JPQL并配合结果限制:
@Query("SELECT a FROM Author a JOIN FETCH a.books b ORDER BY b.publishDate DESC") List<Author> findAuthorsWithLatestBook();
不过这种查询会因为每个书籍对应一个作者实例,导致返回结果中出现重复的Author对象,需要你在代码里手动去重,只保留每个作者的第一条记录。
关于@EntityGraph的说明
@EntityGraph确实只能预加载整个关联集合,无法直接限定为集合的首个元素,所以并不适合你的需求场景。
内容的提问来源于stack exchange,提问作者Zacharie
相关产品推荐
相关产品推荐

