通过Hibernate 操作对象(下)

1.与触发器协同工作

若Session的save()、update()、saveOrUpdate()或delete()方法会触发一个触发器,而这个触发器的行为会导致Session的缓存的数据与数据库不一致,解决办法是在执行完该操作后,立即调用Session的flush()和refresh()方法,迫使Session的缓存与数据库同步。

tx = session.beginTransaction();

session.save(customer);

session.flush();

session.refresh(customer);

tx.commit();

为避免Session的update()方法盲目的激发触发器,可以将<class>元素的select-before-update属性设为true:

<class name="mypack.Customer" table="CUSTOMERS" select-before-update="true">

...

</class>

2.利用拦截器(Interceptor)生成审计日志

可以把拦截器看成是持久化层的触发器,当Session执行save()、update()、saveOrUpdate()、delete()及flush()方法时,就会调用拦截器的相关方法。

用户定义的拦截器必须实现Interceptor接口,该接口主要方法为:

findDirty():查找Session中的脏对象,flush()方法调用该方法;

instantiate(Class clazz,Serializable id):创建实体类的实例;

isUnsaved(Object entity):Session的saveOrUpdate()方法调用该方法;

onDelete():Session删除一个对象之前调用该方法;

onFlushDirty():Session的flush方法检查到脏数据时调用该方法;

onLoad():Session初始化一个持久化对象时调用该方法;

onSave():Session保存一个对象之前调用该方法;

postFlush(Iterator entities):Session的flush()方法执行完所有SQL语句后调用该方法;

preFlush(Iterator entities):Session执行flush()方法之前调用该方法。

Hibernate还提供了Interceptor接口的一个实现类EmptyInterceptor,用户自定义的拦截器也可以扩展EmptyInterceptor类。

3.Hibernate的事件处理机制

Hibernate3的核心处理模块采用“事件/监听器”设计模式。

在org.hibernate.event包中提供了与Session的各个方法对应的事件类及监听器接口,org.hibernate.event.def包中提供了各个监听器接口的默认实现类。

Hibernate允许用户创建客户化监听器 ,来替代或扩展Hibernate默认事件处理行为:

(1)创建客户化监听器

客户化监听器可以直接实现特定的监听器接口,或者继承Hibernate提供的监听器接口的基础实现类(如AbstractSaveEventListener),或者继承Hibernate提供的监听器接口的默认实现类(如DefaultSaveEventListener)。

(2)注册客户化监听器

方式一:在Hibernate的配置文件中静态注册

<event type="load">

    <listener class="mypack.MyLoadListener1" />

    <listener class="mypack.MyLoadListener2" />

</event>

方式二:在程序中动态注册

Configuration config = new Configuration();

LoadEventListener[] listenerStack = {new MyLoadListener1(), new MyLoadListener2()};

config.getEventListeners90.setLoadEventListeners(listenerStack);

4.批量处理数据

4.1.通过Session来进行批量操作

在处理完一个对象或一小批对象后,立即调用flush()方法清理缓存,然后再调用clear()方法清空缓存。

需要注意的是:

(1)需要在Hibernate配置文件中设置JDBC单次批量处理的数目

hibernate.jdbc.batch_size = 20

应保证每次向数据库发送的批量SQL语句数目与这个batch size属性一致;

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

(3)进行批量操作时,建议关闭Hibernate的第二级缓存(默认是关闭的)。

一、批量插入

向数据库中插入十万条CUSTOMERS记录,单次批量插入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();

        session.clear();

    }

}

tx.commit();

session.close();

为保证程序顺利运行,需要遵循以下约束:

(1)hibernate.jdbc.batch_size = 20;

(2)关闭第二级缓存;

(3)Customer对象的标识符生成器不能为“identity”

二、批量更新数据

使用可滚动的结果集ScrollableResults,Query的scroll()方法返回此对象。

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
ScrollableResults customers = session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);

int count = 0;

while(customer.next()){

    Customer customer = (Customer)customers.get(0);

    customer.setAge(customer.getAge()+1);

    if(++count % 20 ==0){

        session.flush();

        session.clear();

    }

}

tx.commit();

session.close();

为保证程序顺利运行,需要遵循以下约束:

(1)hibernate.jdbc.batch_size = 20;

(2)关闭第二级缓存;

如果在配置文件中启用了第二级缓存,可以采用以下方式忽略第二级缓存:
ScrollableResults customers = session.createQuery("from Customer") .setCatchMode(CatchMode.IGNORE)

                                                                                                     .scroll(ScrollMode.FORWARD_ONLY);

4.2.通过StatelessSession来进行批量操作

进行批量操作时,把大量对象放在Session缓存中会消耗大量内存空间,作为替代方案,可采用无状态StatelessSession来进行批量操作

Session session = sessionFactory.openStatelessSession();

Transaction tx = session.beginTransaction();
ScrollableResults customers = session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);

while(customer.next()){

    Customer customer = (Customer)customers.get(0);

    customer.setAge(customer.getAge()+1);

    session.update(customer);    //立即执行update

}

tx.commit();

session.close();

StatelessSession与Session的区别:

(1)StatelessSession没有缓存,通过StatelessSession加载、保存或更新后的对象都处于游离状态;

(2)StatelessSession不会与Hibernate的第二级缓存交互;

(3)当调用StatelessSession的save()、update()或delete()方法时,会立即执行相应的SQL语句,不会仅是计划执行;

(4)StatelessSession不会对所加载的对象自动进行脏检查;

(5)StatelessSession不会对所关联的对象进行任何级联操作;

(6)StatelessSession所做的操作可以被Interceptor拦截器捕获到,但会被Hibernate事件处理系统忽略;

(7)通过同一个StatelessSession对象两次加载OID相同的对象时,会得到两个具有不用内存地址的对象。

4.3.通过HQL来进行批量操作

HQL批量操作实际上直接在数据库中完成,所处理的数据不会被保存在Session的缓存中,因此不会占用内存空间。

一、批量更新数据

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();

二、批量删除数据

Session的delete()方法一次只能删除一个对象,不合适用来进行批量删除操作。以下用HQL来批量删除:

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
String hqlDelete = "deleteCustomer c where c.name = :oldName";

int updatedEntities = session.createQuery(hqlDelete)

                                        .setString("oldName","Tom")

                                        .executeUpdate();

tx.commit();

session.close();

三、批量插入数据

HQL只支持"insert into ... select ..." 形式的插入语句,而不支持"insert into ... values ... "形式的插入语句。

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
String hqlInsert = "insert into Account(id,name) "+

                          "select c.id, c.name from Customer c where c.id > 1";

int createdEntities = session.createQuery(hqlInsert) .executeUpdate();

tx.commit();

session.close();

4.4.直接通过JDBC API来进行批量操作

通过JDBC API来执行SQL insert、update和delete语句时,SQL语句中涉及到的数据不会被加载到内存中,因此不会占用内存空间。

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
Connection con = session.connection();

PreparedStatement stmt = con.prepareStatement("update CUSTOMERS set AGE = AGE+1 where AGE > 0");

stmt.executeUpdate();

tx.commit();

session.close();

Hibernate3及之后不提倡使用Session的connection()方法,使用Work()接口表示直接通过JDBC API来进行数据库操作。

Session的doWork(Work work)方法用于执行Work对象指定的操作,即调用Work对象的execute()方法。

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
Work work = new Work(){

    public void execute(Connection connection) throws SQLException{

        PreparedStatement stmt = connection.prepareStatement("update CUSTOMERS set AGE = AGE+1 where AGE > 0");

        stmt.executeUpdate();

    }

};

session.doWork(work);

tx.commit();

session.close();

5.使用元数据

Hibernate需要通过对象-关系映射文件中的元数据,来了解域模型中的持久化类及其属性的类型。

Hibernate为此提供了ClassMetadata和CollectionMetadata接口,让应用程序能够访问元数据,SessionFactory的getClassMetadata()和getCollectionMetadate()方法分别返回这两个接口的实例。

6.调用存储过程

在Oracle数据库中定义一个名为batchUpdateCustomer()的存储过程:

create or replace procedure batchUpdateCustomer(p_age in number) as

begin

    update CUSTOMERS set AGE=AGE+1 where AGE>0;

end;

应用程序调用该存储过程:

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
Work work = new Work(){

    public void execute(Connection connection) throws SQLException{

        String procedure = "{call batchUpdateCustomer(?)}";

        CallableStatement cstmt = connection.prepareCall(procedure);

        stmt.setInt(1,0).executeUpdate();

    }

};

session.doWork(work);

tx.commit();

session.close();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值