Spring Data JPA将本机查询结果映射到非实体POJO

ale*_*oid 69 spring hibernate jpa pojo spring-data

我有一个带有本机查询的Spring Data存储库方法

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
    GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
Run Code Online (Sandbox Code Playgroud)

我想将结果映射到非实体POJO GroupDetails.

是否有可能,如果可以的话,请你提供一个例子吗?

Mic*_*mal 73

我认为最简单的方法是使用所谓的投影.它可以将查询结果映射到接口.使用SqlResultSetMapping是不方便的,使你的代码丑陋:).

一个例子就是从spring数据JPA源代码:

public interface UserRepository extends JpaRepository<User, Integer> {

   @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
   NameOnly findByNativeQuery(Integer id);

   public static interface NameOnly {

     String getFirstname();

     String getLastname();

  }
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用此方法获取投影列表.

查看此春季数据JPA docs条目,了解有关投影的更多信息.

注1:

请记住将您的User实体定义为正常 - 投影界面中的字段必须与此实体中的字段匹配.否则,字段映射可能会被破坏(getFirstname()可能会返回姓氏等值).

笔记2:

如果使用SELECT table.column ...表示法,请始终定义与实体名称匹配的别名.例如,此代码将无法正常工作(投影将为每个getter返回null):

@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);
Run Code Online (Sandbox Code Playgroud)

但这很好用:

@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);
Run Code Online (Sandbox Code Playgroud)

  • 这是一个更清洁的解决方案。我检查过,但性能比使用 SqlResultSetMapping 差得多(慢了大约 30-40% :( ) (3认同)
  • 当使用小数据集时就可以了。但我已经测试了100000条数据,服务性能大约下降到没有投影情况下的1/12。换句话说,没有投影的 Web 服务大约快 12 倍。 (2认同)

Dai*_*mon 56

假设GroupDetails和orid的答案一样,你试过JPA 2.1 @ConstructorResult吗?

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID"),
                @ColumnResult(name="USER_ID")
            }
        )
    }
)

@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")
Run Code Online (Sandbox Code Playgroud)

并在存储库界面中使用以下:

GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
Run Code Online (Sandbox Code Playgroud)

根据Spring Data JPA 文件,春季将首先尝试找到一个名为查询匹配方法名称-通过使用这样@NamedNativeQuery,@SqlResultSetMapping@ConstructorResult你应该能够实现这一行为

  • 为了使spring数据能够与NamedNativeQuery匹配,域实体的Class名称后跟一个点,需要在NamedNativeQuery的名称前面加上前缀.所以名称应该是(假设域实体是Group)'Group.getGroupDetails'. (10认同)
  • 在Spring数据存储库中使用的实体上必须存在@SqlResultSetMapping和@NamedNativeQuery批注(例如,对于公共接口,CustomRepository扩展了CrudRepository &lt;CustomEntity,Long&gt;,这是`CustomEntity`类) (2认同)

Waq*_*mon 9

您可以按照自己的方式编写本机或非本机查询,并且可以使用自定义结果类的实例包装 JPQL 查询结果。创建一个与查询中返回的列名称相同的 DTO,并创建一个具有与查询返回的相同序列和名称的全参数构造函数。然后使用以下方式查询数据库。

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")
Run Code Online (Sandbox Code Playgroud)

创建 DTO:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你说这也应该适用于本机查询?你能举个例子吗? (9认同)
  • OP 要求本地查询,但给出的示例是非本地查询 (4认同)

Ash*_*rma 5

我认为Michal的方法更好。但是,还有另一种方法可以从本机查询中获取结果。

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
Run Code Online (Sandbox Code Playgroud)

现在,您可以将此2D字符串数组转换为所需的实体。

  • 这太冒险了。如果表中的列顺序发生更改(例如,添加新列或修改现有列),则结果值将能够混合。例如,如果您的实体具有“ClientId”字段,但列从“client_id”更改为“user_id”,则很难检测到此更改,因为缺乏列名称验证。 (3认同)
  • 简洁大方 (2认同)