编程

JAVA SQL 查询结果映射之构造函数映射

482 2024-07-20 00:33:00

这是 SQL 结果集映射系列的第三篇。

在本系列的第一篇文章中,我们查看了查询结果和单个实体之间的一些映射定义。第二篇文章中的映射定义则更为复杂,因为我们将查询结果映射到了多个实体并处理了额外的字段。

本文中,我们将查看 JPA 2.1 中引入的构造函数结果映射。该特性允许你使用查询结果调用值对象的构造函数,类似于 JPQL 构造函数表达式。如果我们想向客户端提供域模型的特定视图,则经常使用此方法。

示例

开始之前,我们先看看将用于示例的实体模型。如果你读过本系列的第二篇,你已经熟悉了 Author 实体和 Book 实体。这两个实体都很简单。Author 实体具有 id、版本号,名字和姓氏。而 Book 实体具有 id、版本号,标题和对 Author 的引用。为避免过于复杂,每本书(Book)只有一个作者(Author)。

由于我们想将查询结果映射到值对象中,我们需要一个额外的类,我们称之为 BookValue,它有 id、版本号、标题、作者名字。

如何映射到值对象

选择实体并将对象树返回给调用者并不总是最好的方法。调用者通常只需要所提供信息的一个子集,而特定的值对象会更有效率。对于这些情况,JPQL 支持可在  JPQL 查询的检索(select)部分中指定的构造函数表达式,并为每个选定的记录定义构造函数调用。

我们示例中的 BookValue 可以在客户端中用于显示有关书籍(Book)和作者(Author)姓名的一些信息。只要我们只需要作者的名字,而不需要选择和转换整个作者实体。使用 BookValue 对象并在查询中检索作者的名称更高效。

SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName FROM Book b JOIN Author a ON b.author_id = a.id

下一步,我们需要定义使用该查询的映射,用以调用 BookValue 的构造函数。这与我们之前使用 @SqlResultSetMapping  注释创建的映射类似。以下代码片显示的映射由一个名称和一个 @ConstructorResult 注释组成。

@SqlResultSetMapping(

        name = "BookValueMapping",

        classes = @ConstructorResult(

                targetClass = BookValue.class,

                columns = {

                    @ColumnResult(name = "id", type = Long.class),

                    @ColumnResult(name = "title"),

                    @ColumnResult(name = "version", type = Long.class),

                    @ColumnResult(name = "authorName")}))

映射的名称,在本例中为 BookValueMapping,稍后将用于告诉 EntityManager 要使用哪个映射。@ConstructorResult 注释定义了给定目标类(targetClass)的构造函数调用。此处是示例中的 BookValue@ColumnResult 注释的数组定义了查询结果中将用作构造函数参数的字段及其类型和顺序。type 属性是可选的,仅当字段的类型与构造函数参数的类型不同时,才需要提供它。在这种情况下,idversion 字段的默认类型是 BigInteger,需要转换为 Long

与多个实体的映射类似,@SqlResultSetMapping classes 属性接受 @ConstructorResult 注释的数组。如果映射映射到多个值对象或实体,则每个字段可以使用多次。

与之前的所有映射定义一样,构造函数结果映射也可以在映射 XML 文件中定义。最简单的方法是使用名为 orm.xml 的默认映射文件,如果它被添加到 jar 文件的 META-INF 目录中,就会自动使用该文件。

<sql-result-set-mapping name="BookValueMappingXml">

    <constructor-result target-class="org.thoughts.on.java.jpa.value.BookValue">

        <column name="id" class="java.lang.Long"/>

        <column name="title"/>

        <column name="version" class="java.lang.Long"/>

        <column name="authorName"/>

    </constructor-result>

</sql-result-set-mapping>

构造函数映射的用法与其他 SQL 结果集映射相同。我们需要将其提供给 EntityManagercreateNativeQuery(String sqlString, String resultSetMapping) 方法,然后我们将得到一个 List

List<BookValue> results = this.em.createNativeQuery("SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName FROM Book b JOIN Author a ON b.author_id = a.id", "BookValueMapping").getResultList();

结论

在第一篇文章中我们从简单的结果映射开始,并在第二篇文章中创建了更复杂的结果映射之后,现在我们了解了 JPA 2.1 中引入的构造函数结果映射。

它们提供了类似于 JPQL 构造函数表达式的功能,并将查询结果映射到构造函数调用。映射可以通过 XML 或 ·】@SqlResultSetMapping 注释以及一个或多个 @ConstructorResult 注释来定义。