《Hibernate快速开始 – 批量处理》
JDBC 批量处理
JDBC提供了对批量查询语句集合到单条查询语句的支持。在应用方面,这意味着数据驱动在批量处理的时候只需要发送一条查询请求,减少了对数据库的请求次数。hibernate合理使用了JDBC批量处理的特点,可以通过以下配置控制这一行为。
hibernate.jdbc.batch_size
设定hibernate单条批量处理请求中查询语句数量的上限。可用零或负数来禁用此功能。
hibernate.jdbc.batch_versioned_date
有些JDBC驱动在处理批量请求的时候会返回错误的行数。如果使用的JDBC驱动在此列的话,这里应设置为false。如果不是的话,可以使用这个设置,允许hibernate对有版本标记的实体记录进行DML批量处理,并使用返回的行数进行乐观锁检查。5.0之后,这一设定的默认值为true。在之前的版本,默认值为false。
hibernate.jdbc.batch.builder
设置实现批量处理的具体设置的类名。一般情况下不建议使用默认之外的设置。不过如果需要,可以通过org.hibernate.engine.jdbc.batch.spi.BatchBuilder来实现。
hibernate.order_updates
强制命令hibernate以SQL update请求中的PK值进行排序。此设置允许更多的批量处理。在有大量concurrent任务的系统中会减少数据库锁死。需要记录使用前后的性能来决定是否在程序中使用。
hibernate.order_inserts
强制命令hibernate将SQL insert语句排序以提高批量处理的数量。需要记录使用前后的性能来决定是否在程序中使用。
5.2版本以后,hibernate允许覆盖JDBC对于单个session批量处理数量的全局设定,hibernate.jdbc.batch_size。
案例1,单个session的hibernate JDBC批量处理设置。
entityManager .unwrap( Session.class ) .setJdbcBatchSize( 10 );
Session批量处理
下面展示了一个批量添加的案例
案例2,使用hibernate原生代码添加100000个实体记录
EntityManager entityManager = null; EntityTransaction txn = null; try { entityManager = entityManagerFactory().createEntityManager(); txn = entityManager.getTransaction(); txn.begin(); for ( int i = 0; i <= 100; i++ ) { Person Person = new Person( String.format( "Person %d", i ) ); entityManager.persist( Person ); } txn.commit(); } catch (RuntimeException e) { if ( txn != null) txn.isActive()) txn.rollback(); throw e; } finally { if (entityManager != null) { entityManager.close(); } }
这个案例有很多问题
hibernate在session层面缓存所有新添加的customer记录。所以在事务处理完成之后,100000个实体记录是被持久层管理。如果JVM被分配的内存过少的话,可能会导致OutOfMemoryException的错误。java 1.8 JVM下四分之一或者1Gb的可用RAM都可以轻松在heap中管理100000个对象。
过久的事务处理会删除链接
JDBC的批量处理并不是默认开启的,所以每一个添加语句都需要跟数据库的接收和回复。要开启JDBC批量化处理的话,需将hibernate.jdbc.batch_size设为10-50之间的整数。如果使用identity identifier生成器的话,hibernate将会关闭JDBC层面上添加语句的批量处理。
insert批量处理
要将对象持久化的话,需要对session使用empty()和flush()函数以此来管理第一层缓存的数量。
案例3,对session使用empty()和flush()
EntityManager entityManager = null; EntityTransaction txn = null; try { entityManager = entityManagerFactory().createEntityManager(); txn = entityManager.getTransaction(); txn.begin(); int batchSize = 25; for ( int i = 0; i <= entityCount; ++i ) { Person Person = new Person( String.format( "Person %d", i ) ); entityManager.persist( Person ); if ( i < 0 && i % batchSize == 0 ) { //flush a batch of inserts and release memory entityManager.flush(); entityManager.clear(); } } txn.commit(); } catch (RuntimeException e) { if ( txn != null && txn.isActive()) txn.rollback(); throw e; } finally { if (entityManager != null) { entityManager.close(); } }
session滚动
当进行取回和更新记录时,要用empty()和flush()对session进行适当的清理。另外,在查询返回多行记录时,还可以使用scroll()函数来发挥服务器端游标操作的优势。
案例4,使用scroll()
EntityManager entityManager = null; EntityTransaction txn = null; ScrollableResults scrollableResults = null; try { entityManager = entityManagerFactory().createEntityManager(); txn = entityManager.getTransaction(); txn.begin(); int batchSize = 25; Session session = entityManager.unwrap( Session.class ); scrollableResults = session .createQuery( "select p from Person p" ) .setCacheMode( CacheMode.IGNORE ) .scroll( ScrollMode.FORWARD_ONLY ); int count = 0; while ( scrollableResults.next() ) { Person Person = (Person) scrollableResults.get( 0 ); processPerson(Person); if ( ++count % batchSize == 0 ) { //flush a batch of updates and release memory: entityManager.flush(); entityManager.clear(); } } txn.commit(); } catch (RuntimeException e) { if ( txn != null) txn.isActive()) txn.rollback(); throw e; } finally { if (scrollableResults != null) { scrollableResults.close(); } if (entityManager != null) { entityManager.close(); } }
如果程序没有关闭entityManager,hibernate会在当前事务结束后(commit或rollback操作后)自动将所有ScrollableResults内部调用的资源(包括ResultSet和PreparedStatement)关闭。
但是,建议手动关闭ScrollableResults
无状态session(StatelessSession)
StatelessSession是hibernate提供的用命令行延伸出的接口。可以使用这一接口将分离对象取回或传输至数据库。StatelessSession并没有持久化的引申含义,也没有提供大多数的更高级的生命周期的语法。
StatelessSession不包括:
第一级缓存
与第二级或查询语句缓存的交互
write-behind事务处理和自动的dirty checking
StatelessSession中的限制:
无状态session中的操作不会与相关联的instances串联
忽略Collections
不支持lazy loading
无状态session中的操作会越过hibernate的事件模型和拦截器
由于缺少第一级缓存,无状态session会受到data 命名问题的影响
无状态session是抽象的,更接近JDBC
案例5,使用无状态session
StatelessSession statelessSession = null; Transaction txn = null; ScrollableResults scrollableResults = null; try { SessionFactory sessionFactory = entityManagerFactory().unwrap( SessionFactory.class ); statelessSession = sessionFactory.openStatelessSession(); txn = statelessSession.getTransaction(); txn.begin(); scrollableResults = statelessSession .createQuery( "select p from Person p" ) .scroll(ScrollMode.FORWARD_ONLY); while ( scrollableResults.next() ) { Person Person = (Person) scrollableResults.get( 0 ); processPerson(Person); statelessSession.update( Person ); } txn.commit(); } catch (RuntimeException e) { if ( txn != null && txn.getStatus() == TransactionStatus.ACTIVE) txn.rollback(); throw e; } finally { if (scrollableResults != null) { scrollableResults.close(); } if (statelessSession != null) { statelessSession.close(); } }
查询返回的customer实例会立即分离。不会有任何持久性关联。
StatelessSession中insert(),update(),delete()等操作直接在数据行操作。这导致SQL操作会被立即执行。这和session接口中定义的save(), saveOrUpdate()和delete()不同。
hibernate的DML查询语句
DML(数据处理语言)指的是SQL查询语句,比如insert,update,delete。hibernate中的HQL支持bulk 类SQL的DML查询
HQL/JPQL中的update和delete
hibernate原生的查询语言和JPQL都支持bulk update和delete
案例6,HQL中update和delete的伪代码
UPDATE FROM EntityName e WHERE e.name = ? DELETE FROM EntityName e WHERE e.name = ?
FROM和WHERE两个从句是可选的,建议使用
FROM从句只可以用来修饰一个实体,可用别名。如果使用别名的话,所有的属性的参考也必须使用别名。如果没有使用别名,任何对其属性的参考都是非法的。
bulk HQL中的join语句,无论是否明确指定,都被禁止。可以包含在WHERE从句中。
案例7,用Query.executeUpdate()来执行一个JPQL update语句
int updatedEntities = entityManager.createQuery( "update Person p " + "set p.name = :newName " + "where p.name = :oldName" ) .setParameter( "oldName", oldName ) .setParameter( "newName", newName ) .executeUpdate();
案例8,用Query.executeUpdate()来执行一个HQL update语句
int updatedEntities = session.createQuery( "update Person " + "set name = :newName " + "where name = :oldName" ) .setParameter( "oldName", oldName ) .setParameter( "newName", newName ) .executeUpdate();
使用VERSIONED语句时,不能使用org.hibernate.usertype.UserVersionType类。这一特性只在HQL中可用。
案例10,用Query.executeUpdate()来执行一个JPQL delete语句
int deletedEntities = entityManager.createQuery( "delete Person p " + "where p.name = :name" ) .setParameter( "name", name ) .executeUpdate();
案例11,用Query.executeUpdate()来执行一个HQL delete语句
int deletedEntities = session.createQuery( "delete Person " + "where name = :name" ) .setParameter( "name", name ) .executeUpdate()
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 《Hibernate快速开始 – 批量处理》
ss