对象打包成List
关于使用Spring+Hibernate进行大批量数据的插入和更新它的性能和使用JDBC PreparedStatement的batch批量操作以及数据库的存储过程操作几乎可以一样高在Hibernate的官方文档里说到了BatchprocessingSpring+Hibernate大批量处理数据想要说明如何使用Hibernate大批量处理数据获得高性能
使用Hibernate将 条记录插入到数据库的一个很自然的做法可能是这样的
Session session = sessionFactoryopenSession();
Transaction tx = sessionbeginTransaction();
for ( int i=; i<; i++ ) {
Customer customer = new Customer();
sessionsave(customer);
} mit();
sessionclose();
这段程序大概运行到 条记录左右会失败并抛出 内存溢出异常(OutOfMemoryException) 这是因为 Hibernate 把所有新插入的 客户(Customer)实例在session级别的缓存区进行了缓存的缘故
我们会在本章告诉你如何避免此类问题首先如果你要执行批量处理并且想要达到一个理想的性能那么使用JDBC的批量(batching)功能是至关重要将JDBC的批量抓取数量(batch size)参数设置到一个合适值(比如之间)
hibernatejdbcbatch_size
你也可能想在执行批量处理时关闭二级缓存
hibernatecacheuse_second_level_cache false
批量插入(Batch inserts)
如果要将很多对象持久化你必须通过经常的调用 flush() 以及稍后调用 clear() 来控制第一级缓存的大小
Session session = sessionFactoryopenSession();
Transaction tx = sessionbeginTransaction();
for ( int i=; i<; i++ ) {
Customer customer = new Customer();
sessionsave(customer);
if ( i % == ) { // same as the JDBC batch size //与JDBC批量设置相同
//flush a batch of inserts and release memory:
//将本批插入的对象立即写入数据库并释放内存
sessionflush();
sessionclear();
} }
mit();
sessionclose();
批量更新(Batch updates)
此方法同样适用于检索和更新数据此外在进行会返回很多行数据的查询时你需要使用 scroll() 方法以便充分利用服务器端游标所带来的好处
Session session = sessionFactoryopenSession();
Transaction tx = sessionbeginTransaction();
ScrollableResults customers = sessiongetNamedQuery(GetCustomers)
setCacheMode(CacheModeIGNORE)
scroll(ScrollModeFORWARD_ONLY);
int count=; while ( customersnext() ) {
Customer customer = (Customer) customersget();
customerupdateStuff();
if ( ++count % == ) {
//flush a batch of updates and release memory:
sessionflush();
sessionclear();
} }
mit();
sessionclose();
大批量更新/删除(Bulk update/delete)
就像已经讨论的那样自动和透明的 对象/关系 映射(object/relational mapping)关注于管理对象的状态这就意味着对象的状态存在于内存因此直接更新或者删除 (使用 SQL语句 UPDATE 和 DELETE)数据库中的数据将不会影响内存中的对象状态和对象数据 不过Hibernate提供通过Hibernate查询语言(第 章 HQL:Hibernate查询语言)来执行大批量SQL风格的(UPDATE)和(DELETE) 语句的方法
UPDATE 和 DELETE语句的语法为 (UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?有几点说明
在FROM子句(fromclause)中FROM关键字是可选的
在FROM子句(fromclause)中只能有一个类名并且它不能有别名
不能在大批量HQL语句中使用连接(显式或者隐式的都不行)不过在WHERE子句中可以使用子查询
整个WHERE子句是可选的
举个例子使用QueryexecuteUpdate()方法执行一个HQL UPDATE语句
Session session = sessionFactoryopenSession();
Transaction tx = sessionbeginTransaction();
String hqlUpdate = update Customer set name = :newName where name = :oldName;
int updatedEntities = screateQuery( hqlUpdate )
setString( newName newName )
setString( oldName oldName )
executeUpdate();
mit();
sessionclose();
执行一个HQL DELETE同样使用 QueryexecuteUpdate() 方法(此方法是为那些熟悉JDBCPreparedStatementexecuteUpdate() 的人们而设定的)
Session session = sessionFactoryopenSession();
Transaction tx = sessionbeginTransaction();
String hqlDelete = delete Customer where name = :oldName;
int deletedEntities = screateQuery( hqlDelete )
setString( oldName oldName )
executeUpdate();
mit();
sessionclose();
由QueryexecuteUpdate()方法返回的整型值表明了受此操作影响的记录数量注意这个数值可能与数据库中被(最后一条SQL语句)影响了的行数有关也可能没有一个大批量HQL操作可能导致多条实际的SQL语句被执行举个例子对joinedsubclass映射方式的类进行的此类操作这个返回值代表了实际被语句影响了的记录数量在那个joinedsubclass的例子中对一个子类的删除实际上可能不仅仅会删除子类映射到的表而且会影响根表还有可能影响与之有继承关系的joinedsubclass映射方式的子类的表
注意上述大批量HQL操作的少数限制会在新版本中得到改进进一步详细信息请参考JIRA里的路线图