简体   繁体   English

使用DTO对象休眠N + 1 SELECT

[英]Hibernate N+1 SELECTs with DTO objects

I have a problem with hibernate where it is doing N+1 SELECTS when using a DTO object. 我在使用DTO对象时在进行N + 1选择的休眠状态下遇到问题。

For example, this JPQL query: 例如,此JPQL查询:

SELECT com.acme.MyDto(t) FROM Thing t

Where the constructor of MyDto is something like: MyDto的构造函数如下所示:

public MyDto(Thing t) {
   ...
}

Which results in a query of something like: 结果查询如下:

SELECT t.id FROM thing t WHERE condition

followed by all the single queries for each row, ie: 随后是每一行的所有单个查询,即:

SELECT t.id, t.column1, t.column2 FROM thing t WHERE t.id = 1
SELECT t.id, t.column1, t.column2 FROM thing t WHERE t.id = 2
SELECT t.id, t.column1, t.column2 FROM thing t WHERE t.id = 3
...

However if the constructor doesn't accept the Entity, but instead each individual column, then hibernate behaves as you would expect, ie: 但是,如果构造函数不接受Entity,而是接受每个单独的列,则冬眠的行为将与您期望的一样,即:

public MyDto(Integer id, String column1, String column2) {
   ...
}

Then the generated SQL looks like this: 然后,生成的SQL如下所示:

SELECT t.id, t.column1, t.column2 FROM thing t WHERE condition

Aside from creating DTO constructors that takes every column, is there a way to coax hibernate to just select all the columns at once from the beginning? 除了创建占用每一列的DTO构造函数之外,还有没有一种方法可以让休眠模式从一开始就一次选择所有列?

The table that we're working with has 100+ columns spread across embeddables, so it's pretty annoying to maintain a huge constructor. 我们正在使用的表在可嵌入对象中分布了100多个列,因此维护一个巨大的构造函数很烦人。 The table is hugely normalised and has no joins. 该表已被高度规范化,并且没有联接。

Read your question wrong the first time... I don't recall using DTOs if they just take the whole entity and not just some specific columns, so I'm not sure why Hibernate behaves like that when you use a whole entity as a parameter in DTO constructor. 第一次读错您的问题...我不记得使用DTO来获取整个实体,而不仅仅是一些特定的列,所以我不确定为什么当您将整个实体用作对象时,Hibernate的行为会如此DTO构造函数中的参数。 Anyway, you could work around it by just gettin the actual Things via a query and then construct the DTOs in a loop, something along the lines of: 无论如何,您可以通过查询获取实际Things来解决此问题,然后在循环中构造DTO,类似于以下内容:

public List<ThingDTO> getThingDTOs( ... )
{
    Query query = em().createQuery("FROM Thing t WHERE ...");
    query.setParameter( ... );

    List<Thing> things = query.getResultList();

    List<ThingDTO> thingDTOs = new ArrayList(things.size());
    for(Thing t : things)
    {
        thingDTOs.add(new ThingDTO(t));
    }

    return thingDTOs
}

It ain't pretty, but this way Hibernate should fetch all the needed rows in one go 这不是很漂亮,但是这种方式Hibernate应该一次性获取所有需要的行

As you have probably noticed already, the constructor expression approach has quite some downsides. 您可能已经注意到,构造函数表达式方法有很多缺点。 If you need nested associations it going to get worse. 如果您需要嵌套的关联,它将变得更糟。 The main problem with working with entity objects here is that you might still run into N + 1 queries problems. 这里使用实体对象的主要问题是您仍然可能会遇到N + 1个查询问题。 I wrote a blog post on that topic a while ago that justifies why I created Blaze-Persistence Entity Views , a library that allows to map DTOs as interfaces. 不久前,我就该主题写了一篇博客文章 ,论证了为什么我创建了Blaze-Persistence Entity Views的理由,该视图允许将DTO映射为接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM