Hibernate中session的save方法以及缓存的清理

Posted on 2013-04-17 21:41  bokeyuan123465  阅读(1740)  评论(0编辑  收藏  举报

 Session的save()方法使一个临时对象转变为持久化对象。例如以下代码保存一个Customer对象:

        Customer customer = new Customer();
        customer.setId(new Long(9)); // 为Customer临时对象设置OID是无效的
        customer.setName("Tom");
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        session.save(customer);
        session.close();

Session的save()方法完成以下的操作:

    (1)把Customer对象加入到缓存中,使它变为持久化对象。

    (2)选用映射文件指定的标识符生成器为持久化对象分配惟一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定标识符生成器:(此配置文件跟Customer类放在一起,generator的class还有另外一个取值“native”)

<id name="id" column="ID">
            <generator class="increment"/>
        </id>

  

以上程序试图通过setId()方法为Customer临时对象设置OID是无效的。假如起初CUSTOMERS表中没有记录,那么执行完save()方法后,Customer对象的ID为1。如果希望由程序来为Customer对象指定OID,可以调用save()的另一个重载方法:  

save(customer, new Long(9));

    以上save()方法的第二参数显示指定Customer对象的OID。这种形式的save()方法不推荐使用,尤其在使用代理主键的场合,不应该由程序为持久化对象指定OID。

    (3)计划执行一个insert语句,把Customer对象当前的属性值组装到insert语句中:

 insert into CUSTOMERS(ID, NAME, ......) values(1, 'Tom', ......);

值得注意的是,save()方法并不立即执行SQL insert语句只有当Session清理缓存(事务提交时先清理缓存,transaction.commit())时,才会执行SQL insert语句。如果在save()方法之后,又修改了持久化对象的属性,这会使得Session在清理缓存时,额外执行SQL update语句。以下两段代码尽管都能完成相同的功能,但是左边代码仅执行一条SQL insert语句,而右边代码执行一条SQL insert和一条SQL update语句。上边代码减少了操作数据库的次数,具有更好的运行性能

 Customer customer = new Customer();         Customer customer = new Customer();
        // 先设置Customer对象的属性,再保存它       session.save(customer);
        customer.setName("Tom");                    // 先保存Customer对象,再修改它的属性
        session.save(customer);                     customer.setName("Tom");
        transaction.commit();                       transaction.commit();//此时清理缓存

 Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当Customer对象处于持久化状态时,不允许程序随意修改它的OID,例如:

Customer customer = new Customer();
        session.save(customer);
        customer.setId(new Long(100)); // 抛出HibernateException
        transaction.commit();

以上代码会导致Session在清理缓存时抛出异常。

注意:无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的OID。因此,比较安全的做法是,在定义持久化类时,把它的setId()方法设为为private类型,禁止外部程序访问该方法。

Session的save()方法是用来持久化一个临时对象的。在应用程序中应该把持久化对象或游离对象传给save()方法。例如以下代码两次调用了Session的save()方法,第二次传给save()方法的Customer对角处于持久化状态,这步操作其它是多余的:

  Customer customer = new Customer();
        session.save(customer);
        customer.setName("Tom");
        session.save(customer); // 这步操作是多余的
       transaction.commit();

 在例如以下代码把Customer游离对象传给session2的save()方法,session2会把它当做临时对象处理,再次向数据库中插入一条Customer记录:

Customer customer = new Customer();
        customer.setName("Tom");
        Session session1 = sessionFactory.openSession();
        Transaction tx1 = session1.beginTransaction();
        session1.save(customer); // 此时Customer对象的ID变为1
        tx1.commit();
        session1.close(); // 此时Customer对象变为游离对象
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        session2.save(cutomer); // 此时Customer对象的ID变为2
        tx2.commit();
        session2.close();

尽管以上程序代码能正常运行,但是会导致CUSTOMERS表中有两条代表相同业务实体的记录,因此不符合业务逻辑。

 ------------------------------------------------------------------------------------------------------

在默认情况下,session会在下面的时间点清理缓存

当应用程序调用net.sf.hibernate.Transactioncommit()方法时,commit()方法先清理缓存,然后再看数据库提交事务

当调用sessionfind()或者iterator()时,如果缓存中持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。

当应用程序显式调用sessionflush()方时。

Session的setFlushMode()方法用于设定清理缓存的时间点。FlushMode类定义了三种不同的清理模式:FlushMode.AUTO、FlushMode.COMMIT和FlushMode.NEVER。例如,以下代码显示把清理模式设为FlushModo.Commit:

  session.setFlushMode(FlushMode.COMMIT);

                                                                                             三种清理模式

 清理缓存的模式

 Session的查询方法

 Session的commit()方法

 Session的flush()方法

 FlushMode.AUTO  清理  清理  清理
 FlushMode.COMMIT  不清理  清理  清理
 FlushMode.NEVER  不清理  不清理  不清理

 

 FlushMode.AUTO是默认值,这也是优先考虑的清理模式,它会保证在整个事务中,数据保持一致。如果事务公包含查询数据库的操作,而不会修改数据库的数据,也可以选用FlushMode.COMMIT模式,这可以避免在执行Session查询方法时先清理缓存,以稍微提高应用程序的性能。

    在大多数情况下,应用程序不需要显示调用Session的flush()方法,flush()方法适用于以下场合:

    (1)插入、删除或更新某个持久化对象会引发数据库中的触发器。假定向CUSTOMERS表新增一条记录时会引发一个数据库触发器,在应用程序中,通过Session的save()方法保存了一个Customer对象,应用随后调用Session的flush()方法:

session.save(customer);

      session.flush();

Session的flush()方法会立即执行insert语句,该语句接着引发相关的触发器工作。

    (2)在应用程序中混合使用Hibernate API和JDBC API。

    (3)JDBC驱动程序不健壮,导致Hibernate在自动清理缓存的模式下无法正常工作。

 我们知道,如果一味的让新的数据放到缓存中去,那我们计算机肯定会内存崩溃。所以进行必要的缓存清除还是很有必要的。 
下面我们分析一下几种方法: 
1、

for(int i=0;i<1000;i++){   
Order order = new Order();   
order.setId();   
session.save(order);   
if(i%100==0){   
session.flush();   
session.clear();   
}   
}  

2 、

for(int i=0;i<1000;i++){   
Order order = new Order();   
order.setId();   
session.save(order);   
session.evict();//清除session缓存   
SessionFactory.evict();//清除二级缓存   
}  

 注意:一级缓存只在同一个session中有效,二级缓存是全局性质的

一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据。
二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。(
Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。

evict()方法 
  该方法于上一个方法不同,它只能用于处理单个对象的清除工作。 

clear()方法 
  我们可以在session-factory标签下创建property标签,name属性为hibernate.jdbc.batch_size,值为我们想要设定的数字,假如为100,下一步当我们执行操作flush()发送SQL语句时候调用session.clear()方法,就可以实现清除缓存的效果了。 

 

 

Copyright © 2024 bokeyuan123465
Powered by .NET 8.0 on Kubernetes