【问题标题】:Why hibernate generates ~500 SQL queries?为什么 hibernate 会生成约 500 个 SQL 查询?
【发布时间】:2017-10-06 15:31:00
【问题描述】:

在一个遗留项目中尝试优化 Hibernate 4.2 生成的 MySQ 慢查询时,我发现下面的代码生成了近 500 个 SQL 查询(有很多重复):

class MyDAO {
    public List<Message> findMessages() {
        Session session = MyHibernateUtils.openSession();

        String queryStr = "SELECT DISTINCT m FROM Message m "
                + " LEFT JOIN fetch m.types types "
                + " LEFT JOIN fetch m.mainType mainType "
                + " LEFT JOIN fetch m.place place "
                + " LEFT JOIN fetch m.building building "
                + " LEFT JOIN fetch m.city city "
                + " LEFT JOIN fetch m.kind kind "
                + " LEFT JOIN fetch m.domain domain "
                + " LEFT JOIN fetch m.action action "
                + " LEFT JOIN fetch m.customParameterA customParameterA "
                + " LEFT JOIN fetch m.customParameterB customParameterB "
                + " LEFT JOIN fetch m.scheduleEvents scheduleEvents "
                + " LEFT JOIN fetch m.comments comments "
                + " LEFT JOIN fetch m.messageLastActivities messageLastActivities "
                + " LEFT JOIN fetch m.customListA customListA "
                + " LEFT JOIN fetch m.childEvents childEvents "
                + " LEFT JOIN fetch m.parentEvent parentEvent "
                + " WHERE ...";

        List<Message> messages;
        try {
            session.getTransaction().begin();

            Query query = session.createQuery(queryStr);
            query.setTimeout(10);

            messages = query.list();
            session.getTransaction().commit();
        } catch (RuntimeException e) {
            session.getTransaction().rollback();
            throw e;
        } finally {
            session.close();
        }

        return messages;
    }
}

我怎样才能避免有这么多的 SQL 查询?

我不知道它是否有帮助,但实体之间有很多 onyToMany 和 manyToMany 关系。

感谢您的帮助。

【问题讨论】:

  • ORM 框架大部分时间都是这样工作的......他们需要额外触发一个SELECT * FROM table WHERE id = 1来查找相关实体
  • @RaymondNijland 不,他们没有。好吧,在最坏的情况下,他们会这样做,但大多数时候这是可以避免的,当然在 Hibernate 中也是如此。只有非常幼稚的 ORM 才会这样做,因为它会降低性能并使框架变得毫无价值。
  • 至于 OP,为 Hibernate 启用 SQL 日志记录(环顾四周,我不记得它是如何完成的)并查看执行了 what 查询.这将允许您查明原因,这可能只是您需要修复的单个 N+1 查询。
  • 真@Kayaman ..
  • @Kayaman 是的,他们这样做了,而且 raymond 是对的。 Eagar 提取通常需要额外的操作(如添加注释或连接)

标签: java mysql hibernate


【解决方案1】:

您应该检查 hibernate 正在生成的查询,以查看经常访问哪个表。

您还必须 join fetch 与您的 related entities 相关的实体,请参阅此处:

Hibernate is doing multiple select requests instead one (using join fetch)

我个人更喜欢使用带注释的 @BatchSize() 进行延迟加载,以保持延迟查询计数较小。只需使用 2 的 Batch-Size 即可将查询次数减少一半。

还可以查看 @Cache 注释,它可以显着减少您的查询计数。 (想想所有几乎静态的东西,比如城市/建筑/类型/域等)

【讨论】:

  • 正确!谢谢@dognose,在添加了一些缺失的相关实体后,查询的数量大大减少了。
【解决方案2】:

根据您的关系设计,@OneToMany@ManyToManyFetch 的默认值是LAZY,这意味着在子实体中加载相关记录(当您调用getter 方法时)hibernate 再执行一个查询加载该记录(例如:select * from foo where id = ?),因此如果加载的实体(主实体)包含许多子实体,例如 ManyToManyOneToMany,您将在控制台中看到许多查询。 要使这些查询无效,您可以将Fetch 设置为EAGER,但不建议在优化时这样做。

@Entity
public class MainEntity {

@ManyToMany(Fetch = FetchType.EAGER)
public List<Foo> foos;

}

【讨论】:

  • 感谢@Moodi 的回复! left join fetch 的使用是否已经强制每个 *ToMany 依赖项都被急切地获取?
  • 是的,它会获取属性。
  • 您可以在子实体中使用实体。
猜你喜欢
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-18
相关资源
最近更新 更多