AbstractFlushingEventListener中的Hibernate刷新减速(JBoss 5.2.2 Hibernate 3.3.2)

时间:2014-12-03 03:33:53

标签: hibernate jpa jboss seam jboss5.x

我遇到了一个问题,即hibernate刷新策略在处理订单上的一组订单项时会随着时间的推移而减速。当后续更新被调用到JBoss Seam更新时,速度会增加。

代码的业务流程接受带有'n'个订单项的订单,为业务目的修改每个订单项,在该订单项上调用seam / hibernate update(),然后转到下一个订单项。在Hibernate时,刷新策略会在调用update()时立即响应。但是当代码沿着行项目列表向下移动时,Hibernate会逐渐减慢(几乎呈指数级)。启用日志记录后,我可以看到减速开始为秒,然后是一两分钟,然后是5分钟以上,然后是20分钟以上。所有处理集合中的相同订单行项目。它不会更改集合的大小,也不会添加或删除任何项目。

我已经启用了对hibernate事务的跟踪,并在输出中找到org.hibernate.event.def.AbstractFlushingEventListener在调用之间的某个地方遇到问题:

prepareCollectionFlushes(session);

flushEntities(event);

我根据Hibernate创建的日志条目跟踪了这两个方法调用之间的问题:

2014-12-02 17:50:30,808 DEBUG [org.hibernate.engine.CollectionEntry] (http-0.0.0.0-8443-2) Collection dirty: [com.companioncabinet.ccs.domain.data.Job.jobPhases#10324859]

2014-12-02 18:19:09,015 TRACE [org.hibernate.event.def.AbstractFlushingEventListener] (http-0.0.0.0-8443-2) Flushing entities and processing referenced collections

注意17:50的消息组,然后是18:19的时间间隔。代码全部在Hibernate的AbstractFlushingEventListner中。我检查了Hibernate源代码,发现它们的代码看起来像这样导致上面的消息:

prepareCollectionFlushes(session);   
// now, any collections that are initialized   
// inside this block do not get updated - they   
// are ignored until the next flush   

persistenceContext.setFlushing(true);   
try {   
   flushEntities(event);   
   flushCollections(session);   
}   
   finally {   
   persistenceContext.setFlushing(false);   
}

出现的消息来自:

prepareCollectionFlushes(session);

flushingEntities(event);

方法prepareCollectionFlushes调用CollectionEntry.preFlush,它产生“Collection dirty”消息。

以下是所涉及的所有方法:

prepareCollectionFlushes

/**  
* Initialize the flags of the CollectionEntry, including the  
* dirty check.  
*/   
private void prepareCollectionFlushes(SessionImplementor session) throws HibernateException {   
   // Initialize dirty flags for arrays + collections with composite elements   
   // and reset reached, doupdate, etc.   
   log.debug("dirty checking collections");   
   final List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );   
   final int size = list.size();   
   for ( int i = 0; i < size; i++ ) {   
   Map.Entry e = ( Map.Entry ) list.get( i );   
      ( (CollectionEntry) e.getValue() ).preFlush( (PersistentCollection) e.getKey() );   
   }   
}

预冲洗

public void preFlush(PersistentCollection collection) throws HibernateException {

    boolean nonMutableChange = collection.isDirty() && 
            getLoadedPersister()!=null && 
            !getLoadedPersister().isMutable();
    if (nonMutableChange) {
        throw new HibernateException(
                "changed an immutable collection instance: " + 
                MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
            );
    }

    dirty(collection);

    if ( log.isDebugEnabled() && collection.isDirty() && getLoadedPersister() != null ) {
        log.debug(
                "Collection dirty: " +
                MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
            );
    }

    setDoupdate(false);
    setDoremove(false);
    setDorecreate(false);
    setReached(false);
    setProcessed(false);
}

/**
 * Determine if the collection is "really" dirty, by checking dirtiness
 * of the collection elements, if necessary
 */
private void dirty(PersistentCollection collection) throws HibernateException {

    boolean forceDirty = collection.wasInitialized() &&
            !collection.isDirty() && //optimization
            getLoadedPersister() != null &&
            getLoadedPersister().isMutable() && //optimization
            ( collection.isDirectlyAccessible() || getLoadedPersister().getElementType().isMutable() ) && //optimization
            !collection.equalsSnapshot( getLoadedPersister() );

    if ( forceDirty ) {
        collection.dirty();
    }

}

上面的块中的 collection.dirty()将PersistenceCollection脏设置为true。

flushEntities

/**  
* 1. detect any dirty entities  
* 2. schedule any entity updates  
* 3. search out any reachable collections  
*/   
private void flushEntities(FlushEvent event) throws HibernateException {   
   log.trace("Flushing entities and processing referenced collections");   
   ...remainder of code omitted since the delay happens before the above log entry appears

掌握所有代码后,没有任何明确的事情发生。在延迟期间,代码中没有任何内容发生。一切似乎都停止了。

数据库是PostgreSQL。我已启用所有查询的记录。当我将最后一个查询对齐到延迟之前时,它似乎是所有脏的实体的重新加载。当我运行记录的选择时,它只需不到1毫秒。

我知道这些是旧版本,“升级”是要做的事情。但是,最好指出一个特定的缺陷,错误或一般已知的性能问题作为触发升级的催化剂。

感谢您的期待!

orm.xml中

<persistence-unit-metadata>
    <persistence-unit-defaults>
        <entity-listeners>
            <entity-listener class="org.jboss.seam.security.EntitySecurityListener"/>
        </entity-listeners>
    </persistence-unit-defaults>
</persistence-unit-metadata>

的persistence.xml

<persistence-unit name="DS1" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/DS1</jta-data-source>
    <properties>
        <property name="hibernate.archive.autodetection" value="class" />
        <property name="hibernate.search.default.indexBase" value="/home/lucene/DS1" />
         <!-- Binds the EntityManagerFactory to JNDI where Seam can look it up.
        This is only relevant when the container automatically loads the persistence unit, as is the case in JBoss AS 5. -->
        <property name="jboss.entity.manager.factory.jndi.name" value="java:/DS1EntityManagerFactory"/>
    </properties>

</persistence-unit>

hibernate.properties为空

ds.xml中

<local-tx-datasource>
    <jndi-name>DS1</jndi-name>
    <connection-url>jdbc:postgresql://127.0.0.1:5432/data</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>a_user</user-name>
    <password>a_pw</password>
    <idle-timeout-minutes>90</idle-timeout-minutes>
    <new-connection-sql>select 1</new-connection-sql>
    <check-valid-connection-sql>select 1</check-valid-connection-sql>
</local-tx-datasource>

数据库日志摘录

2014-12-08 19:43:19 EST LOG:  duration: 0.000 ms  execute <unnamed>: update PurchaseOrder set active=$1, createdby=$2, createdStamp=$3, ...
2014-12-08 19:43:19 EST LOG:  duration: 0.000 ms  execute <unnamed>: select orderlines0_.purchaseorderid as purchas66_18_5_, ...
2014-12-08 19:49:29 EST LOG:  duration: 0.000 ms  execute <unnamed>: update PurchaseOrder set active=$1, createdby=$2, createdStamp=$3, ...
2014-12-08 19:49:29 EST LOG:  duration: 0.000 ms  execute <unnamed>: select orderlines0_.purchaseorderid as purchas66_18_5_, ...

1 个答案:

答案 0 :(得分:0)

持久性上下文越大,花费的时间越to verify all dirty properties

默认的自动脏检查机制必须遍历当前连接到当前运行的Session的所有实体的所有属性。

如果你想加快速度,你需要:

  1. switch to a custom dirty checking mechanism
  2. use bytecode enhancingdidn't seem to work on latest Hibernate versions
  3. 更新

    你的一个陈述引起了我的注意:

      

    代码的业务流程接受订单,其中包含&#39; n&#39;订单项,   为业务目的修改每个行项目,调用seam / hibernate   对该订单项进行update(),然后移至下一个订单项。在   当呼叫时,Hibernate刷新策略会立即响应   更新()。

    如果您加载N个订单项,则只要会话仍处于活动状态,就需要运行更新。脏检查机制将自动检测所有更改。

    如果要混合管理实体更改并发出选择,则AUTO flush mode will trigger a flush prior to any HQL query。因此,最好安排代码先进行选择,然后对事务的最后一部分进行修改。

    您还可以将休眠刷新模式设置为COMMIT:

    session.setFlushMode(FlushMode.COMMIT);