如何在JPA Repository中关联子实体查询书籍及对应作者并返回自定义DTO
解决方法:查询书籍及对应作者信息并映射到自定义DTO
针对你的需求,我提供两种可行的方案,既不用EntityManager,也能避免在JPQL中写冗长的全限定类名(或者完全绕过构造方法的全限定写法):
方案一:不修改原有实体类,用原生SQL+结果集映射
这种方法不需要改动你现有的Book和Author实体类,通过JPA的结果集映射功能将查询结果直接组装到BookWithAuthor中。
步骤1:给BookWithAuthor添加带参数的构造函数
为了让JPA能自动组装数据,需要添加一个包含所有必要字段的构造函数,在构造函数里把作者相关字段组装成Author对象:
public class BookWithAuthor { public Integer id; public String title; public Author author; // 新增构造函数 public BookWithAuthor(Integer id, String title, Integer authorId, String firstName, String lastName) { this.id = id; this.title = title; this.author = new Author(); this.author.id = authorId; this.author.firstName = firstName; this.author.lastName = lastName; } }
步骤2:在Book实体类上定义结果集映射
用@SqlResultSetMapping和@ConstructorResult注解,指定查询结果如何映射到BookWithAuthor:
@Entity @SqlResultSetMapping( name = "BookWithAuthorMapping", classes = @ConstructorResult( targetClass = BookWithAuthor.class, columns = { @ColumnResult(name = "book_id", type = Integer.class), @ColumnResult(name = "book_title", type = String.class), @ColumnResult(name = "author_id", type = Integer.class), @ColumnResult(name = "author_first_name", type = String.class), @ColumnResult(name = "author_last_name", type = String.class) } ) ) public class Book { public Integer id; public Integer authorId; public String title; }
步骤3:在Repository中编写原生SQL查询
在BookRepository里使用原生SQL,并指定刚才定义的结果集映射:
public interface BookRepository implements JpaRepository<Book, Integer> { @Query( value = "SELECT b.id AS book_id, b.title AS book_title, a.id AS author_id, a.first_name AS author_first_name, a.last_name AS author_last_name " + "FROM book b JOIN author a ON b.author_id = a.id", nativeQuery = true, resultSetMapping = "BookWithAuthorMapping" ) List<BookWithAuthor> findAllWithAuthor(); }
这样就可以直接返回你需要的BookWithAuthor列表,完全不需要EntityManager,也不用在JPQL里写全限定类名。
方案二:修改实体类建立关联,用简化的JPQL构造投影
如果你能接受修改Book实体类,建立和Author的关联关系,查询会更简洁,同时也能简化构造方法的写法。
步骤1:修改Book实体类添加关联
给Book添加@ManyToOne注解,建立和Author的关联(设置insertable = false, updatable = false避免影响原有表结构的操作):
@Entity public class Book { @Id public Integer id; // 保留原有的authorId字段(可选,如果你还需要直接操作这个字段) public Integer authorId; public String title; // 新增关联字段 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "author_id", referencedColumnName = "id", insertable = false, updatable = false) public Author author; }
步骤2:简化JPQL查询(避免全限定类名)
默认情况下JPQL需要写全限定类名,但你可以通过配置让JPA自动识别你的BookWithAuthor类,这样就可以直接使用类名而不用全限定:
- 如果是Spring Boot项目,在
application.properties中添加:
然后创建spring.jpa.properties.hibernate.import_file=imports.hbm.xmlimports.hbm.xml文件放在src/main/resources下:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <import class="com.example.BookWithAuthor"/> </hibernate-mapping>
之后你的Repository查询就可以简化成:
public interface BookRepository implements JpaRepository<Book, Integer> { @Query("SELECT new BookWithAuthor(b.id, b.title, b.author) FROM Book b JOIN FETCH b.author") List<BookWithAuthor> findAllWithAuthor(); }
这样既保留了JPQL的类型安全,又避免了冗长的全限定类名写法。
内容的提问来源于stack exchange,提问作者Ludal
相关产品推荐
相关产品推荐

