Hibernate3 学习(九)

11快取

二级快取可以跨越 Session 生命周期,Hibernate 透过第三方来实现二级快取,这边也来看看 Query 的快取。

111二级快取(Second-level

Hibernate[Session level 快取]随着Session生命周期起始与消灭。

以第一个 Hibernate中的范例来说,在未使用二级快取的情况下,如果使用以下的程序片段来查询数据:

Session session = sessionFactory.openSession();

User user1 = (User) session.load(User.class, new Integer(1));

user1.getName();

session.close();

 

session = sessionFactory.openSession();

User user2 = (User) session.load(User.class, new Integer(1));

user2.getName();

session.close();

Hibernate将会使用以下的SQL来进行数据查询:

Hibernate:

select user0_.id as id0_, user0_.name as name0_0_, user0_.age as age0_0_

from user user0_ where user0_.id=?

Hibernate:

select user0_.id as id0_, user0_.name as name0_0_, user0_.age as age0_0_

from user user0_ where user0_.id=?

由于Session被关闭,Session level无法起作用,所以第二次的查询仍必须向数据库直接查询。

Hibernate二级快取可以跨越数个Session,二级快取由同一个SessionFactory所建立的Session所共享,因而又称为SessionFactory level快取。

Hibernate本身并未提供二级快取的实现,而是藉由第三方(Third-party)产品来实现,Hibernate预设使用EHCache作为其二级快取的实现,在最简单的情况下,您只需在Hibernate下撰写一个ehcache.xml作为EHCache的资源定义档,可以在 Hibernate下载档案中的etc目录下找到一个已经撰写好的ehcache.xml,以下撰写最简单的ehcache.xml

ehcache.xml

<ehcache>

    <diskStore path="java.io.tmpdir"/>

    <defaultCache

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="120"

        timeToLiveSeconds="120"

        overflowToDisk="true"

        />

</ehcache>

将这个档案放在Hibernate项目Classpath可存取到的路径下,接着重新运行上面的程序片段,您可以发现Hibernate将使用以下的SQL进行查询:

Hibernate:

select user0_.id as id0_, user0_.name as name0_0_, user0_.age as age0_0_

from user user0_ where user0_.id=?

二级快取被同一个SessionFactory所建立的Session实例所共享,所以即使关闭了Session,下一个Session仍可使用二级快取,在查询时,Session会先在Session level快取中查询看有无数据,如果没有就试着从二级快取中查询数据,查到数据的话就直接返回该笔数据,所以在上例中,第二次无需再向数据库进行SQL查询。

如果打算清除二级快取的资料,可以使用SessionFactoryevict()方法,例如:
sessionFactory.evict(User.class, user.getId());

如果打算在Hibernate中使用其它第三方产品进行快取,则可以在hibernate.cfg.xml中定义hibernate.cache.provider_class属性,例如:

hibernate.cfg.xml

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

      

<hibernate-configuration>

 

    <session-factory>

 

        ....

        <property name="hibernate.cache.provider_class">

            org.hibernate.cache.HashtableCacheProvider

        </property>

        ....

       

    </session-factory>

 

</hibernate-configuration>

HashtableCacheHibernate自己所提供的二级快取实现,不过性能与功能上有限,只用于开发时期的测试之用。

可以在映射文件中指定快取策略,使用<cache>卷标在映像实体或Collection上设定快取策略,例如:

User.hbm.xml

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

 

<hibernate-mapping>

 

    <class name="onlyfun.caterpillar.User" table="user">

        <cache usage="read-only"/>

 

        <id name="id" column="id" type="java.lang.Integer">

            <generator class="native"/>

        </id>

 

        <property name="name" column="name" type="java.lang.String"/>

 

        <property name="age" column="age" type="java.lang.Integer"/>

 

    </class>

 

</hibernate-mapping>

可以设定的策略包括read-onlyread-writenonstrict-read-writetransactional,并不是每一个第三方快取实现都支持所有的选项,每一个选项的使用时机与支持的产品,可以直接参考Hibernate官方参考快册的 20.2.The Second Level Cache

112 Query 快取

HibernateSession level 快取会在使用Sessionload()方法时起作用,在设定条件进行查询时,无法使用快取的功能,现在考虑一种情况,您的数据库表格中的数据很少变动,在使用Query查询数据时,如果表格内容没有变动,您希望能重用上一次查询的结果,除非表格内容有变动才向数据库查询。

您可以开启Query的快取功能,因为要使用Query的快取功能必须在两次查询时所使用的SQL相同,且两次查询之间表格没有任何数据变动下才有意义, 所以Hibernate预设是关闭这个功能的,如果您觉得符合这两个条件,那么可以试着开启Query快取功能来看看效能上有无改进。

先来看看下面的查询程序片段:

Session session = sessionFactory.openSession();

       

String hql = "from User";

       

Query query = session.createQuery(hql);

List users = query.list();

       

for(int i = 0; i < users.size(); i++) {

    User user = (User) users.get(i);

    System.out.println(user.getName());

}

       

query = session.createQuery(hql);

users = query.list();

       

for(int i = 0; i < users.size(); i++) {

    User user = (User) users.get(i);

    System.out.println(user.getName());

}

       

session.close();

在不启用Query快取的情况下,Hibernate会使用两次SQL向数据库查询数据:

Hibernate:

select user0_.id as id, user0_.name as name0_, user0_.age as age0_

from user user0_

momor

caterpillar

 

Hibernate: select user0_.id as id, user0_.name as name0_, user0_.age as age0_ from user user0_

momor

caterpillar

如果打算启用Query快取功能,首先在hibernate.cfg.xml中设定hibernate.cache.use_query_cache属性:

hibernate.cfg.xml

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

      

<hibernate-configuration>

 

    <session-factory>

        ....

 

        <property name="hibernate.cache.use_query_cache">true</property>

        ....

       

    </session-factory>

 

</hibernate-configuration>

然后在每次建立Query实例时,执行setCacheable(true)

Session session = sessionFactory.openSession();

       

String hql = "from User";

       

Query query = session.createQuery(hql);

// 使用Query快取

query.setCacheable(true);

List users = query.list();

       

for(int i = 0; i < users.size(); i++) {

    User user = (User) users.get(i);

    System.out.println(user.getName());

}

       

query = session.createQuery(hql);

// 使用Query快取

query.setCacheable(true);

users = query.list();

        

for(int i = 0; i < users.size(); i++) {

    User user = (User) users.get(i);

    System.out.println(user.getName());

}

       

session.close();

Hibernate在启用Query快取后,会保留执行过的查询SQL与查询结果,在下一次查询时会看看SQL是否相同,并看看对应的数据库表格是否有变动(Update/Delete/Insert),如果SQL相同且数据库也没有变动,则将Query快取中的查询结果返回,上面的程序片段将使用一次 SQL查询,第二次查询时直接返回快取中的结果:

Hibernate:

select user0_.id as id, user0_.name as name0_, user0_.age as age0_

from user user0_

momor

caterpillar

momor

caterpillar

113 Query.listiterator

Query上有list()iterator()方法,两者的差别在于list()方法在读取数据时,并不会利用到快取,而是直接再向数据库查询,而iterator()则将读取到的数据写到快取,并于读取时再次利用。

来看看下面的程序:

Session session = sessionFactory.openSession();

       

Query query = session.createQuery("from User");

List users = query.list();

users = query.list();

 

session.close();

这个程序片段会使用两次SQL来查询数据库:

Hibernate:

select user0_.id as id, user0_.name as name0_, user0_.age as age0_

from user user0_

 

Hibernate: select user0_.id as id, user0_.name as name0_, user0_.age as age0_

from user user0_

如果在Session关闭之前,要再将所有数据在取出,可以使用iterator()方法,例如:

Session session = sessionFactory.openSession();

 

Query query = session.createQuery("from User");

Iterator users = query.iterate();

users = query.iterate();

 

session.close();

这个程序片段会使用一次SQL向数据库查询,第二次则直接从快取中取得数据:

Hibernate: select user0_.id as col_0_0_ from user user0_

由于使用iterator()方法时会使用到Session level快取,所以在查询大量数据时,会耗用大量的内存,必要时可以使用Sessionevict()clear()方法来清除快取。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值