与jdbc相比,Hibernate(PostgreSQL)外键慢选择查询WHERE子句

时间:2019-01-03 12:36:09

标签: java sql postgresql hibernate

我写了一个应用程序,该应用程序会抓取互联网广播播放列表,然后将其保存到数据库中。为了了解休眠,我将应用程序迁移为使用休眠,但是与其他尝试相比,在执行SELECT ... WHERE查找时,我发现性能下降很大。在我的python sqlite原型中,相同的过程(获取大约17,000个曲目(按播放它们的程序以及播放它们的人分组))花费了150ms,而使用apache db utils的最初的Java版本花费了250ms,与我相比(可能太恐怖了) )的休眠版本,大约需要1100毫秒。

@Override
public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
    DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
    session.beginTransaction();
    List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
            .setParameterList("names", names)
            .getResultList();
    for (Presenter presenter : result) {
        int presenter_id = presenter.getPresenter_id();
        List<Programme> programmes = session
                .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
                .setParameter("pres_orig_id", presenter_id)
                .getResultList();
        for (Programme programme : programmes) {
            //this is the critical performance death zone 
            List<Track> tracksOnThisProgramme = session
                    .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
                    .setParameter("progIds", programme.getProgramme_id())
                    .getResultList();
            djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
        }
    }
    session.getTransaction().commit();
    return djAllProgrammes;
}

调试信息:

INFO:会话指标

{
    33339 nanoseconds spent acquiring 1 JDBC connections;
    71991 nanoseconds spent releasing 1 JDBC connections;
    12938819 nanoseconds spent preparing 258 JDBC statements;
    88949720 nanoseconds spent executing 258 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    4671332 nanoseconds spent executing 1 flushes (flushing a total of 9130 entities and 0 collections);
    599862735 nanoseconds spent executing 258 partial-flushes (flushing a total of 1079473 entities and 1079473 collections)
}

环顾互联网,我看到了一个建议,该建议基于交易中有太多实体以至于无法使用“分页和较小的批次增量”-我可以找到有关什么是“分页”的信息,而不是“使用较小的批次增量”的信息意思是

我有点束手无策,在使用Apache DB Utils(轻量级的jdbc包装器)的情况下,此应用程序具有出色的性能,几乎可以完成相同的操作,而且我非常无知,甚至不知道要搜索什么加快速度。帮兄弟出去吗

这里https://pastebin.com/pSQ3iGK2

也使用了bean(持久性实体...?)

2 个答案:

答案 0 :(得分:1)

通常所说的:OR-Mapper允许您为另一个实体建模。 我看到您的代码中的演示者具有许多程序,它们之间存在一些1:N关系。

类名“ Programme”可能是第一个错误,因为它是复数形式。最好在“ Presenter”类中使用“ Programm”并为@OneToMany关系建模。

这样做时,您只需触发一个休眠查询。找到的“ Presenter”类型的实体将包含“ Programm”的列表/集。遍历实体,并将其转换为返回值'DJAllProgrammes',该返回值应仅包含纯值(dto),而不包含对实体的引用。即将实体映射到dto。

答案 1 :(得分:0)

为此任务使用像Hibernate这样的ORM总是比直接使用JDBC层的原型版本中的db utils慢。考虑发生了什么事

List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
        .setParameterList("names", names)
        .getResultList();

解析查询之后,解析对象,然后names的大小确定将扩展为(?,?,?...)的参数的数量。
然后发送查询,结果输入后,每个查询都会创建两个副本。在结果列表中将为您提供一个,在内部将其保留以检查更改的一个。

for (Presenter presenter : result) {
    int presenter_id = presenter.getPresenter_id();
    List<Programme> programmes = session
            .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
            .setParameter("pres_orig_id", presenter_id)
            .getResultList();

在这里,我们又发生了同样的事情,只是实际情况更糟。您不是在重复使用查询,而是在每个循环上创建一个新查询,然后将其丢弃。
嵌套循环中发生了同样的事情。
同样,如果presenter.getPresenter_id()返回一个Integer对象而不是原始的int,则表示您正在进行不必要的拆箱,然后在.setParameter("pres_orig_id", presenter_id)调用中重新装箱。如果该方法返回一个Integer对象,则将其更改为Integer presenter_id。但是,如果它是原始的int,则没有必要,但这不会造成伤害,因为唯一的用法是作为对象传递。您甚至可以直接在setParameter中使用它。

因此,总的来说,当您将createQuery调用带出循环时,您会得到它。

@Override
public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
    DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
    session.beginTransaction();
    List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
            .setParameterList("names", names)
            .getResultList();
    TypedQuery<Programme> progByPresenterOrigin = session
                .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class);
    TypedQuery<Track> trackByProgrammeId = session
                    .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
    for (Presenter presenter : result) {
        List<Programme> programmes = progByPresenterOrigin
                .setParameter("pres_orig_id", presenter.getPresenter_id())
                .getResultList();
        for (Programme programme : programmes) {
            //this is the critical performance death zone 
            List<Track> tracksOnThisProgramme = trackByProgrammeId
                    .setParameter("progIds", programme.getProgramme_id())
                    .getResultList();
            djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
        }
    }
    session.getTransaction().commit();
    return djAllProgrammes;
}