DoytoQuery中的关联查询方案示例详解

 更新时间:2022年12月27日 17:33:12   作者:f0rb  
这篇文章主要为大家介绍了DoytoQuery中的关联查询方案示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1. 背景

Java Persistence with Hibernate 在12.2.1小节使用如下例子描述 n+1查询问题:

List<Item> items = em.createQuery("select i from Item i").getResultList();
// select * from ITEM
for (Item item : items) {
    assertTrue(item.getBids().size() > 0);
    // select * from BID where ITEM_ID = ?
}

在这个例子中,每个bids集合的加载都需要执行一条额外的查询语句,当item有N条记录,一共就会执行N+1条查询语句:

SELECT * FROM item;
SELECT * FROM bid WHERE item_id = ?;
SELECT * FROM bid WHERE item_id = ?;
SELECT * FROM bid WHERE item_id = ?;
SELECT * FROM bid WHERE item_id = ?;

2. SQL层的解决方案

在本方案中,首先通过两个步骤对SQL语句加以改造,从SQL层面上解决这个问题。

使用关键字UNION ALL将N条查询语句合为一条语句,便将N+1次查询转化为了1+1次查询。

由于第二次查询的所有记录被一次性返回,而我们需要将Bid实体关联到相关的Item实体上,因此我们需要添加一个额外的item_id列以便进行实体关联。

以下是改造后的两条查询语句。

SELECT * FROM item;
SELECT ? AS item_id, b.* FROM bid b WHERE item_id = ? UNION ALL
SELECT ? AS item_id, b.* FROM bid b WHERE item_id = ? UNION ALL
SELECT ? AS item_id, b.* FROM bid b WHERE item_id = ? UNION ALL
SELECT ? AS item_id, b.* FROM bid b WHERE item_id = ?;

When we want to query a bid list and every bid entity to carry its item, we can execute two query statements as follows:

SELECT * FROM bid;
SELECT ? AS bid_id, i.* FROM item i WHERE id IN (SELECT item_id FROM bid WHERE id = ?) UNION ALL
SELECT ? AS bid_id, i.* FROM item i WHERE id IN (SELECT item_id FROM bid WHERE id = ?) UNION ALL
SELECT ? AS bid_id, i.* FROM item i WHERE id IN (SELECT item_id FROM bid WHERE id = ?) UNION ALL
SELECT ? AS bid_id, i.* FROM item i WHERE id IN (SELECT item_id FROM bid WHERE id = ?);

ItemBid之间的关系是典型的一对多/多对一关系。以上这一解决方案也可用于多对多关系。

3. ORM应用层面的解决方案

对于ORM层,我们需要想办法从表结构的信息中映射到第二条查询语句,Java中常用注解的方式。

上面的SQL语句中只有四个要素,两个表名itembid,表bid中的外键列item_id和表item中的引用列id。 其中,查询实体的表名是已知的,于是便只剩下三个要素。 DoytoQuery定义了一个注解@DomainPath来配置这三个要素,用以映射第二条查询语句。

@Target(FIELD)
@Retention(RUNTIME)
public @interface DomainPath {
    String[] value();
    String localField() default "id";
    String foreignField() default "id";
}

由于第二条查询语句中附加的id列仅用于实体赋值,因此我们将附加列的别名统一命名为 MAIN_ENTITY_IDItemBid的类定义如下:

@Getter
@Setter
public class ItemView extends AbstractPersistable<Integer> {
    // other fields in Item
    // one-to-many
    // SELECT ? AS MAIN_ENTITY_ID, b.*
    //              FROM bid b           WHERE item_id = ? [UNION ALL ...]
    @DomainPath(value = "bid", foreignField = "item_id")
    private List<BidView> bids;
}
@Getter
@Setter
public class BidView extends AbstractPersistable<Integer> {
    // other fields in Bid
    // many-to-one
    // SELECT ? AS MAIN_ENTITY_ID, i.*
    //              FROM item i           WHERE id      IN (SELECT item_id FROM bid WHERE id = ?)  [UNION ALL ...]
    @DomainPath(value = "item", foreignField = "id", localField = "item_id")
    private ItemView item;
}  

假设ItemCategory之间的多对多关系存放于中间表CATEGORY_ITEM中,我们可以使用@DomainPath来定义如下实体,用以映射第二条查询语句:

@Getter
@Setter
public class ItemView extends AbstractPersistable<Integer> {
    // other fields in Item
    // many-to-many
    @DomainPath({"item", "~", "category"})
    private List<ItemView> items;
}
@Getter
@Setter
public class CategoryView extends AbstractPersistable<Integer> {
    // other fields in Category
    // many-to-many
    @DomainPath({"category", "item"})
    private List<ItemView> items;
}

4. 小结

在本文中,我们介绍了DoytoQuery中的一种可以避免n+1查询问题的关联查询方案。并且,我们只需要通过一个注解@DomainPath便可管理ERM中定义的四种实体关系。

以上就是DoytoQuery中的关联查询方案示例详解的详细内容,更多关于DoytoQuery关联查询方案的资料请关注脚本之家其它相关文章!

相关文章

  • LoggingEventAsyncDisruptorAppender类执行流程源码解读

    LoggingEventAsyncDisruptorAppender类执行流程源码解读

    这篇文章主要介绍了LoggingEventAsyncDisruptorAppender类执行流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Springboot引用外部配置文件的方法步骤

    Springboot引用外部配置文件的方法步骤

    这篇文章主要介绍了Springboot引用外部配置文件的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java使用elasticsearch基础API使用案例讲解

    Java使用elasticsearch基础API使用案例讲解

    这篇文章主要介绍了Java使用elasticsearch基础API使用案例讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • springboot如何去获取前端传递的参数的实现

    springboot如何去获取前端传递的参数的实现

    这篇文章主要介绍了springboot如何去获取前端传递的参数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 利用Java实现调用http请求

    利用Java实现调用http请求

    在实际开发过程中,我们经常需要调用对方提供的接口或测试自己写的接口是否合适。本文就为大家准备了几个java调用http请求的几种常见方式,需要的可以参考一下
    2022-08-08
  • Java Thread多线程开发中Object类详细讲解

    Java Thread多线程开发中Object类详细讲解

    这篇文章主要介绍了Java Thread多线程开发中Object类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • Java实现批量发送带附件的邮件代码

    Java实现批量发送带附件的邮件代码

    大家好,本篇文章主要讲的是Java实现批量发送带附件的邮件代码,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • java合并多个文件的两种方法

    java合并多个文件的两种方法

    这篇文章主要为大家详细介绍了java合并多个文件的两种方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • SpringCloud使用CircuitBreaker实现熔断器的详细步骤

    SpringCloud使用CircuitBreaker实现熔断器的详细步骤

    在微服务架构中,服务之间的依赖调用非常频繁,当一个下游服务因高负载或故障导致响应变慢或不可用时,可能会引发上游服务的级联故障,最终导致整个系统崩溃,熔断器是解决这类问题的关键模式之一,Spring Cloud提供了对熔断器的支持,本文将详细介绍如何集成和使用它
    2025-02-02
  • java 中类似js encodeURIComponent 函数的实现案例

    java 中类似js encodeURIComponent 函数的实现案例

    这篇文章主要介绍了java 中类似js encodeURIComponent 函数的实现案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10

最新评论