HQL select 对 @ManyToMany 映射实体的查询产生无效的 SQL 语法
HQL select query on @ManyToMany mapped entity produces invalid SQL syntax
这里有很多关于 HQL 多对多映射 table 查询的问题,但我已经看了很多,还没有看到这个。
我有两个 table 与源和目标 table 映射为实体的多对多关系以及它们之间定义的 @ManyToMany 关系,配置为通常推荐的.
不同之处在于,我正在尝试查询这些 table,以便我可以在单个查询中同时获取源键和目标键的值。原因是性能,我们有深层嵌套的分层数据,并通过香草 Hibernate 获取所有相关数据会导致数十或数百个查询,这些查询无法充分执行。相反,我打算一次获取所有数据并将其映射到代码中。
这里是示例 class 定义:
@Entity
public class This {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@Column
protected Long otherId;
@ManyToMany
@JoinTable(joinColumns = {@JoinColumn(name = "this_id")},
inverseJoinColumns = {@JoinColumn(name = "that_id")})
private List<That> thats;
// getters and setters
}
和
@Entity
public class That {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name", unique = true)
private String name;
// getters and setters
}
HQL 是这样的:
select t.id,t.thats from This t where t.otherId=13
这是生成的 SQL:
select
this0_.id as col_0_0_,
. as col_1_0_,
that2_.id as id1_59_,
that2_.name as name2_59_
from
this this0_
inner join
this_thats thats1_
on this0_.id=thats1_.this_id
inner join
that that2_
on thats1_.that_id=that2_.id
where
this0_.other_id=13
如您所见,select
子句中的第 2 列无效,它看起来好像正在尝试创建一个列但未使用有效的列名或别名并且有点令人惊讶地产生了一个SQL 执行时失败的无效语法语句。我已经逐步完成了 SQL 生成代码并打开了跟踪日志记录,但无法弄清楚它试图做什么或如何修复它。
如果我删除第 2 列,此查询运行正常并且 return 正是我要查找的数据。作为替代方案,我考虑过在本机 SQL 中实现它,但我想避免这种情况,并尽可能坚持使用 HQL,以确保项目其余部分的一致性。
我尝试的另一个选项是 select t,t.thats ...
。这确实产生了一个有效的查询,但是,在这个例子中没有显示,它发出了更多的查询来查询其他关系,这些关系是急切地从 This
实体中获取的,这也会降低性能。我真的需要从源 table.
中获取单个键列
真正最小的查询 return 我需要的数据根本不需要点击 That
table,它只会在 This
table 和由 JPA 创建和管理但未映射到实体的隐式未映射 this_thats
table。我尝试将它映射到一个实体,以便我可以查询它,但是 Hibernate 在启动时抱怨说这个 table 被映射了两次。如果有其他方法可以获取此 table 并直接查询它,我很乐意这样做。
这是与 hibernate-jpa-2.1 一起使用的,我知道这是一个旧版本,但这是生产中的遗留系统。如果升级 JPA 版本会有帮助,我会考虑它,但如果可能的话,我想先了解根本原因。
我不熟悉这个旧版本的休眠,但我会尝试替换
{@JoinColumn(name = "this_id")} by {@JoinColumn(name = "this.id")}
和
{@JoinColumn(name = "that_id")} by {@JoinColumn(name = "that.id")}
再试一次:也许问题是,您无法将列表作为 SQL select 语句的单行结果。也许您不必 select 第二列,因为
List<That> thats
由hibernate自动填充。
你说的或想做的没有意义。 t.thats
是一个集合,因此您不能 return 它作为一行的一部分。您需要更多地考虑使用 JPA 面向对象。此外,JPA 2.1 并没有特别过时,因此应该没有问题。因此,预取 thats
集合的工作查询是这样完成的:
This t2 = em.createQuery("from This t left outer join fetch t.thats where t.otherId = 12", This.class).getSingleResult();
System.out.println("" + t2 + t2.getThats());
此查询告诉 JPA 在单个查询中获取您想要的 This
和任何相关的 thats
。
您声明您只需要 ThisThat
连接 table 的内容。那table只有ids
的关系。出于某种原因,您可能需要 id,以便稍后可以获取 thats
。很公平,但就像你说的那样,你必须将连接 table 创建为一个单独的实体,然后使用 joins
仅该实体的查询。这是很有可能的,但这也是一个单独的问题,你应该先尝试自己做。
为了完整起见,您已经为 This
实体拥有的 unidirectional
多对多映射建模。
我找到了答案,我将其发布以帮助遇到此问题的其他人。 join
需要像这样在 HQL 中显式显示:
select t.id,th from This t join t.thats th where t.otherId=13
显然,如果没有这个,Hibernate 无法计算出正确的 select 语句。
在使用 spring 数据时,jpa 只需使用存储库上的 findAll 来获取列表
但如果您想要 HQL 方法,请尝试下面的方法
将以下方法添加到存储库
@Query("select new ThisThat(u.id, u.otherId,th.name), "
"from This u " +
"inner join this_thats l on l.this_id=u.id " +
"inner join that th on th.id=l.that.id " +
"where u.id=:id)
List<ThisAndThat> getThisAndThat(long id);
创建一个名为 ThisAndThat 的 class,如下所示
class ThisAndThat{
long id;
long otherId;
String name;
.......
}
这里有很多关于 HQL 多对多映射 table 查询的问题,但我已经看了很多,还没有看到这个。
我有两个 table 与源和目标 table 映射为实体的多对多关系以及它们之间定义的 @ManyToMany 关系,配置为通常推荐的.
不同之处在于,我正在尝试查询这些 table,以便我可以在单个查询中同时获取源键和目标键的值。原因是性能,我们有深层嵌套的分层数据,并通过香草 Hibernate 获取所有相关数据会导致数十或数百个查询,这些查询无法充分执行。相反,我打算一次获取所有数据并将其映射到代码中。
这里是示例 class 定义:
@Entity
public class This {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@Column
protected Long otherId;
@ManyToMany
@JoinTable(joinColumns = {@JoinColumn(name = "this_id")},
inverseJoinColumns = {@JoinColumn(name = "that_id")})
private List<That> thats;
// getters and setters
}
和
@Entity
public class That {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name", unique = true)
private String name;
// getters and setters
}
HQL 是这样的:
select t.id,t.thats from This t where t.otherId=13
这是生成的 SQL:
select
this0_.id as col_0_0_,
. as col_1_0_,
that2_.id as id1_59_,
that2_.name as name2_59_
from
this this0_
inner join
this_thats thats1_
on this0_.id=thats1_.this_id
inner join
that that2_
on thats1_.that_id=that2_.id
where
this0_.other_id=13
如您所见,select
子句中的第 2 列无效,它看起来好像正在尝试创建一个列但未使用有效的列名或别名并且有点令人惊讶地产生了一个SQL 执行时失败的无效语法语句。我已经逐步完成了 SQL 生成代码并打开了跟踪日志记录,但无法弄清楚它试图做什么或如何修复它。
如果我删除第 2 列,此查询运行正常并且 return 正是我要查找的数据。作为替代方案,我考虑过在本机 SQL 中实现它,但我想避免这种情况,并尽可能坚持使用 HQL,以确保项目其余部分的一致性。
我尝试的另一个选项是 select t,t.thats ...
。这确实产生了一个有效的查询,但是,在这个例子中没有显示,它发出了更多的查询来查询其他关系,这些关系是急切地从 This
实体中获取的,这也会降低性能。我真的需要从源 table.
真正最小的查询 return 我需要的数据根本不需要点击 That
table,它只会在 This
table 和由 JPA 创建和管理但未映射到实体的隐式未映射 this_thats
table。我尝试将它映射到一个实体,以便我可以查询它,但是 Hibernate 在启动时抱怨说这个 table 被映射了两次。如果有其他方法可以获取此 table 并直接查询它,我很乐意这样做。
这是与 hibernate-jpa-2.1 一起使用的,我知道这是一个旧版本,但这是生产中的遗留系统。如果升级 JPA 版本会有帮助,我会考虑它,但如果可能的话,我想先了解根本原因。
我不熟悉这个旧版本的休眠,但我会尝试替换
{@JoinColumn(name = "this_id")} by {@JoinColumn(name = "this.id")}
和
{@JoinColumn(name = "that_id")} by {@JoinColumn(name = "that.id")}
再试一次:也许问题是,您无法将列表作为 SQL select 语句的单行结果。也许您不必 select 第二列,因为
List<That> thats
由hibernate自动填充。
你说的或想做的没有意义。 t.thats
是一个集合,因此您不能 return 它作为一行的一部分。您需要更多地考虑使用 JPA 面向对象。此外,JPA 2.1 并没有特别过时,因此应该没有问题。因此,预取 thats
集合的工作查询是这样完成的:
This t2 = em.createQuery("from This t left outer join fetch t.thats where t.otherId = 12", This.class).getSingleResult();
System.out.println("" + t2 + t2.getThats());
此查询告诉 JPA 在单个查询中获取您想要的 This
和任何相关的 thats
。
您声明您只需要 ThisThat
连接 table 的内容。那table只有ids
的关系。出于某种原因,您可能需要 id,以便稍后可以获取 thats
。很公平,但就像你说的那样,你必须将连接 table 创建为一个单独的实体,然后使用 joins
仅该实体的查询。这是很有可能的,但这也是一个单独的问题,你应该先尝试自己做。
为了完整起见,您已经为 This
实体拥有的 unidirectional
多对多映射建模。
我找到了答案,我将其发布以帮助遇到此问题的其他人。 join
需要像这样在 HQL 中显式显示:
select t.id,th from This t join t.thats th where t.otherId=13
显然,如果没有这个,Hibernate 无法计算出正确的 select 语句。
在使用 spring 数据时,jpa 只需使用存储库上的 findAll 来获取列表
但如果您想要 HQL 方法,请尝试下面的方法
将以下方法添加到存储库
@Query("select new ThisThat(u.id, u.otherId,th.name), "
"from This u " +
"inner join this_thats l on l.this_id=u.id " +
"inner join that th on th.id=l.that.id " +
"where u.id=:id)
List<ThisAndThat> getThisAndThat(long id);
创建一个名为 ThisAndThat 的 class,如下所示
class ThisAndThat{
long id;
long otherId;
String name;
.......
}