@ManyToMany映射实体上的HQL选择查询产生无效的SQL语法

时间:2019-06-16 22:55:19

标签: hibernate many-to-many hql

关于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
}

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子句中的第二列是无效的,看起来好像正在尝试创建一列,但没有使用有效的列名或别名,并且令人惊讶地生成了一条SQL语句无效语法的执行失败。我已经逐步完成了SQL生成代码,并打开了跟踪日志记录,但无法弄清它正在尝试做什么或如何解决它。

如果我删除第二列,则此查询运行良好,并完全返回我要查找的数据。作为替代方案,我曾考虑在本机SQL中实现此功能,但我想避免这种情况,并尽可能使用HQL,以确保整个项目的其余部分保持一致。

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

真正返回查询所需数据的最小查询根本不必命中That表,只需要在This表和隐式,未映射的{{1 }}表是由JPA创建和管理的,但未映射到实体。我尝试将其映射到实体,以便可以对其进行查询,但是Hibernate在启动时抱怨该表被映射了两次。如果还有其他方法可以访问此表并直接对其进行查询,我将很乐意这样做。

这是与hibernate-jpa-2.1一起使用的,我知道它是旧版本,但这是生产中的旧系统。如果升级JPA版本会有所帮助,我会考虑,但如果可能的话,希望首先了解根本原因。

5 个答案:

答案 0 :(得分:1)

在使用 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;
.......
}

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

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

    List<That> thats 

由休眠自动填充。

答案 3 :(得分:0)

您所说的或试图做的没有意义。 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仅该实体的查询。很有可能,但这也是一个单独的问题,您应该首先尝试更多操作。

为完整起见,您已为unidirectional实体拥有的This ManyToMany映射建模。

答案 4 :(得分:0)

我想出了答案,我正在发布该答案以帮助遇到此问题的任何其他人。 join必须在HQL中明确显示,如下所示:

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

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