加载多对多关系并映射到实体
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 个实体 Parameter
、Formula
、Value
和 Variable
,它们是这样相关的(仅显示相关的部分,Value
和 Variable
是实体,此处未列出)
@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
我是 Spring Boot/Hibernate 开发的新手,这是我的第一个大项目。
我有一个应用程序使用 Spring Boot、Hibernate 和 Hibernate Envers 来审计一些实体。 Hibernate Envers 设置为使用 ValidityAuditStrategy。 由于 Hibernate Envers 不支持开箱即用的急切加载 to-Many 关系,我试图找到一种方法来执行单个查询并检索我需要的所有数据,为了避免 N+1 查询问题,该问题目前正在扼杀我的性能:在我们的开发环境中,我们需要将近 2 分钟才能完全加载我们需要的实体以及所需的关系。
由于我需要检索经过审核的版本,因此我无法像在应用程序的其他部分中使用的那样利用 EntityGraph(至少据我所知)。
我需要解决的一个情况有 4 个实体 Parameter
、Formula
、Value
和 Variable
,它们是这样相关的(仅显示相关的部分,Value
和 Variable
是实体,此处未列出)
@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