使用DTO对象Hibernate N + 1 SELECT

时间:2012-02-28 06:52:50

标签: java hibernate jpql dto

我在使用DTO对象时正在进行N + 1 SELECTS的hibernate有问题。

例如,这个JPQL查询:

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

MyDto的构造函数类似于:

public MyDto(Thing t) {
   ...
}

导致查询类似的内容:

SELECT t.id FROM thing t WHERE condition

后面跟着每一行的所有单个查询,即:

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
...

但是,如果构造函数不接受实体,而是接受每个单独的列,则hibernate的行为与您期望的一样,即:

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

然后生成的SQL如下所示:

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

除了创建占用每一列的DTO构造函数之外,还有一种方法可以让hibernate从一开始就一次选择所有列吗?

我们正在使用的表格中有100多个列分布在嵌入式文件中,因此维护庞大的构造函数非常烦人。该表非常规范化,没有连接。

2 个答案:

答案 0 :(得分:1)

第一次读错了你的问题...我不记得使用DTO如果他们只是拿整个实体而不只是一些特定的列,所以我不确定为什么Hibernate在你使用整体时表现得那样实体作为DTO构造函数中的参数。无论如何,您可以通过查询实际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
}

它不漂亮,但这样Hibernate应该一次性获取所有需要的行

答案 1 :(得分:0)

您可能已经注意到,构造函数表达式方法有很多缺点。如果您需要嵌套的关联,它将变得更糟。这里使用实体对象的主要问题是您仍然可能会遇到N + 1个查询问题。不久前,我就该主题写过blog post,证明了我创建Blaze-Persistence Entity Views的原因,informatica read rest api是一个允许将DTO映射为接口的库。