我有一个非常复杂的条件查询运行JPA(Hibernate 4.x作为提供程序)。我试图提高性能,因为实际上我有一个N + 1查询问题。代码如下所示:
@Override
public SearchResult<EntityA> findByAnyCritere(SomeCriteria critere, Integer page, Integer pageSize, String... orderBys) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
// count all entities
Long unfilteredCount = countAll();
// count filtered entities
Long count = unfilteredCount;
CriteriaQuery<EntityA> criteriaQuery = builder.createQuery(EntityA.class);
Root<EntityA> from = criteriaQuery.from(getEntityClass());
Join<EntityA, EntityB> joinB = from.join("entityB", JoinType.LEFT);
Join<EntityB, EntityC> joinC = joinB.join("entityC", JoinType.LEFT);
Join<EntityB, EntityD> joinD = joinB.join("entityD", JoinType.LEFT);
Join<EntityD, EntityE> joinE = joinD.join("entityE", JoinType.LEFT);
Predicate predicate = builder.equal(from.<EntityF> get("entityF").<Long> get("id"), critere.getSomeId());
Predicate predicateKeyword = filterLookupByValue(critere.getAnyCriteria(), builder, criteriaQuery, LookupType.TEXT_KEYWORD, keywordJoin);
Predicate predicateNumberText = filterNumber(critere, builder, textJoin, false);
Subquery<Long> subqueryLibelle = filterLibelle(critere.getAnyCriteria(), builder, criteriaQuery, from);
if (StringUtils.isNotBlank(critere.getAnyCriteria())) {
criteriaQuery.where(builder.and(predicate,
builder.or(predicateKeyword, predicateNumberText, builder.exists(subqueryLibelle))));
} else {
criteriaQuery.where(builder.and(predicate));
}
criteriaQuery.select(from);
count = JpaUtils.count(getEntityManager(), criteriaQuery);
addSortingInstructionsIfNeeded(builder, criteriaQuery, from, orderBys);
TypedQuery<EntityA> typedQuery = getEntityManager().createQuery(criteriaQuery);
addPaginationRestrictionIfNeeded(typedQuery, page, pageSize);
List<EntityA> entities = typedQuery.getResultList();
LOG.debug("found {} entities", entities.size());
return toSearchResult(unfilteredCount, count, entities, page, pageSize);
}
我试图理解为什么我会运行很多查询。例如,如果我加载大小为10个项目的第2页,我可以看到Hibernate首先运行这样的查询:
select * from (
select inner2_.*, rownumber() over(order by order of inner2_) as rownumber_ from (
select
<<fields>>
from
EntityA versionelk0_
left outer join EntityB textversio1_ on versionelk0_.ID_TEXT_VERSION=textversio1_.ID
left outer join EntityC textentity2_ on textversio1_.ID_TEXT=textentity2_.ID
left outer join EntityD textdefini3_ on textversio1_.ID_TEXT_DEFINITION=textdefini3_.ID
left outer join EntityE lookupenti4_ on textdefini3_.ID_LOOKUP_KEYWORD=lookupenti4_.ID,
EntityF textentity6_
where
textversio1_.ID_TEXT=textentity6_.ID and versionelk0_.ID_VERSION_ELKAT=41
order by textentity6_.NUMBER asc
fetch first 20 rows only
) as inner2_
) as inner1_
where rownumber_ > 10 order by rownumber_
...它似乎将id提取到负载。
但在此之后,我可以看到启动了10个查询,每个查询都加载一个带有id的实体。
我希望只运行2个查询,第1个保持原样,但是第二个查询保留,如#34; WHERE ID(&lt;&gt;)&#34;
有人有解释(或更好,解决方案)吗?
编辑:相关映射(重新命名为保密)
@Entity
public class EntityA extends AbstractVersionedEntity<Long> {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@ManyToOne
@JoinColumn(name = "ID_TEXT_VERSION")
private EntityB entityB;
[...]
}
@Entity
public class EntityB extends AbstractVersionedEntity<Long> {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@OneToOne
@JoinColumn(name = "ID_TEXT")
private EntityC entityC;
@OneToOne
@JoinColumn(name = "ID_TEXT_DEFINITION")
private EntityD definition;
[...]
}
@Entity
public class EntityC extends AbstractVersionedEntity<Long> {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "NUMBER")
private String number;
[...]
}
@Entity
public class EntityD extends AbstractVersionedEntity<Long> {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@ManyToOne
@JoinColumn(name = "ID_LOOKUP_CHAPTER")
private EntityE chapter;
@ManyToOne
@JoinColumn(name = "ID_LOOKUP_CATEGORY")
private EntityE category;
@ManyToOne
@JoinColumn(name = "ID_LOOKUP_KEYWORD")
private EntityE keyword;
@ManyToOne
@JoinColumn(name = "ID_LOOKUP_RECIPIENT")
private EntityE recipient;
[...]
}
@Entity
public class EntityE extends AbstractVersionedEntity<Long> {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "KEY")
private String key;
@Column(name = "INDEX")
private Integer index;
@Column(name = "ACTIVE")
private Boolean active = Boolean.TRUE;
@ManyToOne
@JoinColumn(name = "ID_LOOKUPTYPE")
private EntityF type;
[...]
}
AbstractVersionedEntity类仅提供对@Version编号的支持,equals / hashCode / toString的默认实现和通用主键管理(在我的示例中)
大多数关系是延迟加载的,但我的问题不是属性是延迟加载的,而是我得到10个查询来加载10&#34; root&#34;实体。
由于
答案 0 :(得分:0)
很难说没有任何映射会导致这种行为的原因。如果您的实体A中有一对多的集合,请尝试使用@BatchSize注释,如
@BatchSize(size = 10)
@OneToMany
private Set<EntityB> items = new HashSet<>();
通过这个注释,Hibernate将使用size属性指定批量获取集合的内容,这意味着将使用一个Select语句获取实体A,并使用一个包含IN子句的Select语句获取集合条目,最多包含10个IDS。
https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/annotations/BatchSize.html
答案 1 :(得分:0)
我误解了用N个查询查询了哪个实体。事实上,它是我的根实体之间的关系,所以我的整个帖子都没有意义......请忽略它。谢谢