Hibernate用Query删除数据表中的数据的有关问题(EntityManager.clear())

Hibernate用Query删除数据表中的数据的问题(EntityManager.clear())

前段时间把项目中的JPA从EclipseLink换成Hibernate。这其中遇到了很多问题,也记录了一部分在其他的文章中。这里介绍一个UT中遇到的问题。
当时的测试是忘H2数据库中插入多项数据,然后用Query的方式删除,assert读出来的数据是null。这个测试用例在EclipseLink下面是可以通过的,但换成Hibernate之后发现数据仍然可以读出这个数据。
测试用例如下:

 @Test
    public void shouldRemovesAllFmReportFromDb()
    {
        FmReport fmReport1 = givenFmReportIsStoredInDb();
        FmReport fmReport2 = givenFmReportIsStoredInDb();
        FmReport fmReport3 = givenFmReportIsStoredInDb();

        Integer previousSize = getEntityManager().createQuery( "SELECT re FROM FmReport re" ).getResultList().size();
        System.out.println("*** before delete, the size is " + previousSize);

        FmReport fmReportById1 = getEntityManager().find(FmReport.class, fmReport1.getId());
        Assert.assertNotNull( fmReportById1 );


        getEntityManager().createQuery( "DELETE FROM FmReport" ).executeUpdate();

        Integer finalSize = getEntityManager().createQuery( "SELECT re FROM FmReport re" ).getResultList().size();
        System.out.println("*** after delete, the size is " + finalSize);

        Assert.assertNull( getEntityManager().find(FmReport.class, fmReport1.getId()));
    }

测试的log:

18:25:36.027 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
18:25:36.029 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
18:25:36.029 [main] DEBUG o.h.internal.SessionFactoryImpl - Checking 0 named SQL queries
18:25:36.042 [main] DEBUG o.h.s.internal.StatisticsInitiator - Statistics initialized [enabled=false]
18:25:36.105 [main] DEBUG o.h.e.i.EntityManagerFactoryRegistry - Initializing EntityManagerFactoryRegistry : org.hibernate.ejb.internal.EntityManagerFactoryRegistry@6ef93d8a
18:25:36.105 [main] DEBUG o.h.e.i.EntityManagerFactoryRegistry - Registering EntityManagerFactory: test-pu 18:25:36.216 [main] DEBUG o.h.e.t.spi.AbstractTransactionImpl - begin
18:25:36.216 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Obtaining JDBC connection
18:25:36.216 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Obtained JDBC connection
18:25:36.216 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - initial autocommit status: true
18:25:36.216 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - disabling autocommit
18:25:36.223 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
18:25:36.231 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[1]]
18:25:36.234 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 1, using strategy: org.hibernate.id.SequenceHiLoGenerator
18:25:36.249 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
18:25:36.249 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[2]]
18:25:36.249 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 2, using strategy: org.hibernate.id.SequenceHiLoGenerator
18:25:36.250 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
18:25:36.250 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[3]]
18:25:36.250 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 3, using strategy: org.hibernate.id.SequenceHiLoGenerator
18:25:36.268 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
18:25:36.270 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
18:25:36.273 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 3 insertions, 0 updates, 0 deletions to 3 objects
18:25:36.273 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
18:25:36.274 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
18:25:36.277 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.277 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.277 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.277 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Changes must be flushed to space: fm_report
18:25:36.289 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
18:25:36.295 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
18:25:36.296 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
18:25:36.300 [main] DEBUG org.hibernate.SQL - select fmreport0_.id as id1_0_, fmreport0_.alarms_json as alarms_j2_0_, fmreport0_.date as date3_0_ from fm_report fmreport0_
18:25:36.302 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
18:25:36.306 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[FmReport#1]
18:25:36.307 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1
18:25:36.308 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[FmReport#2]
18:25:36.309 [main] DEBUG org.hibernate.loader.Loader - Result set row: 2
18:25:36.309 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[FmReport#3]
*** before delete, the size is 3
18:25:36.322 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
18:25:36.323 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
18:25:36.323 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects
18:25:36.324 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
18:25:36.324 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
18:25:36.324 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.324 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.324 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.330 [main] DEBUG org.hibernate.SQL - delete from fm_report
18:25:36.334 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
18:25:36.335 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
18:25:36.337 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects
18:25:36.337 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
18:25:36.337 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
18:25:36.337 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.338 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.338 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.338 [main] DEBUG org.hibernate.SQL - select fmreport0_.id as id1_0_, fmreport0_.alarms_json as alarms_j2_0_, fmreport0_.date as date3_0_ from fm_report fmreport0_
*** after delete, the size is 0
18:25:36.338 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
18:25:36.339 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
18:25:36.339 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects
18:25:36.339 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.339 [main] DEBUG o.h.e.t.spi.AbstractTransactionImpl - rolling back
18:25:36.343 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - rolled JDBC Connection
18:25:36.343 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - re-enabling autocommit
18:25:36.346 [main] DEBUG o.h.e.j.internal.JdbcCoordinatorImpl - HHH000420: Closing un-released batch
18:25:36.346 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Releasing JDBC connection
18:25:36.346 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Released JDBC connection

java.lang.AssertionError: expected null, but was:<FmReport@88dfd83>

可以在Log中明显看到执行了对应的删除的sql语句:
18:25:36.330 [main] DEBUG org.hibernate.SQL - delete from fm_report
并且再用select语句读取数据库时,数据是真实从数据库中删掉了的,但为什么entityManager的find()函数还是能够读取到对应的entity呢?
并且entity printer的打印显示它们仍然存在:

18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
18:25:36.339 [main] DEBUG o.h.internal.util.EntityPrinter - FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}

分析得出的结论是:

  • hibernate query的executeUpdate()是直接对数据库进行操作
  • executeUpdate()不对cache进行更新,executeUpdate()会造成find()和query select的不同步。

这点在JBoss的官网上有描述:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-direct

This means that manipulating data directly in the database (using the SQL Data Manipulation Language (DML) the statements: INSERT, UPDATE, DELETE) will not affect in-memory state. However, Hibernate provides methods for bulk SQL-style DML statement execution that is performed through the Hibernate Query Language (HQL).

并且,hibernate做了多次的dirty checking collections:

    Line 21: 18:25:36.270 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
    Line 41: 18:25:36.323 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
    Line 50: 18:25:36.335 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
    Line 60: 18:25:36.339 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections

可见,除了出现在hibernate中使用query是有比较大的performance的开销。
但注意,这个问题不是common的,不同的JPA有不同的实现方式,EclipseLink就没有这个问题。
那我们该怎么解决呢?
问题的关键在于同步cache/mem中的数据与数据库同步,调用clear()函数可以解决这个问题:

        getEntityManager().clear();
        getEntityManager().createQuery( "delete FmReport" ).executeUpdate();

Log:

14:14:16.348 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - --- HQL AST ---
 \-[DELETE] Node: 'DELETE'
    \-[FROM] Node: 'FROM'
       \-[RANGE] Node: 'RANGE'
          \-[DOT] Node: '.'
             +-[DOT] Node: '.'
             |  +-[DOT] Node: '.'
             |  |  +-[DOT] Node: '.'
             |  |  |  +-[DOT] Node: '.'
             |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  |  |  +-[IDENT] Node: 'com'
             |  |  |  |  |  |  |  |  |  \-[IDENT] Node: 'nsn'
             |  |  |  |  |  |  |  |  \-[IDENT] Node: 'ood'
             |  |  |  |  |  |  |  \-[IDENT] Node: 'cp'
             |  |  |  |  |  |  \-[IDENT] Node: 'aswu'
             |  |  |  |  |  \-[IDENT] Node: 'fm'
             |  |  |  |  \-[IDENT] Node: 'nms'
             |  |  |  \-[IDENT] Node: 'db'
             |  |  \-[IDENT] Node: 'report'
             |  \-[IDENT] Node: 'domain'
             \-[IDENT] Node: 'FmReport'

14:14:16.348 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.352 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete << begin [level=1, statement=delete]
14:14:16.353 [main] DEBUG o.h.h.internal.ast.tree.FromElement - FromClause{level=1} : com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport (<no alias>) -> fmreport0_
14:14:16.354 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete : finishing up [level=1, statement=delete]
14:14:16.354 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete >> end [level=1, statement=delete]
14:14:16.355 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - --- SQL AST ---
 \-[DELETE] DeleteStatement: 'DELETE'  querySpaces (fm_report)
    \-[FROM] FromClause: 'FROM' FromClause{level=1, fromElementCounter=1, fromElements=1, fromElementByClassAlias=[], fromElementByTableAlias=[fmreport0_], fromElementsByPath=[], collectionJoinFromElementsByPath=[], impliedElements=[]}
       \-[FROM_FRAGMENT] FromElement: 'fm_report' FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=fm_report,tableAlias=fmreport0_,origin=null,columns={,className=com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport}}

14:14:16.355 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.358 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.358 [main] DEBUG o.h.internal.SessionFactoryImpl - Checking 0 named SQL queries
14:14:16.369 [main] DEBUG o.h.s.internal.StatisticsInitiator - Statistics initialized [enabled=false]
14:14:16.449 [main] DEBUG o.h.e.i.EntityManagerFactoryRegistry - Initializing EntityManagerFactoryRegistry : org.hibernate.ejb.internal.EntityManagerFactoryRegistry@775dfcbc
14:14:16.449 [main] DEBUG o.h.e.i.EntityManagerFactoryRegistry - Registering EntityManagerFactory: test-pu 14:14:16.589 [main] DEBUG o.h.e.t.spi.AbstractTransactionImpl - begin
14:14:16.589 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Obtaining JDBC connection
14:14:16.589 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Obtained JDBC connection
14:14:16.589 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - initial autocommit status: true
14:14:16.589 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - disabling autocommit
14:14:16.597 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
14:14:16.619 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[1]]
14:14:16.624 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 1, using strategy: org.hibernate.id.SequenceHiLoGenerator
14:14:16.644 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
14:14:16.645 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[2]]
14:14:16.645 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 2, using strategy: org.hibernate.id.SequenceHiLoGenerator
14:14:16.645 [main] DEBUG org.hibernate.SQL - select nextval ('fm_report_id_seq')
14:14:16.645 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[3]]
14:14:16.645 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 3, using strategy: org.hibernate.id.SequenceHiLoGenerator
14:14:16.665 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
14:14:16.666 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
14:14:16.669 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 3 insertions, 0 updates, 0 deletions to 3 objects
14:14:16.669 [main] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
14:14:16.672 [main] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
14:14:16.675 [main] DEBUG o.h.internal.util.EntityPrinter - com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport{id=3, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
14:14:16.675 [main] DEBUG o.h.internal.util.EntityPrinter - com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport{id=2, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
14:14:16.676 [main] DEBUG o.h.internal.util.EntityPrinter - com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport{id=1, alarmsJson=jsonString, date=Thu Jan 01 08:00:00 CST 1970}
14:14:16.676 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Changes must be flushed to space: fm_report
14:14:16.693 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
14:14:16.702 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
14:14:16.703 [main] DEBUG org.hibernate.SQL - insert into fm_report (alarms_json, date, id) values (?, ?, ?)
14:14:16.711 [main] DEBUG org.hibernate.SQL - select fmreport0_.id as id1_0_, fmreport0_.alarms_json as alarms_j2_0_, fmreport0_.date as date3_0_ from fm_report fmreport0_
14:14:16.712 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
14:14:16.715 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport#1]
14:14:16.715 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1
14:14:16.715 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport#2]
14:14:16.715 [main] DEBUG org.hibernate.loader.Loader - Result set row: 2
14:14:16.715 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport#3]
*** before delete, the size is 3
14:14:16.723 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - parse() - HQL: delete com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport
14:14:16.726 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - --- HQL AST ---
 \-[DELETE] Node: 'delete'
    \-[FROM] Node: 'FROM'
       \-[RANGE] Node: 'RANGE'
          \-[DOT] Node: '.'
             +-[DOT] Node: '.'
             |  +-[DOT] Node: '.'
             |  |  +-[DOT] Node: '.'
             |  |  |  +-[DOT] Node: '.'
             |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  |  +-[DOT] Node: '.'
             |  |  |  |  |  |  |  |  |  +-[IDENT] Node: 'com'
             |  |  |  |  |  |  |  |  |  \-[IDENT] Node: 'nsn'
             |  |  |  |  |  |  |  |  \-[IDENT] Node: 'ood'
             |  |  |  |  |  |  |  \-[IDENT] Node: 'cp'
             |  |  |  |  |  |  \-[IDENT] Node: 'aswu'
             |  |  |  |  |  \-[IDENT] Node: 'fm'
             |  |  |  |  \-[IDENT] Node: 'nms'
             |  |  |  \-[IDENT] Node: 'db'
             |  |  \-[IDENT] Node: 'report'
             |  \-[IDENT] Node: 'domain'
             \-[IDENT] Node: 'FmReport'

14:14:16.726 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.727 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete << begin [level=1, statement=delete]
14:14:16.727 [main] DEBUG o.h.h.internal.ast.tree.FromElement - FromClause{level=1} : com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport (<no alias>) -> fmreport0_
14:14:16.727 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete : finishing up [level=1, statement=delete]
14:14:16.727 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - delete >> end [level=1, statement=delete]
14:14:16.728 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - --- SQL AST ---
 \-[DELETE] DeleteStatement: 'delete'  querySpaces (fm_report)
    \-[FROM] FromClause: 'FROM' FromClause{level=1, fromElementCounter=1, fromElements=1, fromElementByClassAlias=[], fromElementByTableAlias=[fmreport0_], fromElementsByPath=[], collectionJoinFromElementsByPath=[], impliedElements=[]}
       \-[FROM_FRAGMENT] FromElement: 'fm_report' FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=fm_report,tableAlias=fmreport0_,origin=null,columns={,className=com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport}}

14:14:16.728 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.728 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
14:14:16.730 [main] DEBUG org.hibernate.SQL - delete from fm_report
14:14:16.732 [main] DEBUG org.hibernate.SQL - select fmreport0_.id as id1_0_, fmreport0_.alarms_json as alarms_j2_0_, fmreport0_.date as date3_0_ from fm_report fmreport0_
*** after delete, the size is 0
14:14:16.733 [main] DEBUG org.hibernate.loader.Loader - Loading entity: [com.nsn.ood.cp.aswu.fm.nms.db.report.domain.FmReport#1]
14:14:16.733 [main] DEBUG org.hibernate.SQL - select fmreport0_.id as id1_0_0_, fmreport0_.alarms_json as alarms_j2_0_0_, fmreport0_.date as date3_0_0_ from fm_report fmreport0_ where fmreport0_.id=?
14:14:16.733 [main] DEBUG org.hibernate.loader.Loader - Done entity load
14:14:16.734 [main] DEBUG o.h.e.t.spi.AbstractTransactionImpl - rolling back
14:14:16.738 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - rolled JDBC Connection
14:14:16.738 [main] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - re-enabling autocommit
14:14:16.739 [main] DEBUG o.h.e.j.internal.JdbcCoordinatorImpl - HHH000420: Closing un-released batch
14:14:16.739 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Releasing JDBC connection
14:14:16.739 [main] DEBUG o.h.e.j.i.LogicalConnectionImpl - Released JDBC connection

这下清爽了许多,而且也没有了dirty checking的开销

但需要注意的是:

    /**
     * Clear the persistence context, causing all managed
     * entities to become detached. Changes made to entities that
     * have not been flushed to the database will not be
     * persisted.
     */
    public void clear();

如果在clear之前我们有还有没提交的transaction, 那之前保存在内存中还没提交到数据库的修改就会被丢弃。
因此,所有的saveOrUpdate操作必须有事务控制。
另外,关于何时调用clear()函数是有比较好的总结的:

Clearing the entity manager empties its associated cache, forcing new database queries to be executed later in the transaction. It’s almost never necessary to clear the entity manager when using a transaction-bound entity manager. I see two reasons to clear:

  • when doing batch processing, in order to avoid having a giant cache eating memory and increasing the time to flush because of long dirty checks
  • when you’re doing DML or SQL queries, which completely bypass the entity manager cache. In this case, the state held by the cache doesn’t reflect what is in the database because of the queries, so you want to clear the cache to avoid this inconsistency.