Spring Data JPA:查询如何返回非实体对象或对象列表?

39
我在项目中使用Spring Data JPA。我正在处理数百万条记录。我有一个需求,需要从各种表中获取数据并构建一个对象,然后在UI上绘制它。现在如何通过Spring数据存储库实现这一点呢?我已经阅读到可以通过命名本地查询来实现。
如果命名本地查询不返回实体或实体列表,我们可以使用 @SqlResultSetMapping 注释将查询结果映射到正确的返回类型。但是当我尝试使用@SqlResultSetMapping时,它需要另一个实体结果。我的理解是它只是将某些查询结果转换为实体结果集,但我想要非实体对象的结果集。
@SqlResultSetMapping(
    name="studentPercentile",
    entities={
        @EntityResult(
           entityClass=CustomStudent.class,
              fields={
                  @FieldResult(name="id", column="ID"),
                  @FieldResult(name="firstName", column="FIRST_NAME"),
                   @FieldResult(name="lastName", column="LAST_NAME")
              }         
        )
   }
) 
@NamedNativeQuery(
    name="findStudentPercentile", 
    query="SELECT * FROM STUDENT", 
    resultSetMapping="studentPercentile")

在上面的例子中,我只是试图从学生实体中获取结果,并将其放入另一个非实体pojo“CustomStudent”中。(这个例子只是为了POC目的而执行,实际用例更加复杂,使用复杂查询返回不同的结果集)。

如何实现上述用例?除了使用命名查询外,还有其他方法可以使我的存储库方法返回非实体对象吗?


我最近遇到了同样的问题,很高兴看到有人发布并得到解决方案! - Phoebe Li
我们手头遇到了一个类似的问题,需要从三个表中获取记录计数,最终我们使用了Spring JdbcTemplate(尽管在项目中通常使用Spring JPA Hibernate)。仅仅为了获取记录计数而编写实体等工作量太大了。 - JavaTec
答案与此问题相同。 - Ilya Serbis
4个回答

26

你可以这样做

@NamedQuery(name="findWhatever", query="SELECT new path.to.dto.MyDto(e.id, e.otherProperty) FROM Student e WHERE e.id = ?1")

那么MyDto对象只需要定义一个带有正确字段的构造函数,例如:

然后MyDto对象只需要定义具有正确字段的构造函数,即:

public MyDto(String id, String otherProperty) { this.id = id; this.otherProperty = otherProperty; }

2
我可以写像这样的代码吗 SELECT new path.to.dto.MyDto(e.id, new path.to.dto.OtherDto) - Don Jose
1
不过在你的MyDto中,你可以有一个OtherDto属性,在MyDto的构造函数中使用传递给MyDto构造函数的数据设置OtherDto属性。或者你可以使用@SqlResultSetMapping注释。 - tlavarea
当我写这个时,编译失败并显示“验证公共抽象方法查询失败”。 - Nikhil Sahu

24

当我第一次看到这个时非常惊讶,但是,是的,你可以使用@SqlResultSetMapping将查询结果映射到标量和托管实体。

我猜最好的方法是跳过自动映射。没有映射的查询将返回List<Object[]>,您可以按需要进行映射。

另一种方法是使用@MappedSuperclass。 作为@MappedSuperclass(在您的情况下为CustomStudent)指定的类可能(虽然不确定100%)用于@SqlResultSetMapping。但是您需要引入继承层次结构,即您的Student实体必须扩展CustomStudent。这通常会使正确的OO设计变得有些艰难,因为继承有点人为......


你是指原始类型吗? - Alex78191
1
你可以使用ConstructorResult https://dev59.com/8FgQ5IYBdhLWcg3wXirH#42942353 - Alex78191

18

JPA 2.1的ConstructorResult怎么样?

@SqlResultSetMapping(
    name="studentPercentile",
    classes={
        @ConstructorResult(
            targetClass=CustomStudent.class,
            columns={
                @ColumnResult(name="ID"),
                @ColumnResult(name="FIRST_NAME"),
                @ColumnResult(name="LAST_NAME")
            }
        )
    }
)

@NamedNativeQuery(name="findStudentPercentile", query="SELECT * FROM STUDENT", resultSetMapping="studentPercentile")

3
你好,这听起来不错,但NamedQuery仍然必须附加到实体才能成为持久化单元的一部分吗? - romainbsl

1
我们也可以使用JSON帮助进行解析。
类级别声明。
@Autowired
private EntityManager em;
private ObjectMapper mapper = new ObjectMapper(); 

主要代码。

Query query = em.createNativeQuery(argQueryString);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> result = nativeQuery.getResultList();
List<CustomStudent> resultList = result.stream()
   .map(o -> {
         try {
              return 
            mapper.readValue(mapper.writeValueAsString(o),CustomStudent.class);
       } catch (Exception e) {
           ApplicationLogger.logger.error(e.getMessage(),e);
       }
     return null;
    }).collect(Collectors.toList());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接