Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA继承@EntityGraph包含子类的可选关联_Java_Postgresql_Hibernate_Jpa_Entitygraph - Fatal编程技术网

Java JPA继承@EntityGraph包含子类的可选关联

Java JPA继承@EntityGraph包含子类的可选关联,java,postgresql,hibernate,jpa,entitygraph,Java,Postgresql,Hibernate,Jpa,Entitygraph,给定以下域模型,我希望加载所有Answers,包括它们的值和各自的子项,并将其放入AnswerDTO中,然后转换为JSON。我有一个可行的解决方案,但它有一个N+1问题,我想通过使用一个特别的@EntityGraph来解决这个问题。所有关联都已配置LAZY 失败,因为所选字段当然只是部分值实体的一部分: Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Valu

给定以下域模型,我希望加载所有
Answer
s,包括它们的
值和各自的子项,并将其放入
AnswerDTO
中,然后转换为JSON。我有一个可行的解决方案,但它有一个N+1问题,我想通过使用一个特别的
@EntityGraph
来解决这个问题。所有关联都已配置
LAZY

失败,因为
所选
字段当然只是部分
实体的一部分:

Unable to locate Attribute  with the the given name [selected] on this ManagedType [x.model.Value];

如果值是
MCValue
,我如何告诉JPA仅尝试获取所选的
关联?我需要类似于
optionalAttributePath
的东西,我不知道Spring数据在那里做什么,但要做到这一点,通常必须使用
TREAT
操作符才能访问子关联,但该操作符的实现非常有缺陷。 Hibernate支持隐式子类型属性访问,这正是您在这里需要的,但显然Spring数据无法正确处理这一点。我可以建议您看看,一个在JPA之上工作的库,它允许您根据实体模型映射任意结构。您可以以类型安全的方式映射DTO模型,也可以映射继承结构。用例的实体视图可以如下所示

@EntityView(Answer.class)
interface AnswerDTO {
  @IdMapping
  Long getId();
  ValueDTO getValue();
}
@EntityView(Value.class)
@EntityViewInheritance
interface ValueDTO {
  @IdMapping
  Long getId();
}
@EntityView(TextValue.class)
interface TextValueDTO extends ValueDTO {
  String getText();
}
@EntityView(RatingValue.class)
interface RatingValueDTO extends ValueDTO {
  int getRating();
}
@EntityView(MCValue.class)
interface TextValueDTO extends ValueDTO {
  @Mapping("selected.id")
  Set<Long> getOption();
}

我的最新项目使用了GraphQL(对我来说是第一次),我们在N+1查询方面遇到了一个大问题,并试图优化查询,以便在需要时只连接表。我发现这是不可替代的。它扩展了
JpaRepository
,并添加了将实体图传递给查询的方法。然后,您可以在运行时构建动态实体图,以便仅为所需的数据添加左连接

我们的数据流如下所示:

  • 接收GraphQL请求
  • 解析GraphQL请求并转换为查询中的实体图节点列表
  • 从发现的节点创建实体图,并传递到存储库中执行
  • 为了解决不将无效节点包括到实体图中的问题(例如,graphql中的
    \uuu typename
    ),我创建了一个实用程序类来处理实体图的生成。调用类传入它为其生成图形的类名,然后根据ORM维护的元模型验证图形中的每个节点。如果节点不在模型中,则会将其从图形节点列表中删除。(此检查需要是递归的,并检查每个子项)


    在找到这一点之前,我尝试了Spring JPA/Hibernate文档中推荐的投影和所有其他替代方案,但似乎没有什么能优雅地解决问题,或者至少在您的评论之后编辑了大量额外的代码:

    很抱歉,我没有在第一轮中理解您的问题,您的问题发生在spring data启动时,而不仅仅是在您尝试调用findAll()时

    因此,您现在可以浏览完整的示例,可以从my github中获取:

    您可以轻松复制并修复此项目中的问题

    实际上,Spring数据和hibernate在默认情况下无法确定“选定”图形,您需要指定收集选定选项的方式

    因此,首先,您必须声明类的NamedEntityGraphsAnswer

    如您所见,对于类答案的属性,有两个名称dentityGraph

    • 所有值的第一个与负载没有特定关系

    • 第二个用于特定的多选值。如果删除此项,则会复制异常

    其次,如果要获取类型为LAZY的数据,则需要处于事务上下文answerRepository.findAll()

    @实体
    @表(name=“answer”)
    @姓名识别图({
    @姓名识别图(
    name=“graph.Answer”,
    attributeNodes=@NamedAttributeNode(value=“value”)
    ),
    @姓名识别图(
    name=“graph.AnswerMultichoice”,
    attributeNodes=@NamedAttributeNode(value=“value”),
    子图={
    @命名子图(
    name=“graph.AnswerMultichoice.selected”,
    属性节点={
    @NamedAttributeNode(“选定”)
    }
    )
    }
    )
    }
    )
    公开课答案
    {
    @身份证
    @GeneratedValue(策略=GenerationType.IDENTITY)
    @列(updateable=false,nullable=false)
    私有int-id;
    @OneToOne(级联=级联类型.ALL)
    @JoinColumn(name=“value\u id”,referencedColumnName=“id”)
    私人价值;
    // ..
    }
    
    只有当关联属性是超类的一部分并且也是所有子类的一部分时,才能使用。否则,
    EntityGraph
    将始终失败,并出现当前获得的
    异常

    避免N+1选择问题的最佳方法是将查询分为两个查询:

    第一个查询使用
    EntityGraph
    获取
    MCValue
    实体,以获取所选
    属性映射的关联。在该查询之后,这些实体将存储在Hibernate的一级缓存/持久性上下文中。Hibernate将在处理第二个查询的结果时使用它们

    @Query("SELECT m FROM MCValue m") // add WHERE clause as needed ...
    @EntityGraph(attributePaths = {"selected"})
    public List<MCValue> findAll();
    
    因为我们已经获取了所有
    MCValue
    实体以及相关的
    所选的
    实体,所以现在我们获得了
    应答
    实体以及初始化的
    关联。如果关联包含一个
    MCValue
    实体,那么它所选择的
    关联也将被初始化。

    Hmm感谢您的提示
    @EntityView(Answer.class)
    interface AnswerDTO {
      @IdMapping
      Long getId();
      ValueDTO getValue();
    }
    @EntityView(Value.class)
    @EntityViewInheritance
    interface ValueDTO {
      @IdMapping
      Long getId();
    }
    @EntityView(TextValue.class)
    interface TextValueDTO extends ValueDTO {
      String getText();
    }
    @EntityView(RatingValue.class)
    interface RatingValueDTO extends ValueDTO {
      int getRating();
    }
    @EntityView(MCValue.class)
    interface TextValueDTO extends ValueDTO {
      @Mapping("selected.id")
      Set<Long> getOption();
    }
    
    @Transactional(readOnly = true)
    interface AnswerRepository extends Repository<Answer, Long> {
      List<AnswerDTO> findAll();
    }
    
    SELECT
      a.id, 
      v.id,
      TYPE(v), 
      CASE WHEN TYPE(v) = TextValue THEN v.text END,
      CASE WHEN TYPE(v) = RatingValue THEN v.rating END,
      CASE WHEN TYPE(v) = MCValue THEN s.id END
    FROM Answer a
    LEFT JOIN a.value v
    LEFT JOIN v.selected s
    
    @Query("SELECT m FROM MCValue m") // add WHERE clause as needed ...
    @EntityGraph(attributePaths = {"selected"})
    public List<MCValue> findAll();
    
    @Query("SELECT a FROM Answer a")
    @EntityGraph(attributePaths = {"value"})
    public List<Answer> findAll();