[英]Hibernate native query also gets JPA select
I have a two database tables, "A" and "B" with @OneToMany(mappedBy = "a") on a List<B> field in entity A, and a @ManyToOne on field Ba I ran into the "N+1" problem when doing default queries on A, so I am trying a native query such as:我有两个数据库表,“A”和“B”,实体 A 中的 List<B> 字段上带有 @OneToMany(mappedBy = "a") 和字段 Ba 上的 @ManyToOne 我遇到了“N+1 " 在 A 上进行默认查询时出现问题,因此我正在尝试本机查询,例如:
@Query(value="select * from A as a left join B as b " +
"on a.ID = b.b ",
nativeQuery=true)
This works in the sense that the data is mapped back to the entities as expected.这在数据按预期映射回实体的意义上起作用。
My problem is that I can see that Hibernate is doing a separate select for each B rather than using the results of the join.我的问题是我可以看到 Hibernate 正在为每个 B 做一个单独的选择,而不是使用连接的结果。 That is, I see in the console a sequence of:
也就是说,我在控制台中看到了一系列:
In other words, I've still got the "n+1" problem.换句话说,我仍然有“n+1”问题。
I figured that the @OneToMany and @ManyToOne annotations might be causing Hibernate to do these extra selects, but when I take them out, my IDE (IntelliJ) says:我认为 @OneToMany 和 @ManyToOne 注释可能会导致 Hibernate 执行这些额外的选择,但是当我将它们取出时,我的 IDE (IntelliJ) 说:
'Basic' attribute should not be a container
... on the List property in A. ... 在 A 中的 List 属性上。
How can I get it to map the results back in a single select with join?我怎样才能让它通过连接将结果映射回单个选择? Should I just give up on Hibernate and JPA?
我应该放弃 Hibernate 和 JPA 吗?
I am using spring-boot-start-data-jpa.2.5.4我正在使用 spring-boot-start-data-jpa.2.5.4
Native @Query
doesn't have sufficient mapping power, so it seems that Hibernate native query must be needed. Native
@Query
没有足够的映射能力,所以似乎必须需要Hibernate native query。
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.Session;
import org.hibernate.transform.BasicTransformerAdapter;
import org.springframework.stereotype.Repository;
// https://docs.spring.io/spring-data/jpa/docs/2.5.6/reference/html/#repositories.custom-implementations
@Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<A> getAll() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#sql-entity-associations-query
final Session sess = (Session) entityManager.getDelegate();
final List<A> res = sess
// If no duplicate column names, original sql can be used, too.
.createNativeQuery("select {a.*},{b.*} from A as a left join B as b on a.ID = b.a ")
.addEntity("a", A.class)
.addJoin("b", "a.bs")
.setResultTransformer(DistinctResultTransformer.INSTANCE)
.list();
return res;
}
// https://stackoverflow.com/q/12071014/4506703
static class DistinctResultTransformer extends BasicTransformerAdapter {
private static final long serialVersionUID = 1L;
static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer();
@Override
public List transformList(final List collection) {
final List<Object> res = new ArrayList<>();
for (final Object[] obj : (List<Object[]>) collection) {
if (!res.contains(obj[0])) {
res.add(obj[0]);
}
}
return res;
}
}
}
Above code executes 1 query:上面的代码执行 1 个查询:
select a.id as id1_0_0_, a.name as name2_0_0_,b.a as a3_1_0__, b.id as id1_1_0__, b.id as id1_1_1_, b.a as a3_1_1_, b.name as name2_1_1_
from A as a left join B as b on a.ID = b.a
You can use some methods avoiding N+1 problem.您可以使用一些方法来避免 N+1 问题。
Using JPQL fetch, instead of native-query:使用 JPQL fetch,而不是 native-query:
@Query("select distinct a from A a left join fetch a.bs")
List<A> getAllJpqlFetch();
Above code executes 1 query:上面的代码执行 1 个查询:
select distinct a0_.id as id1_0_0_, bs1_.id as id1_1_1_, a0_.name as name2_0_0_, bs1_.a as a3_1_1_, bs1_.name as name2_1_1_, bs1_.a as a3_1_0__, bs1_.id as id1_1_0__
from a a0_ left outer join b bs1_ on a0_.id=bs1_.a
Using JPA Criteria fetch, is equivalent to above JPQL:使用 JPA Criteria fetch,相当于上面的 JPQL:
@Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<A> getAllCriteria() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria-from-fetch
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<A> criteria = builder.createQuery(A.class);
final Root<A> root = criteria.from(A.class);
root.fetch("bs", JoinType.LEFT);
criteria.select(root).distinct(true);
return entityManager.createQuery(criteria).getResultList();
}
Above code executes 1 query:上面的代码执行 1 个查询:
select distinct a0_.id as id1_0_0_, bs1_.id as id1_1_1_, a0_.name as name2_0_0_, bs1_.a as a3_1_1_, bs1_.name as name2_1_1_, bs1_.a as a3_1_0__, bs1_.id as id1_1_0__
from a a0_ left outer join b bs1_ on a0_.id=bs1_.a
Using @Fetch(FetchMode.SUBSELECT)
:使用
@Fetch(FetchMode.SUBSELECT)
:
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
// ...
@Entity
public class A {
@OneToMany(mappedBy = "a")
@Fetch(FetchMode.SUBSELECT)
private List<B> bs;
// ...
}
// findAll() method implementation is auto-generated by Spring Data JPA
// https://docs.spring.io/spring-data/jpa/docs/2.5.6/reference/html/#repositories.core-concepts
repository.findAll();
Above code executes 2 queries(root entities and their relational entities):上面的代码执行 2 个查询(根实体及其关系实体):
select a0_.id as id1_0_, a0_.name as name2_0_ from a a0_
select bs0_.a as a3_1_1_, bs0_.id as id1_1_1_, bs0_.id as id1_1_0_, bs0_.a as a3_1_0_, bs0_.name as name2_1_0_
from b bs0_ where bs0_.a in (select a0_.id from a a0_)
I ended up using the following solution, given by DEWA Kazuyuki, above.我最终使用了上面由 DEWA Kazuyuki 提供的以下解决方案。 I'm copying it here because DEWA suggested several answers and I thought it useful to identify the particular one that worked for me.
我在这里复制它是因为 DEWA 提出了几个答案,我认为确定对我有用的特定答案很有用。 Thanks, DEWA.
谢谢,德瓦。
@Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<A> getAllCriteria() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria-from-fetch
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<A> criteria = builder.createQuery(A.class);
final Root<A> root = criteria.from(A.class);
root.fetch("bs", JoinType.LEFT);
criteria.select(root).distinct(true);
return entityManager.createQuery(criteria).getResultList();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.