【问题标题】:Join cartesian product加入笛卡尔积
【发布时间】:2015-07-13 19:15:04
【问题描述】:

上下文

我每周都会向一些企业发送一些文件。无论文件是否发送,我都需要为每个星期和每个企业恢复。

表格

  • ent(企业)
  • 周(周)
  • fil(文件:参考 wek 和 ent)

纯 SQL 解决方案

entwek 之间做一个笛卡尔积,然后左外连接 fil。这有效:

select * from 
  (
  select * from wek, ent e
  ) as t1
left join fil f 
  on f.ent_id = t1.ent_id 
  and f.wek_id = t1.wek_id

问题:

如何将其翻译成 JPA(以 CriteriaBuilder 的方式)?

例如,如果我尝试:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ResultClass> query = cb.createQuery(ResultClass.class);
Root<Week> week = query.from(Week.class);
Root<Enterprise> query.from(Enterprise.class);
Expression<???> cartesianProduct = ??? //How?
cartesianProduct.leftJoin(???_.file);
query.where(
  cb.equal(file.get(File_.wek_id), week.get(Week.week_id));
)

使用 2 个“from”子句可以得到笛卡尔积,但我如何离开加入这个结果?

无法解决的问题:

创建一个视图:

CREATE VIEW view_ent_wek AS
  SELECT ent_id as ent_id,  wek_id as wek_id, ent_id || '-' || ent_id as id
  FROM ent, wek;

将其映射到实体:

@Entity
@Table(name = "view_ent_wek")
public class WeekEnterprise {

  @Id
  @Column(name = "id")
  private String id;

  @ManyToOne
  @JoinColumn(name = "ent_id")
  private Enterprise enterprise;

  @ManyToOne
  @JoinColumn(name = "wek_id")
  private Week week;

  [...]

然后我可以在查询中使用它:

Root<WeekEnterprise> weekEnterprise = query.from(WeekEnterprise.class);
weekEnterprise.join(...)

我不喜欢这个解决方案,因为它让我创建了一个显然没有必要的视图。有什么想法吗?

【问题讨论】:

  • 不确定但是,你可以试试这个。考虑到,你在Test1 类中将Test2 类作为JoinColumn 属性。试试这个Root&lt;Test1&gt; root = criteria.from(Test1.class); root.get("test2Object").get("test1FieldInTest2");。我认为它可以让你在 Test1Test2 之间进行交叉连接。
  • 您究竟想从 JPA 查询中返回什么。我的意思是,你想要列还是实体本身?例如,您是否只想要entwek 的ID,后跟一个(可能为null)fil 对象?还是要返回 entwek 和(可能为 null)fil 对象?

标签: java hibernate postgresql jpa


【解决方案1】:

我看不出使用 JPA 执行报告式 SQL 查询的意义,更不用说使用 Criteria Query。

当然,JPA(及其实现)确实支持元组查询(与实体查询相反,另请参阅:Hibernate tuple criteria queries),但元组查询并不是 JPA 擅长的。想象一下,您想要更改该报告并添加更多计算。用 SQL 做这件事不是更容易吗?

使用 JPA 映射到 Java 对象和进行事务建模仍然有意义,因此,您的 SQL 视图解决方案已经相当不错,或者如果您不想将 SQL 存储在视图中,请使用 JPA 的本机查询 API:

List<Object[]> list = em.createNativeQuery(sql).getResultList();

或者:

List<WeekEnterprise> list = em.createNativeQuery(sql, WeekEnterprise.class)
                              .getResultList();

【讨论】:

  • 这是有道理的。视图将是自然的解决方案,或者只是原始 SQL。在这种情况下,ORM 层只是对馈送到 RDBMS 的实际代码的笨拙翻译。
  • @AndreiI:这很有可能......你想说什么? :-)
  • @LukasEder 我想说我是第一个提出这个想法的人。
  • @AndreiI:这令人印象深刻。我给了你一个赞成票,因为你是第一个。干杯
  • @LukasEder 实际上,使用 SQL 进行计算要容易得多。由于老板也有兴趣在自己的报表工具中导出这个查询的结果,所以我最终选择用整个SQL select创建一个视图。然后,在 JPA 方面,我只是 select from 视图。
【解决方案2】:

我有一些想法:

  1. 有 2 个 JPA 查询。第一:取wek和enterprise的笛卡尔积。第二:做周、企业、档案的内联。使用映射 (wek_id+ent_id => Tuple(Wek, Ent, File)) 快速识别文件放置位置。

  2. 编写普通 SQL 查询并使用 JPA API 执行它们。

  3. (没想太多)从 Wek 和 Ent 到 File(延迟加载)创建一个反向引用,然后您应该能够继续您的第一个想法。

【讨论】:

    【解决方案3】:

    不太了解 JPA,但了解 Postgres。我会建议这个高级查询:

    SELECT *
    FROM   wek CROSS JOIN ent
    LEFT   JOIN fil USING (ent_id, wek_id);
    

    您不需要子选择。
    结果的唯一区别(除了更短和更快):您在输出中得到ent_idwek_id once 列(这可能会解决列名重复的问题)。

    现在应该很容易翻译成 JPA。

    【讨论】:

    • 这个解决方案如何更快?
    • @LukasEder。少一个子查询(计划时间)和少两个输出列(数据传输和处理)。它不会太多,所以这只是我的回答。
    • 好的,我明白了... 嗯,除了偶尔的游标缓存争用之外,我还没有遇到任何关于规划时间的问题(在 Oracle 中)。也许这取决于 SQL 引擎。至于数据传输 - SELECT * 是邪恶的部分,不一定是 join conditionnamed columns join。但这是讨论学术的地方:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2013-07-14
    相关资源
    最近更新 更多