如何使JPA使用单个联接来获取双方条件的列
我的问题是以下
是有两个实体类,我们将其称为entity1
和entity2
,在之间,即一对多的关系,即一个entity1 包含多个
entity2
s,entity2
可能只有一个entity1
:
@Entity
@Table(name = "entity1")
public class Entity1 {
int x;
int y;
...
@LazyCollection(LazyCollectionOption.TRUE)
@OneToMany(mappedBy = "e1", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Entity2> entity2s = new HashSet<>();
}
@Entity
@Table(name = "entity2")
public class Entity2 {
int a;
int b;
...
@ManyToOne
@JoinColumn(name = "entity1_id")
@JsonBackReference
private Entity1 e1;
}
现在我想发出查询以检索entity2
s with whore entity2
及其相应的entity1
:
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>' AND e2.e1.x = '<val3>' AND e2.e1.y ='<val4>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
因此,这种方法的问题是,它确实得到了需要的entity2
s通过交叉加入entity1
和entity2
具有指定条件的表,但为每个条件提供了e1
s entity2
在结果中带有单独的查询。
因此,例如,如果加入的结果为5 entity2
s,则将对entity1
表有5个其他查询。
我尝试在entity2
中设置@manytoone
@manytoone(fetch = fetchType.lazy)>,但这无济于事。我想这是预期的,因为懒惰
只会将e1
s的检索推迟,但不会完全消除它。
接下来,我阅读了有关@entityGraph
的信息,并将其添加到entity2
:
@Entity
@Table(name = "entity2")
@NamedEntityGraph(name = "graph.entity2.entity1",
attributeNodes = { @NamedAttributeNode("e1") })
public class Entity2 {
int a;
int b;
...
@ManyToOne
@JoinColumn(name = "entity1_id")
@JsonBackReference
private Entity1 e1;
}
在存储库中,我添加了它:
@EntityGraph(value = "graph.entity2.entity1")
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>' AND e2.e1.x = '<val3>' AND e2.e1.y ='<val4>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
在这种情况下,单独的SQL查询消失了,EntityGraph确实可以左JOIN及其结果包含来自entity1
和Entity2
的列,但是因为e2.e1
的条件仍在子句,它添加了另外一个不必要的交叉加入,entity1
表(e2.e1
条件都在该交叉上加入中检查)。
我找不到摆脱额外交叉加入的方法,所以现在我正在使用以下查询:
@EntityGraph(value = "graph.entity2.entity1")
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
基本上我会得到entity2
s,在应用程序中,我根据条件过滤出来entity1
(e2.e1.x ='&lt; val3&gt;'和e2.e1.y ='&lt; val4&gt;'
)。
是否有一种方法可以使其仅适用于两个实体条件,而不仅仅是entity2
条件?我现在这样做的方式对我来说似乎并不正确,我觉得只有使用存储库方法就可以做到这一点,而无需涉及应用程序。感谢有关此
UPD的任何帮助。在@qoery> @query
注释中,阅读有关
(Entity Graph被禁用了)。我试图启用实体图,但删除了例外,表明实体图不能与本机查询一起使用,这是可以预期的nativeQuery
option> option(nativeQuery = true
),该注释允许指定原始查询,从而绕过基于实体的查询,但是查询仍然使用entity1_id
My problem is the following,
There are two entity classes, let's call them Entity1
and Entity2
with One-to-Many relationship in between, i.e. one Entity1
contains multiple Entity2
s, and Entity2
may have only one Entity1
:
@Entity
@Table(name = "entity1")
public class Entity1 {
int x;
int y;
...
@LazyCollection(LazyCollectionOption.TRUE)
@OneToMany(mappedBy = "e1", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Entity2> entity2s = new HashSet<>();
}
@Entity
@Table(name = "entity2")
public class Entity2 {
int a;
int b;
...
@ManyToOne
@JoinColumn(name = "entity1_id")
@JsonBackReference
private Entity1 e1;
}
Now I would like to issue a query for retrieving Entity2
s with WHERE conditions for both Entity2
and its corresponding Entity1
:
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>' AND e2.e1.x = '<val3>' AND e2.e1.y ='<val4>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
So the problem with this approach is that, it indeed gets desired Entity2
s by cross joining entity1
and entity2
tables with specified WHERE conditions BUT it fetches e1
s for each of those Entity2
s in the result with a separate query.
So for example if the result of join is 5 Entity2
s, there will be 5 additional queries to entity1
table.
I tried to set @ManyToOne
in Entity2
as @ManyToOne(fetch = FetchType.LAZY)
but it didn't help. I guess that's expected because LAZY
would simply postpone the retrieval of e1
s but wouldn't eliminate it completely.
Next, I read about @EntityGraph
, and added it to Entity2
:
@Entity
@Table(name = "entity2")
@NamedEntityGraph(name = "graph.entity2.entity1",
attributeNodes = { @NamedAttributeNode("e1") })
public class Entity2 {
int a;
int b;
...
@ManyToOne
@JoinColumn(name = "entity1_id")
@JsonBackReference
private Entity1 e1;
}
and in the repository, I added it as:
@EntityGraph(value = "graph.entity2.entity1")
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>' AND e2.e1.x = '<val3>' AND e2.e1.y ='<val4>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
In this case, the separate SQL queries disappear, EntityGraph does left join and its result contains columns from both entity1
and entity2
, BUT because the conditions for e2.e1
are still in WHERE clause, it adds ONE MORE unnecessary cross join with entity1
table (e2.e1
conditions are checked in that cross join).
I couldn't find a way to get rid of that extra cross join, so now I'm using the following query:
@EntityGraph(value = "graph.entity2.entity1")
@Query("SELECT " +
" e2 " +
"FROM " +
" Entity2 e2 " +
"WHERE " +
" e2.a = '<val1>' AND e2.b = '<val2>'")
List<Entity2> findMyEntity2s(
@Param...,
@Param...,
);
So basically I get Entity2
s and in the application I filter out based on conditions of Entity1
(e2.e1.x = '<val3>' AND e2.e1.y ='<val4>'
).
Is there a way to make it work with a single join only, for both entity's conditions, not only Entity2
conditions? The way I'm doing it now, does not seem correct and efficient to me, and I feel there's a way to do that using repository method only, without involving the app. Would appreciate any help on this
UPD. Read about nativeQuery
option (nativeQuery = true
) for @Query
annotation, which allows specifying a raw query and thus bypassing entity-based query, but the query still fetches many-to-one e1
field, using entity1_id
(entity graph was disabled). I tried to enable entity graph but it dropped exception stating that entity graph cannot be used with native query, which is expected
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是经典的N + 1查询问题。
您可以在此处阅读详细信息: 1-问题 - 问题/
在您的查询中,附加:
这将在第一个和单个查询中用E2获取E1。
不要忘记;始终使用fetchtype.lazy并使用
加入Fetch
获取实体。否则,在项目扩大的范围时,您将陷入较大的质量。此外,为什么在实体课程中使用Jaxson注释?使用实体仅访问DAO,然后将其映射到另一个DTO,以便在其他地方使用。
This is the classic n + 1 query problem.
You can read the detail here: https://vladmihalcea.com/n-plus-1-query-problem/
In your query, append:
This will fetch e1 with e2 in the first and single query.
Don't forget; always use FetchType.LAZY and fetch your entities with
JOIN FETCH
. Otherwise, you will get into a big mass while the scope of the project enlarges.In addition, why do you use Jaxson annotations in your Entity classes? Use entities for only DAO access and map them to another DTOs to use elsewhere.