《精通Hibernate》学习(6)——通过Hibernate操纵对象

一、与触发器协同工作

当Hibernate与数据库的触发器协同工作时,会造成两类问题:

1、触发器会使Session缓存中的数据与数据库不一致

当Session向数据库中保存、更新或删除对象时,如果会激发数据库中的某个触发器,常常会带来一个问题,那就是Session缓存中的持久化对象无法与数据库中的数据保持同步。问题的原因在于触发器运行在数据库中,它执行的操作对Session是透明的。 

比如说Session缓存中已经有一个Customer对象,接下来触发器修改了CUSTOMERS表的相应记录,而触发器的操作对Session是透明的,Session无法检测到数据库中数据的变化,因此Session不会自动刷新缓存中的Customer对象。

解决方法:加入Session的save()、update()、saveOrUpdate()、delete()方法会激发一个触发器,而这个触发器行为会导致Session的缓存的数据和数据库不一致,解决的方法是在执行完这个操作后,立即调用Session的flush()和reflush()方法,迫使他们同步。

tx=session.beginTransaction();
session.save(customer);
session.flush();
session.reflush(customer);
tx.commit();

 

2、Session的update()方法盲目地激发触发器

 

二、利用拦截器(Interceptor)生成审计日志 

在实际的编程中,可能要记录数据库中重要数据的更新历史,每当发生对CUSTOMERS表进行操作时都会向审计日志表中插入一条记录。

数据库的触发器常常用来生成审计日志,这种方法简单但是不能支持跨平台数据库;Hibernate的拦截器也可以生成审计日志而且不依赖于具体的数据库平台。可以把Interceptor看成是持久化层面上的触发器。

用户使用触发器必须要实现org.hibernate.Interceptor接口,在这个接口中主要定义了一下方法:

 

三、Hibernate的事件处理机制 

Hibernate 3与Hibernate 2的一个很大的区别在于,Hibernate 3的核心处理模块采用“事件/监听器”设计模式。例如,当Hibernate从数据库中加载一个对象时,它会出发一个加载事件。在默认情况下,该事件由Hibernate的内置的默认监听器处理,该监听器会从数据库中家挨相应的对象。

 

四、批量处理数据

通常,在一个Session对象的缓存中只存放数量有限的持久化对象,等到Session对象处理事务完毕后,还要关闭Session对象,从而及时释放Session的缓存占用的内存。批量处理数据是指在一个事务中处理大量的数据。如:

Transaction tx=session.beginTransaction();
Iterator customers=session.createQuery("from Customer c where c.age>0").list().iterator();
while(customer.hasNext()){
Customer customer=(Customer)custoemrs.next();
customer.setAge(customer.getAge()+1);
}
tx.commit();
session.close();

如果CUSTOMERS表中有一万条age>0记录,那么Hibernate一下要加载一万条Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate会执行1万条更新记录。

这样做有两大缺点:占用过大内存;执行Update语句过多。

一般说,应当尽可能避免在应用层进行批量操作,而应该在数据库执行进行批量操作。例如,直接在数据库中执行批量SQL语句,如果逻辑比较复杂可以使用数据库中的存储过程来完成批量操作。

当并不是所有的DB System都支持存储过程(Mysql就不支持)。

在应用层也可以进行批量操作,主要有以下方式:

(1)通过Session来进行批量操作;

(2)通过StatelessSession来进行批量操作;

(3)通过HQL来进行批量操作;

(4)直接通过JDBC API来进行批量操作。

 

1、通过Session来进行批量操作

如批量插入十万条数据,单次插入20条记录,即每20次执行一次Session.flush()向数据库中批量插入20条,然后再调用sessio.clear()来将刚保存的20条记录从缓存中清除。

Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();

for(int i=0;i<100000;i++){
Customer customer=new Customer(.......);
session.save(customer);
if(i%20==0){
session.flush();//执行批量插入20条的记录,并清理缓存
session.clear();//清空缓存}}
tx.commit();
session.close();

通过Session来进行批量操作会受到以下条件的约束:

(1)需要在Hibernate的配置文件中设置JDBC单次批量处理的数目(建议设置为10-50):

hibernate.jdbc.batch_size=20;

(2)如果对象采用“identity”标识符生成器,则Hibernate无法在JDBC层进行批量操作。

(3)建议关闭Hibernate第二级缓存。

hibernate.cache.use_second_level_cache=false;

而进行批量更新数据时,如果一下子把所有的对象都加载到Session缓存中,然后再在缓存中一一更新他们,显然是不可取的。解决问题的方法是使用可滚动的结果集org.hibernate.ScrollabeResults,Query的scroll()犯非法返回一个ScrollabeResults对象。如下:  

Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();

ScrollableResults customers=session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while(customers.next()){
Customer customer=(Customer) customers.get(0);
customer.setAge(customer.getAge()+1);
if(++count%20==0){
session.flush();
session.clear();}}
tx.commit();
session.close();


 2、通过StatelessSession来进行批量操作

 

StatelessSession session=sessionFactory.openStatelessSession();
Transaction tx=session.beginTransaction();

ScrollableResults customers=session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while(customers.next()){
Customer customer=(Customer) customers.get(0);
customer.setAge(customer.getAge()+1);
}
tx.commit();
session.close();

从形式看(无状态)StatelessSession与Session用法相似,但是有如下区别:

(1)StatelessSession没有缓存,通过StatelessSession来加载、保存或更新后的对象都处于游离状态。
(2)StatelessSession不会与Hibernate的第二级缓存交互。
(3)当调用StatelessSession的save()、update()或delete()方法时,这些方法会立即执行相应的SQL语句,而不会仅计划执行一条SQL语句。
(4)StatelessSession不会对所加载的对象自动进行脏检查。所以在以上程序中,修改了内存中Customer对象的属性后,还需要通过StatelessSession的update()方法来更新数据库中的相应数据。
(5)StatelessSession不会对关联的对象进行任何级联操作。举例来说,通过StatelessSession来保存一个Customer对象时,不会级联保存与之关联的Order对象。
(6)StatelessSession所做的操作可以被Interceptor拦截器捕获到,但会被Hibernate的事件处理系统忽略。
(7)通过同一个StatelessSession对象两次加载OID为1的Customer对象时,会得到两个具有不同内存地址的Customer对象,例如:

StatelessSession session =sessionFactory.openStatelessSession();
Customer c1=(Customer)session.get(Customer.class,newLong(1));
Customer c2=(Customer)session.get(Customer.class,newLong(1));
System.out.println(c1==c2); //打印false

 

3、通过HQL来进行批量操作

Hibernate3中的HQL(Hibernate QueryLanguage,Hibernate查询语言)不仅可以检索数据,还可以用于进行批量更新、删除和插入数据。批量操作实际上直接在数据库中完成,所处理的数据不会被保存在Session的缓存中,因此不会占用内存空间。
Query.executeUpdate()方法和JDBCAPI中的PreparedStatement.executeUpdate()很相似,前者执行用于更新、删除和插入的HQL语句,而后者执行用于更新、删除和插入的SQL语句。

(1).批量更新数据
以下程序代码演示通过HQL来批量更新Customer对象

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate =
"update Customer c set c.name = :newName where c.name =:oldName";
int updatedEntities = session.createQuery( hqlUpdate )
.setString( "newName", "Mike" )
.setString( "oldName", "Tom" )
.executeUpdate();

tx.commit();
session.close();
以上程序代码向数据库发送的SQL语句为:

update CUSTOMERS set NAME="Mike" where NAME="Tom"


(2).批量删除数据

Session的delete()方法一次只能删除一个对象,不适合进行批量删除操作。以下程序代码演示通过HQL来批量删除Customer对象:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlDelete = "delete Customer c where c.name =:oldName";
int deletedEntities = session.createQuery( hqlDelete ).setString( "oldName", "Tom" ).executeUpdate();
tx.commit();
session.close();

以上程序代码向数据库提交的SQL语句为

delete from CUSTOMERS where NAME="Tom";

(3).批量插入数据

插入数据的HQL语法为:

insert into EntityName properties_list select_statement;

以上EntityName表示持久化类的名字,properties_list表示持久化类的属性列表,select_statement表示子查询语句。
HQL只支持insert into ... select ... 形式的插入语句,而不支持"insert into ...values ... "形式的插入语句。
下面举例说明如何通过HQL来批量插入数据。

假定有DelinquentAccount和Customer类,它们都有id和name属性,与这两个类对应的表分别为DELINQUENT_ACCOUNTS和CUSTOMERS表。DelinquentAccount.hbm.xml和Customer.hbm.xml文件分别为这两个类的映射文件。以下代码能够把CUSTOMERS表中的数据复制到DELINQUENT_ACCOUNTS表中:

 

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlInsert = "insert into DelinquentAccount (id, name)select c.id, c.name from Customer c wherec.id>1";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
tx.commit();
session.close();


以上程序代码向数据库提交的SQL语句为:

insert into DELINQUENT_ACCOUNTS(ID,NAME) select ID,NAME from CUSTOMERS where ID>1 ;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值