实际上,这种情况的答案比你想象的要简单。为正确的工作使用正确的工具是一件简单的事情。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();
}