如何在Spring Data JPA中使用投影接口和分页功能?

17

我正在尝试使用Spring Data的新功能投影来获取部分实体(NetworkSimple)的页面。

我已经查阅了文档,如果我只请求以下内容:

Collection<NetworkSimple> findAllProjectedBy();

它是可行的,但如果我正在使用分页:

Page<NetworkSimple> findAllProjectedBy(Pageable pageable);

它会抛出一个错误:

org.hibernate.jpa.criteria.expression.function.AggregationFunction$COUNT cannot be cast to org.hibernate.jpa.criteria.expression.CompoundSelectionImpl

有人已经使用过这个了吗?

我的NetworkSimple类如下:

public interface NetworkSimple {
    Long getId();

    String getNetworkName();

    Boolean getIsActive();
}
7个回答

15

注意:这个功能应该按照原始帖子中描述的方式工作,但由于这个错误,它没有正常工作。该bug已在Hopper SR2版本中修复,如果你使用的是早期版本,则以下解决方法可以起作用。

在Spring Data JPA 1.10 (Hopper)中引入了新的查询投影功能,可以与Pageable一起使用。您需要使用@Query注释并手动编写查询所需的字段,还必须使用AS进行别名操作,以便Spring Data能够确定如何投影结果。在spring boot存储库的spring-boot-samples部分中有一个很好的例子。

在您的示例中,这将非常简单:

@Query("SELECT n.id AS id, n.name AS networkName, n.active AS isActive FROM Network n")
Page<NetworkSimple> findAllProjectedBy(Pageable pageable);

我假设您的实体看起来像这样:

@Entity
public class Network
{
    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @Column
    private boolean active;

    ...
}

Spring Data会自动为分页信息推导出计数查询。也可以在查询中使用联接以获取关联项,然后在投影中对其进行总结。


1
这不是完全正确的。这实际上应该按照原贴作者的期望工作,但您提供了一个有效的解决方法。我已经更改了原始帖子提交的 票证,以使用您的解决方法更新文档以修复实际的错误。如果您能更新您的回答并提及它将很酷。修复程序将在Hopper SR2中发布。 - Oliver Drotbohm
票已经固定,快照版本可用,将包含在 Hopper 的 SR2 中。 - Oliver Drotbohm
1
@OliverGierke,我已经更新了答案。我曾经认为这只是一个限制,因为我找不到任何使用它的例子。 - Robert Hunt

2
即使在spring-data-jpa 1.11.4中,类似以下的代码:
public interface NetworkRepository extends JpaRepository<Network, String> {
    Page<NetworkSimple> findAll(Pageable pageable);
}

无法编译;报告错误

findAll(org.springframework.data.domain.Pageable) in NetworkRepository clashes with findAll(org.springframework.data.domain.Pageable) in org.springframework.data.repository.PagingAndSortingRepository
return type org.springframework.data.domain.Page<NetworkSimple> is not compatible with org.springframework.data.domain.Page<Network>

我们找到的解决方法是将findAll重命名为findAllBy,例如:
public interface NetworkRepository extends JpaRepository<Network, String> {
    Page<NetworkSimple> findAllBy(Pageable pageable);
}

1

1
你可以像这样使用带有Pageable的接口投影:
Page<NetworkSimple> findPagedProjectedBy(Pageable pageable);

带一些参数:

Page<NetworkSimple> findPagedProjectedByName(String name, Pageable pageable);

0

如果我删除“By”,它会返回一个错误消息,因为它无法创建查询。org.springframework.data.mapping.PropertyReferenceException: 未找到类型Network的属性findAllProjected! - David Magalhães
我的错,你应该使用findAll方法名称。 - Michael Desigaud
但我不能使用它,因为返回的是一个网络对象,而我想要投影。 - David Magalhães
似乎您可以向存储库添加注释以指定投影:https://github.com/spring-projects/spring-data-examples/blob/master/rest/projections/src/main/java/example/springdata/rest/projections/CustomerRepository.java - Michael Desigaud

0

使用分页实现接口投影

1. 我们的ResourceEntity.java

@Getter
@Setter
@NoArgsConstructor
@Entity
public class ResourceEntity{
    private Long id;
    private String name;
}

2. 创建投影接口名称ProjectedResource.java,将从仓库层方法收集的数据映射到SQL查询中

public interface ProjectedResource {
  Long getId();
  String getName();
  String getAnotherProperty();
}

3. 创建存储库层方法:getProjectedResources()

  • 我们考虑数据库表名为resource
  • 我们仅在此处获取idname
@Query(name="select id, name, anotherProperty from resource", countQuery="select count(*) from resource", nativeQuery=true)
Page<ProjectedResource> getProjectedResources(Pageable page);

希望问题能够得到解决!


-1

您可以使用:

@Query("SELECT n FROM Network n")
Page<? extends NetworkSimple> findAllProjectedBy(Pageable pageable);

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