You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.xml
    
    然后创建imports.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:04:06