[size=x-large]感谢别人的分享[/size]。
[size=large]Hibernate中大批量处理数据机制
如果我们要保存的数据量十分巨大,海量信息的保存、更新,那么在程序中执行添加、更新方法,如session.save(),Session对象自身开辟的一级缓存会不断消耗,直至内存溢出。因为每save()一个对象都会添加到一级缓存中,数据量太大,绝对会造成内存溢出。那么该怎样解决大批量操作数据呢?
有一种方法就是在每保存指定条数的数据时,先将一级缓存中的数据与数据库同步一下,之后再清空一级缓存,继续保存接下来的数据,依次循环,直至保存完毕。如下代码: [/size]
[size=large]实际上Hibernate中为我们处理海量信息的操作提供了解决办法,通过StatelessSession接口实现,该接口是一个无状态接口,它不和一级缓存、二级缓存交互,也不出发任何事件、监听器、拦截器,通过该接口的操作会立即发送给数据库,与JDBC功能一样, StatelessSession session = sessionFactory.openStatelessSession();
还有一种方法进行批量操作就是利用Query对象的executeUpdate()方法执行批量更新、删除、增加,它会清除相关联类的二级缓存(利用:sessionFactory.Evict(Class clz)),但是这样会造成级联和乐观锁定出现问题
如果你要执行批量处理并且想要达到一个理想的性能, 那么使用JDBC的批量(batching)功能是至关重要。将JDBC的批量抓取数量(batch size)参数设置到一个合适值 (比如,10-50之间):
hibernate.jdbc.batch_size 20
其实上面设置值和在save保存数据一定数据然后在flush 和 clear 是一个道理。
有时候,这种org.hibernate.SessionException: Session is closed!报错就可能是由于批处理不对引起的.
[size=x-large][b]Hibernate中批量更新[/b][/size]
[size=large]上面介绍的方法同样适用于批量更新数据,如果需要返回多行数据,可以使用scroll()方法,从而可充分利用服务器端游标所带来的性能优势。下面是进行批量更新的代码片段:[/size]
[size=large]通过这种方式,虽然可以执行批量更新,但效果非常不好。执行效率不高,而且需要先执行数据查询,然后再执行数据更新,并且这种更新将是逐行更新,即每更新一行记录,都需要执行一条update语句,性能非常低下。
为了避免这种情况,Hibernate提供了一种类似于SQL的批量更新和批量删除的HQL语法。
[/size]
[size=x-large]SQL风格的批量更新/删除[/size]
[size=large]Hibernate提供的HQL语句也支持批量的UPDATE和DELETE语法。
批量UPDATE和DELETE语句的语法格式如下:
UPDATE | DELETE FROM? ClassName [WHERE WHERE_CONDITIONS]
关于上面的语法格式有以下四点值得注意:
● 在FROM子句中,FROM关键字是可选的。即完全可以不写FROM关键字。
● 在FROM子句中只能有一个类名,该类名不能有别名。
● 不能在批量HQL语句中使用连接,显式的或隐式的都不行。但可以在WHERE子句中使用子查询。
● 整个WHERE子句是可选的。
假设,需要批量更改User类实例的name属性,可以采用如下代码片段完成:
[/size]
[size=large]从上面代码中可以看出,这种语法非常类似于PreparedStatement的executeUpdate语法。实际上,HQL的这种批量更新就是直接借鉴了SQL语法的UPDATE语句。
注意:使用这种批量更新语法时,通常只需要执行一次SQL的UPDATE语句,就可以完成所有满足条件记录的更新。但也可能需要执行多条UPDATE语句,这是因为有继承映射等特殊情况,例如有一个Person实例,它有Customer的子类实例。当批量更新Person实例时,也需要更新Customer实例。如果采用joined-subclass或union-subclass映射策略,Person和Customer实例保存在不同的表中,因此可能需要多条UPDATE语句。
执行一个HQL DELETE,同样使用 Query.executeUpdate() 方法,下面是一次删除上面全部记录的代码片段:
[/size]
[size=large]由Query.executeUpdate()方法返回一个整型值,该值是受此操作影响的记录数量。实际上,Hibernate的底层操作是通过JDBC完成的。因此,如果有批量的UPDATE或DELETE操作被转换成多条UPDATE或DELETE语句,该方法返回的是最后一条SQL语句影响的记录行数。
[/size]
[/size]
[size=large]Hibernate中大批量处理数据机制
如果我们要保存的数据量十分巨大,海量信息的保存、更新,那么在程序中执行添加、更新方法,如session.save(),Session对象自身开辟的一级缓存会不断消耗,直至内存溢出。因为每save()一个对象都会添加到一级缓存中,数据量太大,绝对会造成内存溢出。那么该怎样解决大批量操作数据呢?
有一种方法就是在每保存指定条数的数据时,先将一级缓存中的数据与数据库同步一下,之后再清空一级缓存,继续保存接下来的数据,依次循环,直至保存完毕。如下代码: [/size]
public void savePetInfo() {
// 创建Session对象
Session session = HibernateSessionFactory.getSession(); // 创建PetInfo对象
PetInfo petInfo = new PetInfo(); petInfo.setPetName("灰太狼"); petInfo.setPetLove(100); // 批量保存数据 for (int i=0;i<100000;i++)
{ // 保存
session.save(petInfo);
// 当保存50条之后,将缓存中的数据与数据库同步,之后清空一级缓存
if (i%50==0) {
// 将一级缓存中的数据同步到数据库中 session.flush();
// 清空一级缓存中的数据,这样不至于造成内存溢出 session.clear(); } }
// 创建Transaction对象
Transaction transaction = session.beginTransaction(); // 提交事务
transaction.commit(); // 关闭session
session.close(); }
[size=large]实际上Hibernate中为我们处理海量信息的操作提供了解决办法,通过StatelessSession接口实现,该接口是一个无状态接口,它不和一级缓存、二级缓存交互,也不出发任何事件、监听器、拦截器,通过该接口的操作会立即发送给数据库,与JDBC功能一样, StatelessSession session = sessionFactory.openStatelessSession();
还有一种方法进行批量操作就是利用Query对象的executeUpdate()方法执行批量更新、删除、增加,它会清除相关联类的二级缓存(利用:sessionFactory.Evict(Class clz)),但是这样会造成级联和乐观锁定出现问题
如果你要执行批量处理并且想要达到一个理想的性能, 那么使用JDBC的批量(batching)功能是至关重要。将JDBC的批量抓取数量(batch size)参数设置到一个合适值 (比如,10-50之间):
hibernate.jdbc.batch_size 20
其实上面设置值和在save保存数据一定数据然后在flush 和 clear 是一个道理。
有时候,这种org.hibernate.SessionException: Session is closed!报错就可能是由于批处理不对引起的.
[size=x-large][b]Hibernate中批量更新[/b][/size]
[size=large]上面介绍的方法同样适用于批量更新数据,如果需要返回多行数据,可以使用scroll()方法,从而可充分利用服务器端游标所带来的性能优势。下面是进行批量更新的代码片段:[/size]
private void testUser()throws Exception
{
//打开Session
Session session = HibernateUtil.currentSession();
//开始事务
Transaction tx = session.beginTransaction();
//查询出User表中的所有记录
ScrollableResults users = session.createQuery("from User")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
//遍历User表中的全部记录
while ( users.next() )
{
User u = (User) users.get(0);
u.setName("新用户名" + count);
//当count为20的倍数时,将更新的结果从Session中flush到数据库
if ( ++count % 20 == 0 )
{
session.flush();
session.clear();
}
}
tx.commit();
HibernateUtil.closeSession();
}
[size=large]通过这种方式,虽然可以执行批量更新,但效果非常不好。执行效率不高,而且需要先执行数据查询,然后再执行数据更新,并且这种更新将是逐行更新,即每更新一行记录,都需要执行一条update语句,性能非常低下。
为了避免这种情况,Hibernate提供了一种类似于SQL的批量更新和批量删除的HQL语法。
[/size]
[size=x-large]SQL风格的批量更新/删除[/size]
[size=large]Hibernate提供的HQL语句也支持批量的UPDATE和DELETE语法。
批量UPDATE和DELETE语句的语法格式如下:
UPDATE | DELETE FROM? ClassName [WHERE WHERE_CONDITIONS]
关于上面的语法格式有以下四点值得注意:
● 在FROM子句中,FROM关键字是可选的。即完全可以不写FROM关键字。
● 在FROM子句中只能有一个类名,该类名不能有别名。
● 不能在批量HQL语句中使用连接,显式的或隐式的都不行。但可以在WHERE子句中使用子查询。
● 整个WHERE子句是可选的。
假设,需要批量更改User类实例的name属性,可以采用如下代码片段完成:
[/size]
private void testUser()throws Exception
{
//打开Session
Session session = HibernateUtil.currentSession();
//开始事务
Transaction tx = session.beginTransaction();
//定义批量更新的HQL语句
String hqlUpdate = "update User set name = :newName";
//执行更新
int updatedEntities = session.createQuery( hqlUpdate )
.setString( "newName", "新名字" )
.executeUpdate();
//提交事务
tx.commit();
HibernateUtil.closeSession();
}
[size=large]从上面代码中可以看出,这种语法非常类似于PreparedStatement的executeUpdate语法。实际上,HQL的这种批量更新就是直接借鉴了SQL语法的UPDATE语句。
注意:使用这种批量更新语法时,通常只需要执行一次SQL的UPDATE语句,就可以完成所有满足条件记录的更新。但也可能需要执行多条UPDATE语句,这是因为有继承映射等特殊情况,例如有一个Person实例,它有Customer的子类实例。当批量更新Person实例时,也需要更新Customer实例。如果采用joined-subclass或union-subclass映射策略,Person和Customer实例保存在不同的表中,因此可能需要多条UPDATE语句。
执行一个HQL DELETE,同样使用 Query.executeUpdate() 方法,下面是一次删除上面全部记录的代码片段:
[/size]
private void testUser()throws Exception
{
//打开Session实例
Session session = HibernateUtil.currentSession();
//开始事务
Transaction tx = session.beginTransaction();
//定义批量删除的HQL语句
String hqlUpdate = "delete User";
//执行批量删除
int updatedEntities = session.createQuery( hqlUpdate )
.executeUpdate();
//提交事务
tx.commit();
//关闭Session
HibernateUtil.closeSession();
}
[size=large]由Query.executeUpdate()方法返回一个整型值,该值是受此操作影响的记录数量。实际上,Hibernate的底层操作是通过JDBC完成的。因此,如果有批量的UPDATE或DELETE操作被转换成多条UPDATE或DELETE语句,该方法返回的是最后一条SQL语句影响的记录行数。
[/size]
[/size]