Java OneToMany在spring jpa中包含筛选子句的问题
在SpringJPA中对@OneToMany关系使用时,我当前在MYSQL8/H2测试用例中得到了意外的结果。我想使用JPQL在TKBData表中筛选TKBColumn表列表。我希望得到一个带有过滤TKBColumn的TKBData表,但我总是得到带有所有TKBColumn(未过滤)的TKBData表。当我使用SQL命令时,它可以工作 我不知道这里有什么问题,为什么它总是给我TKBData表,里面总是有所有TKBColumn表 本机查询(此操作有效): 输出Java OneToMany在spring jpa中包含筛选子句的问题,java,mysql,spring,jpa,spring-data-jpa,Java,Mysql,Spring,Jpa,Spring Data Jpa,在SpringJPA中对@OneToMany关系使用时,我当前在MYSQL8/H2测试用例中得到了意外的结果。我想使用JPQL在TKBData表中筛选TKBColumn表列表。我希望得到一个带有过滤TKBColumn的TKBData表,但我总是得到带有所有TKBColumn(未过滤)的TKBData表。当我使用SQL命令时,它可以工作 我不知道这里有什么问题,为什么它总是给我TKBData表,里面总是有所有TKBColumn表 本机查询(此操作有效): 输出 ID NAME 7b6
ID NAME
7b6ec910-3e53-40a3-9221-ee60e75c8d67 column1
JPQL查询(不起作用):
输出:
id: e892bc28-c35f-4fc8-9b09-387f97a758d8, name:column1
id: 069cc76b-3487-4ad8-a4ae-6568694e2287, name:column2
表“TKBData”
public class TKBData {
@Id
@Builder.Default
private String id = UUID.randomUUID().toString();
...
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private Set<TKBColumn> columns = Sets.newHashSet();
...
}
Spring数据存储库
@Service
public interface KBDataRepository extends CrudRepository<TKBData, String>, KBDataCustomRepository {
@Query("select d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(@Param("name") String name);
}
在测试类开始时生成的表的相关内容
Table: TKBDATA
ID
726004cf-5cab-4b1d-bb3f-466ba22622e9
Table: TKBDATA_TKBCOLUMN
TKBDATA_ID COLUMNS_ID
726004cf-5cab-4b1d-bb3f-466ba22622e9 7b4e4ea8-4ff9-4668-8882-67ff93b595ca
726004cf-5cab-4b1d-bb3f-466ba22622e9 d670e813-0466-48a8-be54-ee992cf28462
Table: TKBCOLUMN
ID DATAORDER NAME OWNERID
d670e813-0466-48a8-be54-ee992cf28462 0 column1 16e01046-9a84-4651-98d8-4e3e358212eb
7b4e4ea8-4ff9-4668-8882-67ff93b595ca 1 column2 16e01046-9a84-4651-98d8-4e3e358212eb
有关更多信息,请在此处找到github存储库:测试类:
编辑: 这个问题的解决方案是使用本机查询,因为JPA的设计以及它如何处理对象,这就是为什么我的用例存在这个问题。
- 从TKBData d中选择d并连接d.c列,其中c.name=column1为
- 查找TKBData对象,该对象具有关联的
对象,其中列
为名称
列1
- 一旦它决定哪个TKBData至少有一个
对象,而列
是名称
,那么它将返回在JPA中您无法控制的所有相关列1
对象。(见附件)。另一种方法是编写本机sql并返回自定义的非实体对象列
- 例如,您将
与TKBDATA_1
和column1
关联,您还将column2
与TKBDATA_2
关联column3
- 运行查询时,它将忽略
,并决定返回TKBDATA_2
,因为它至少有一个TKBDATA_1
对象具有列
=名称
但在此之后您无法控制要为列2
返回哪些关联的TKBDATA\u 1
对象,JPA将返回所有关联的列对象列
- 如果您不确定原因,请阅读hibernate会话。它如何提供内存中任何关联项的唯一表示。它是代码<脏检查> /代码>和代码>可重复读取 的基础。
- 更新您的
,如下所示@OneToMany
- 不要忘记,与sql中的选择不同,这里选择的任何列都表示要选择TKBData对象,并限制返回哪些TKBData对象
- 因此,要获得与本机sql相同的结果,请使用第二个JPA查询
where
条件应用于该联接的最右表。- 从TKBData d中选择d并连接d.c列,其中c.name=column1为
- 查找TKBData对象,该对象具有关联的
对象,其中列
为名称
列1
- 一旦它决定哪个TKBData至少有一个
对象,而列
是名称
,那么它将返回在JPA中您无法控制的所有相关列1
对象。(见附件)。另一种方法是编写本机sql并返回自定义的非实体对象列
- 例如,您将
与TKBDATA_1
和column1
关联,您还将column2
与TKBDATA_2
关联column3
- 运行查询时,它将忽略
,并决定返回TKBDATA_2
,因为它至少有一个TKBDATA_1
对象具有列
=名称
但在此之后您无法控制要为列2
返回哪些关联的TKBDATA\u 1
对象,JPA将返回所有关联的列对象列
- 如果您不确定原因,请阅读hibernate会话。它如何提供内存中任何关联项的唯一表示。它是代码<脏检查> /代码>和代码>可重复读取 的基础。
- 更新您的
,如下所示@OneToMany
- 不要忘记,与sql中的选择不同,这里选择的任何列都表示要选择TKBData对象,并限制返回哪些TKBData对象
- 因此,要获得与本机sql相同的结果,请使用第二个JPA查询
即使您在sql查询中使用了左联接,它实际上也是一个内部联接sql查询,因为您还将
where
条件应用于该联接上最右的表。使用DISTINCT JPQL关键字
@Query("select distinct d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(@Param("name") String name);
或者使用JPA方法命名查询
public TKBData findByColumnsName(String name);
使用独特的JPQL关键字
@Query("select distinct d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(@Param("name") String name);
或者使用JPA方法命名查询
public TKBData findByColumnsName(String name);
可能您的关系是多对多的,您正在本机查询中使用连接表。你没有对一对多做任何列映射,关系应该是,一对多。仅当您关心性能或“回溯”对象时,才直接需要映射。这是我读到的。但无论如何,我也尝试过映射,但没有效果。如果你能给我一些提示来解决我的问题,我们欢迎你。问题仍然是为什么我的JPQL过滤查询有(对我来说是错误的)bahaviour。现在我知道您只是使用自动生成的表。您可以使用@joinColumn来避免额外的连接表,这意味着JPA查询的返回值是return
TKBDATA
objects,其中它在l处有
@OneToMany(fetch = FetchType.EAGER,
cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
@JoinTable(name = "TKBDATA_TKBCOLUMN",
joinColumns = @JoinColumn(name = "TKBDATA_ID"),
inverseJoinColumns = @JoinColumn(name = "COLUMNS_ID"))
private Set<TKBColumn> columns = Sets.newHashSet();
select d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name
vs
select d from TKBData d JOIN d.columns c WHERE c.name = :name
@Query("select distinct d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(@Param("name") String name);
public TKBData findByColumnsName(String name);