【问题标题】:JPA Query over a join tableJPA 查询连接表
【发布时间】:2023-03-06 22:49:01
【问题描述】:

我有 3 张桌子,例如:

A                              AB                        B
-------------                  ------------              ---------------
a1                              a1,b1                    b1

AB 是 A 和 B 之间的转换表

有了这个,我的班级在这两个班级之间没有组成。但我想知道,通过 JPQL 查询,我的元素是否存在来自 AB 表中的 A 表的任何记录。我需要的只是数字或布尔值。

因为 AB 是一个转换表,所以它没有模型对象,我想知道我是否可以在我的 Repository 对象中使用 @Query 来做到这一点。

【问题讨论】:

  • 我不确定没有实体对象的 jpql。为什么不使用 SQL?
  • Sql 可以轻松做到这一点,但我想在我的项目中保持一致性(仅使用 JPA)。但是当然如果没有办法,我会选择SQL

标签: jpa jpql


【解决方案1】:

AB 表必须在实体中建模才能在 JPQL 中查询。因此,您必须将其建模为 您的 A 和/或 B 实体中的自己的实体类或关联。

【讨论】:

  • 看来是这样。还是谢谢
【解决方案2】:

我建议使用Native query method 而不是 JPQL(JPA 也支持本地查询)。让我们假设表 A 是客户,表 B 是产品,表 AB 是销售。这是获取客户订购的产品列表的查询。

entityManager.createNativeQuery("SELECT PRODUCT_ID FROM 
                                     SALE WHERE CUSTOMER_ID = 'C_123'");

【讨论】:

  • 更正:认为您的意思是使用本机查询(即 SQL)而不是 JPQL(您将“本机查询放入 JPQL”,而 JPQL 是 JPA 自己的查询语言)。
【解决方案3】:

实际上,这种情况的答案比您想象的要简单。为正确的工作使用正确的工具是一件简单的事情。 JPA 不是为实现复杂的 SQL 查询而设计的,这就是 SQL 的用途!因此,您需要一种让 JPA 访问生产级 SQL 查询的方法;

em.createNativeQuery

因此,在您的情况下,您要做的是访问 AB 表,仅查找 id 字段。检索到查询后,获取 id 字段并使用 id 字段查找 Java 对象。这是第二次搜索,但按 SQL 标准来说是微不足道的。

假设您正在根据 B 对象引用它的次数来查找 A 对象。假设您想要一个半复杂(但典型)的 SQL 查询来根据 B 对象的数量并按降序对类型 A 对象进行分组。这将是一个典型的流行度查询,您可能希望根据项目要求实施。

您的原生 SQL 查询将是这样的:

select a_id as id from AB group by a_id order by count(*) desc;

现在您要做的是告诉 JPA 期望 id 列表以 JPA 可以接受的形式返回。您需要组合一个额外的 JPA 实体。永远不会以 JPA 的正常方式使用的一种。但是 JPA 需要一种方法来将查询的对象返回给您。您可以为此搜索查询组合一个实体;

@Entity
public class IdSearch {

    @Id
    @Column
    Long id;

    public Long getId() {
            return id;
    }

    public void setId(Long id) {
            this.id = id;
    }
    
}

现在您实现一点代码来将这两种技术结合在一起;

    @SuppressWarnings("unchecked")
    public List<IdSearch> findMostPopularA() {
            return em.createNativeQuery("select a_id as id from AB group by a_id 
            order by count(*) desc", IdSearch.class).getResultList();
    }

到这里,您需要做的就是让 JPA 成功完成您的查询。要获取您的 A 对象,您只需使用传统的 JPA 方法交叉引用到您的 A 列表中;

List<IdSearch> list = producer.getMostPopularA();
Iterator<IdSearch> it = list.iterator();
while ( it.hasNext() ) {
    IdSearch a = it.next();
    A object = em.find(A.class,a.getId());
    // your in business!

不过,考虑到 SQL 设计结构的许多功能,对上述内容进行更多改进实际上可以进一步简化事情。稍微复杂一点的 SQL 查询将为您的实际数据提供更直接的 JPA 接口;

    @SuppressWarnings("unchecked")
    public List<A> findMostPopularA() {
            return em.createNativeQuery("select * from A, AB 
            where A.id = AB.a_id 
            group by a_id 
            order by count(*) desc", A.class).getResultList();
    }

这消除了对中间 IdSearch 表的需要!

List<A> list = producer.getMostPopularA();
Iterator<A> it = list.iterator();
while ( it.hasNext() ) {
    A a = it.next();
    // your in business!

肉眼可能不清楚的是,JPA 允许您在 JPA 接口中使用复杂的 SQL 结构,这是一种极其简化的方式。想象一下,如果你的 SQL 如下;

SELECT array_agg(players), player_teams
FROM (
  SELECT DISTINCT t1.t1player AS players, t1.player_teams
  FROM (
    SELECT
      p.playerid AS t1id,
      concat(p.playerid,':', p.playername, ' ') AS t1player,
      array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
    FROM player p
    LEFT JOIN plays pl ON p.playerid = pl.playerid
    GROUP BY p.playerid, p.playername
  ) t1
INNER JOIN (
  SELECT
    p.playerid AS t2id,
    array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
  FROM player p
  LEFT JOIN plays pl ON p.playerid = pl.playerid
  GROUP BY p.playerid, p.playername
) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
) innerQuery
GROUP BY player_teams

关键在于,使用 createNativeQuery 接口,您仍然可以准确地检索您要查找的数据,并直接将其放入所需的对象中,以便 Java 轻松访问。

    @SuppressWarnings("unchecked")
    public List<A> findMostPopularA() {
            return em.createNativeQuery("SELECT array_agg(players), player_teams
            FROM (
                  SELECT DISTINCT t1.t1player AS players, t1.player_teams
                  FROM (
                    SELECT
                      p.playerid AS t1id,
                      concat(p.playerid,':', p.playername, ' ') AS t1player,
                      array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
                    FROM player p
                    LEFT JOIN plays pl ON p.playerid = pl.playerid
                    GROUP BY p.playerid, p.playername
                  ) t1
                INNER JOIN (
                  SELECT
                    p.playerid AS t2id,
                    array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
                  FROM player p
                  LEFT JOIN plays pl ON p.playerid = pl.playerid
                  GROUP BY p.playerid, p.playername
                ) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
                ) innerQuery
                GROUP BY player_teams
            ", A.class).getResultList();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 2011-12-21
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多