对映射实体@ManyToMany HQL 选择查询会生成无效的 SQL 语法



这里有一堆关于 HQL 多对多映射表查询的问题,但我看了很多,还没有看到这个。

我有两个表,它们与映射为实体的源表和目标表以及它们之间定义的@ManyToMany关系具有多对多关系,按照通常的建议进行配置。

转折点是我正在尝试查询这些表,以便我可以在单个查询中获取源键和目标键的值。原因是为了性能,我们已经深度嵌套了分层数据,并且通过原版Hibernate获取所有相关数据会导致数十或数百个查询,这些查询无法充分执行。相反,我打算一次获取所有数据并在代码中映射它。

下面是示例类定义:

@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
}

总部是这样的:

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生成代码并打开了跟踪日志记录,但无法弄清楚它正在尝试做什么或如何修复它。

如果我删除第二列,此查询运行良好,并准确返回我正在寻找的数据。作为替代方案,我已经考虑在本机SQL中实现这一点,但我想避免这种情况,并在可能的情况下坚持使用HQL,以确保项目其余部分的一致性。

我尝试的另一个选择是select t,t.thats ....这确实产生了一个有效的查询,但是,此示例中没有显示,它为其他关系发出了更多查询,这些查询是急切地从This实体中获取的,这也降低了性能。我真的只需要从源表中获取单个键列。

返回我需要的数据的真正最小的查询根本不需要命中That表,它只会在This表和隐式的、未映射的this_thats表上,该表由 JPA 创建和管理,但不映射到实体。我尝试将其映射到一个实体,以便我可以查询它,但 Hibernate 在启动时抱怨此表被映射了两次。 如果有其他方法可以获取此表并直接查询它,我很乐意这样做。

这是hibernate-jpa-2.1,我知道这是一个旧版本,但这是生产中的遗留系统。如果升级 JPA 版本会有所帮助,我会考虑一下,但如果可能的话,我想先了解根本原因。

在使用 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 ThisAndThat{
long id;
long otherId;
String name;
.......
}

我不熟悉这个旧版本的休眠,但我会尝试替换

{@JoinColumn(name = "this_id")} by {@JoinColumn(name = "this.id")} 

{@JoinColumn(name = "that_id")} by {@JoinColumn(name = "that.id")}

再试一次:也许问题是,您无法将列表作为 SQL select 语句的单行结果。也许您不必选择第二列,因为

List<That> thats 

由休眠自动填充。

你说的或试图做的事情没有意义。t.thats是一个集合,因此不能将其作为行的一部分返回。你需要用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联接表的内容。该表仅包含关系的ids。出于某种原因,您可能需要 id,以便稍后获取thats。很公平,但就像您说的那样,您必须将联接表创建为单独的实体,然后使用仅joins该实体的查询。这是很有可能的,但这也是一个单独的问题,您应该先尝试更多自己做。

为了完整起见,您已经对This实体拥有的unidirectionalManyToMany映射进行了建模。

我想出了答案,我发布这个答案是为了帮助其他遇到这种情况的人。join需要在 HQL 中显式,如下所示:

select t.id,th from This t join t.thats th where t.otherId=13

显然,如果没有这个,Hibernate就无法找出正确的选择语句。

最新更新