首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在调用clear()之前,Hibernate使用flushMode=AUTO查询的速度要慢得多

在调用clear()之前,Hibernate使用flushMode=AUTO查询的速度要慢得多
EN

Stack Overflow用户
提问于 2012-04-13 23:35:32
回答 2查看 10.7K关注 0票数 22

我有一个长时间运行(但相当简单)的应用程序,它使用Hibernate (通过JPA)。它在运行过程中经历了相当戏剧性的减速。我已经能够将范围缩小到偶尔需要一个entityManager.clear()调用。当Hibernate的实体管理器跟踪100,000个实体时,它比仅跟踪少数几个实体慢约100倍(请参见下面的结果)。我的问题是:为什么在跟踪大量实体时会如此缓慢?还有没有其他办法绕过它呢?

!更新:我已经能够将范围缩小到Hibernate的自动刷新代码。!

特别是org.hibernate.event.internal.AbstractFlushingEventListenerflushEntities()方法(至少在Hibernate 4.1.1.Final中是这样),其中有一个循环,它遍历持久化上下文中的所有实体,围绕刷新每个实体执行一些广泛的检查(即使在我的示例中所有实体都已经刷新了!)。

因此,部分回答了我的问题的第二部分,可以通过将查询的刷新模式设置为FlushModeType.COMMIT来解决性能问题(请参见下面的更新结果)。例如:

Place place = em.createQuery("from Place where name = :name", Place.class)
    .setParameter("name", name)
    .setFlushMode(FlushModeType.COMMIT)  // <-- yay!
    .getSingleResult();

..。但这似乎是一个相当丑陋的解决方案--将知道事物是否被刷新到query方法的责任传递给query方法,而不是将其保留在更新方法中。这也意味着我必须在所有查询方法上设置刷新模式来提交,或者更有可能的是,在EntityManager上设置它。

这让我想知道:这是预期的行为吗?我在刷新或定义实体的方式上做错了什么吗?或者这是Hibernate的限制(或者可能是Hibernate中的bug )?

我用来隔离问题的示例代码如下:

测试实体

@Entity @Table(name="place") @Immutable
public class Place {
    private Long _id;
    private String _name;

    @Id @GeneratedValue
    public Long getId() { return _id; }
    public void setId(Long id) { _id = id; }

    @Basic(optional=false) @Column(name="name", length=700,
        updatable=false, nullable=false, unique=true,
        columnDefinition="varchar(700) character set 'ascii' not null")
    public String getName() { return _name; }
    public void setName(String name) { _name = name; }

    @Override
    public boolean equals(Object o) { /* ... */ }

    @Override
    public int hashCode() { return getName().hashCode(); }
}

基准代码

我的测试代码生成100000个随机的地名并插入它们。然后按名称随机查询出其中的5000个。name列上有一个索引。

Place place = em.createQuery(
    "select p from Place p where p.name = :name", Place.class)
    .setParameter("name", name)
    .getSingleResult();

为了进行比较,并确保它不在数据库中,我对一个单独的随机选择的5000个地名运行了以下基于JDBC的查询(在em.unwrap(Session.class).doWork(...)下):

PreparedStatement ps = c.prepareStatement(
    "select id, name from place where name = ?");
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
    Place place = new Place();
    place.setId(rs.getLong(1));
    place.setName(rs.getString(2));
}
rs.close();
ps.close();

(请注意,我确实为基准测试的5000个查询中的每个查询创建并关闭了一个PreparedStatement )。

结果

下面的所有结果都是5000次查询的平均值。JVM被赋予了-Xmx1G

Seconds/Query    Approach
0.000160s        JDBC
0.000286s        Hibernate calling clear() after import and every 100 queries
0.000653s        Hibernate calling clear() once after the import
0.012533s        Hibernate w/o calling clear() at all
0.000292s        Hibernate w/o calling clear(), and with flush-mode COMMIT

其他观察结果:在Hibernate查询期间(没有任何清晰调用),java进程将内核的利用率锁定在接近100%的水平。JVM堆大小从未超过500MB。在查询期间也有大量的GC活动,但是CPU利用率显然是由Hibernate代码控制的。

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10143880

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档