【问题标题】:Spring Data Projection with OneToMany returns too many results带有 OneToMany 的 Spring Data Projection 返回太多结果
【发布时间】:2018-02-09 18:59:34
【问题描述】:

我有一个具有单对多关系 (ContactInfo) 的 JPA 实体 (Person)。

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private String lastname;
    private String sshKey;
    @OneToMany(mappedBy = "personId")
    private List<ContactInfo> contactInfoList;
}

@Entity
public class ContactInfo {
    @Id
    @GeneratedValue
    private Integer id;
    private Integer personId;
    private String description;
}

我已经定义了一个投影接口,其中包括here 中描述的这种单对多关系。

public interface PersonProjection {
    Integer getId();
    String getName();
    String getLastname();
    List<ContactInfo> getContactInfoList();
}

public interface PersonRepository extends JpaRepository<Person,Integer> {
    List<PersonProjection> findAllProjectedBy();
}

当我使用 findAllProjectedBy 检索数据时,结果包含太多行。看起来返回的数据是类似于以下内容的连接查询的结果:

select p.id, p.name, p.lastname, ci.id, ci.person_id, ci.description 
from person p 
join contact_info ci on ci.person_id = p.id

例如对于这个数据集:

insert into person (id,name,lastname,ssh_key) values (1,'John','Wayne','SSH:KEY');

insert into contact_info (id, person_id, description) values (1,1,'+1 123 123 123'), (2,1,'john.wayne@west.com');

findAllProjectedBy 方法返回 2 个对象(不正确),标准 findAll 方法返回 1 个对象(正确)。

完整项目是here

我做了一些调试,似乎问题出在 jpa 查询上。 findAll 方法使用此查询:

select generatedAlias0 from Person as generatedAlias0

findAllProjectedBy 使用这个查询:

select contactInfoList, generatedAlias0.id, generatedAlias0.name, generatedAlias0.lastname from Person as generatedAlias0 
left join generatedAlias0.contactInfoList as contactInfoList

有人知道如何解决这种无效行为吗?

【问题讨论】:

  • 这是正常行为。您到底想通过投影实现什么?也许您只需要加载包含ContactInfos 的Persons 列表?...
  • '正常行为' - 在任何地方都有记录?我正在实现的一般用例是带有视图参数的 api。视图可以是完整的 (findAll) 或有限的 (findAllProjectedBy)。我希望能够限制从数据库中检索到的数据。上面的例子非常简单。
  • 对我来说似乎是一个错误/缺失的功能。你能打开一个问题吗? jira.spring.io/browse/DATAJPA/…
  • 我正在使用 Spring Boot v2.0.0,我将得到整个对象......而不仅仅是那些用 Projection 编写的字段

标签: java jpa spring-data one-to-many projection


【解决方案1】:

此处描述了此问题的快速修复方法: https://jira.spring.io/browse/DATAJPA-1173

您需要使用@Value 注释来描述单个投影属性之一。对于上面发布的示例,您最终会得到:

import java.util.List;
import org.springframework.beans.factory.annotation.Value;

public interface PersonProjection {
    @Value("#{target.id}")
    Integer getId();
    String getName();
    String getLastname();
    List<ContactInfo> getContactInfoList();
}

【讨论】:

  • 这个注解使这个投影open而不是closed。他们通过假设投影默认为open 来修复此错误,以防其中一种方法返回集合或地图。
  • 当您使用 Dto 对象代替接口时,它不起作用!我的原因是:org.hibernate.hql.internal.ast.QuerySyntaxException:期待 OPEN,在第 1 行第 157 列附近找到“,”
  • @Sam 好点。这样做的结果是,当在投影中返回实体列的子集时,休眠选择所有列。
  • @pustypawel 我相信您应该使用接口而不是 dto pojos 进行投影。无论如何,我发现 Spring Data 对于原型设计和 rnd 非常有用,但是随着场景变得更加复杂,lib 显示出它的局限性(性能、使用可变参数/结果 cols 的动态查询)。 IMO 这是一个值得一试的好选择:link.
  • @lbd01 therr 也是 graphql,它甚至可以在运行时定义这样的投影,而 sql 实际上只选择需要的东西。此外,对于实体的复杂编译时视图,应创建一个 dto 类并使用 dto 的完全限定构造函数作为选择参数使用@Query。此功能在 jpa 级别。 Google 映射 dto hibernate 的最佳方式,并会看到 Vlad 的帖子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
相关资源
最近更新 更多