加载多对多关系并映射到实体

Load many-to-many relationship and map to entity

我是 Spring Boot/Hibernate 开发的新手,这是我的第一个大项目。

我有一个应用程序使用 Spring Boot、Hibernate 和 Hibernate Envers 来审计一些实体。 Hibernate Envers 设置为使用 ValidityAuditStrategy。 由于 Hibernate Envers 不支持开箱即用的急切加载 to-Many 关系,我试图找到一种方法来执行单个查询并检索我需要的所有数据,为了避免 N+1 查询问题,该问题目前正在扼杀我的性能:在我们的开发环境中,我们需要将近 2 分钟才能完全加载我们需要的实体以及所需的关系。

由于我需要检索经过审核的版本,因此我无法像在应用程序的其他部分中使用的那样利用 EntityGraph(至少据我所知)。

我需要解决的一个情况有 4 个实体 ParameterFormulaValueVariable,它们是这样相关的(仅显示相关的部分,ValueVariable 是实体,此处未列出)

@Entity
public class Parameter {
  @OneToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "formula_id", referencedColumnName = "id", nullable = false)
  private Formula formula;
}

@Entity
public class Formula {
  @ManyToOne(optional = false)
  @JoinColumn(name = "tag_id")
  private Value tag;

  @ManyToMany
  @JoinTable(
      name = "Formulas_Variables",
      joinColumns = @JoinColumn(name = "formula_id"),
      inverseJoinColumns = @JoinColumn(name = "variable_id"))
  private Set<Variable> variables = new HashSet<>();
}

我尝试做的是创建自定义查询并使用 @NamedNativeQuery@SqlResultSetMapping 映射结果,但是,即使 Hibernate 正在创建 Formula 和 [=16 之间的关系=] 正确,它没有在 variables 字段上创建数组。我使用的查询和映射如下:

@SqlResultSetMapping(name = "Parameter.findAllByRevisionMapping", entities = {
    @EntityResult(entityClass = Parameter.class, fields = {
        @FieldResult(name = "id", column = "id"),
        @FieldResult(name = "formula", column = "formula_id")
    }),
    @EntityResult(entityClass = Formula.class, fields = {
        @FieldResult(name = "id", column = "formulaId"),
        @FieldResult(name = "tag", column = "tag_id"),
        @FieldResult(name = "variables", column = "variable_id")
    }),
    @EntityResult(entityClass = DomainValue.class, fields = {
        @FieldResult(name = "id", column = "tag_id")
    }),
    @EntityResult(entityClass = Variable.class, fields = {
        @FieldResult(name = "id", column = "variableId")
    })
})
@NamedNativeQuery(name = "Parameter.findAllByRevision", query = "SELECT om_c_p_aud.id,\n"
    + "       f_aud.id AS formulaId,\n"
    + "       dv_aud.id AS tag_id,\n"
    + "       fv_aud.formula_id AS formula_id,\n"
    + "       v_aud.id AS variable_id,\n"
    + "       v_aud.id AS variableId\n"
    + "FROM Parameters_AUD om_c_p_aud\n"
    + "         LEFT OUTER JOIN Formulas_AUD f_aud\n"
    + "                    ON f_aud.id = om_c_p_aud.formula_id AND f_aud.REV <= ?1 AND\n"
    + "                       f_aud.REVTYPE <> 2 AND (f_aud.REVEND > ?1 OR\n"
    + "                                               f_aud.REVEND IS NULL)\n"
    + "         LEFT OUTER JOIN Values_AUD dv_aud\n"
    + "                    ON dv_aud.id = f_aud.tag_id AND dv_aud.REV <= ?1 AND\n"
    + "                       dv_aud.REVTYPE <> 2 AND (dv_aud.REVEND > ?1 OR\n"
    + "                                                dv_aud.REVEND IS NULL)\n"
    + "         LEFT OUTER JOIN Formulas_Variables_AUD fv_aud\n"
    + "                    ON fv_aud.formula_id = f_aud.id AND fv_aud.REV <= ?1 AND\n"
    + "                       fv_aud.REVTYPE <> 2 AND (fv_aud.REVEND > ?1 OR\n"
    + "                                                fv_aud.REVEND IS NULL)\n"
    + "         LEFT OUTER JOIN Variables_AUD v_aud\n"
    + "                    ON v_aud.id = fv_aud.variable_id AND v_aud.REV <= ?1 AND\n"
    + "                       v_aud.REVTYPE <> 2 AND (v_aud.REVEND > ?1 OR\n"
    + "                                               v_aud.REVEND IS NULL)\n"
    + "WHERE om_c_p_aud.REV <= ?1\n"
    + "  AND om_c_p_aud.REVTYPE <> 2\n"
    + "  AND (om_c_p_aud.REVEND > ?1 OR\n"
    + "       om_c_p_aud.REVEND IS NULL)", resultSetMapping = "Parameter.findAllByRevisionMapping")

直接在数据库上执行查询的结果示例如下:

我在调用 findAllByRevision 查询时收到的 json 个对象的结果数组是这样的

[
   {
      "id":1,
      "formula":{
         "id":52,
         "tag":{
            "id":20
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":null
      }
   }
]

而我期望的是

[
   {
      "id":1,
      "formula":{
         "id":52,
         "tag":{
            "id":20
         },
         "variables":[
            {
               "id":4
            }
         ]
      }
   },
   {
      "id":2,
      "formula":{
         "id":88,
         "tag":{
            "id":24
         },
         "variables":[
            {
               "id":3
            },
            {
               "id":23
            },
            {
               "id":33
            },
            {
               "id":34
            },
            {
               "id":35
            },
            {
               "id":52
            }
         ]
      }
   }
]

有谁知道为什么它不创建公式 <-> 变量关系?我尝试检查在类似情况下使用 EntityGraph 时由 Hibernate 创建的查询,在我看来它与上面显示的相同。不过,我无法检查那种情况下使用的映射(再次以我的理解)。

您应该能够使用 HQL 为已审核的关联指定连接提取。被审计实体就像一个普通实体。实体名称通常以“_AUD”为后缀,因此如果您想查询 Parameters 的审计信息,您可以查询 Parameters_AUD,例如:

SELECT p
FROM Parameters_AUD p
LEFT JOIN FETCH p.values
LEFT JOIN FETCH p.variables