Hibernate第六课--1+N问题和缓存

来源:互联网 发布:数据统计问题有哪些 编辑:程序博客网 时间:2024/06/11 01:30


一 1+N问题
二 一级缓存
三 二级缓存
四 查询缓存

一 1+N问题
1. 注意session.clear运用,尤其是在分页循环
可能内存泄露         //
2. 1+N问题(一对多,多对一时 一个对象外键连接另一个对象默认设置fetch = eger时,默认加载关联的对象,所以将A对象外键链接的B类所有数据查询)
a) lazy(fetch = lazy)
@ManyToOne(fetch=FetchType.LAZY)
@OneToMany(fetch=FetchType.LAZY)


b)BatchSize
@BatchSize(size=100)
public class Category
 
@ManyToOne
@BatchSize(size=5)
public Category getCategory() {
return category;
}

c) join fetch()
List<Topic> lists = (List<Topic>)session.createQuery("from Topic t left join t.category").list();
d)criteria
//Criteria默认使用外连接
//List<Topic> lists = (List<Topic>)session
//.createCriteria(Topic.class)
//.list();


list,iterator区别?

List接口:query.list()不使用一级缓存,每次都要发出SQL eg:(select * from tudent)。
Iterator接口: query.iterate();使用一级缓存。
1. 首先是要发出一条SQL来取得ID存入缓存,(eg: select。id from student;) 
2. 然后把ID拿到缓存中去匹配, 如果有,就直接取,
3. 如果没有,就要再发出SQL。如果都没有,将发出N+1条SQL,这就是N+1问题。

iterator
/**
* 首先将查询的主键获取
* Hibernate:
select
topic0_.id as col_0_0_
from
Topic topic0_
* @return @void
*/
@Test
public void testItrator()
{
session.beginTransaction();
Query q1 = session.createQuery("from Topic t");
Iterator<Topic> topics = q1.iterate();
q1.setCacheable(true);
 
session.getTransaction().commit();
}

/**
* Iterator
* 每次查询后将结果放入session,
* 在同一生命周期的session,iterate查询 如果session有将不会再到数据库查找,直接返回值
* @return @void
*/
@Test
public void testIterator1()
{
session.beginTransaction();
Query q1 = session.createQuery("from Topic t");
Iterator<Topic> ts = q1.iterate();
while(ts.hasNext())
{
Topic t = ts.next();
System.out.println("Topic :"+t.getTitle());
}
Query q2 = session.createQuery("from Topic t");
Iterator<Topic> ts2 = q2.iterate();
while(ts2.hasNext())
{
Topic t = ts.next();
System.out.println("Topic :"+t.getTitle());
}
session.getTransaction().commit();
}

List
/**
* List每次默认发出sql查询
* @return @void
*/
@Test
public void testList1()
{
session.beginTransaction();
Query q1 = session.createQuery("from Topic t");
List<Topic> list1 = q1.list();
for(Topic t :list1)
{
log.debug("Topic :"+t);
}
Query q2 = session.createQuery("from Topic t");
List<Topic> list2 = q2.list();
 
for(Topic t :list2)
{
log.debug("Topic :"+t);
}
session.getTransaction().commit();
}


  • 缓存总结:
    缓存原理生命周期配置方法管理一级缓存session级别的缓存session的生命周期无● get,
    ● load,
    ● iterate
    ● evit(Object obj)
    ● clear()
    ● contains(Object obj)
    ● flush()
    二级缓存
    二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存)
    二级缓存可以被所有的session共享,
    二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
    二级缓存一般使用第三方的产品,如EHCache
    二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存● jar包
    ● ehcache.xml
    ● hibernate.cfg.xml
    <property name="cache.use_second_level_cache">true</property>

    <!--注意三者区别-->
    <!-- hibernte3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte4.2
    <property name="cache.region.factory_class">
    org.hibernate.cacheehcache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte4.3.10-->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>
    ● entity类
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    publicclassCategory
    ● load默认使用二级缓存,
    ● iterate默认使用二级缓存
    ● list默认向二级缓存中加数据,但是查询时候不使用
    ● evict
    ● 注意4.3.10版本放弃了evict,改用getCache()统一管理查询缓存查询缓存 key值是hql,value值是实体主键,主要作用是缓存普通属性查询。当前关联的表发生修改,那么查询缓存生命周期结束
    <!-- 打开查询缓存 -->
    <propertyname="cache.use_query_cache">true</property>
    ●list
    iterator不起作用

    get():直接查询(支持session缓存)
    load():延迟查询(支持session缓存,sessionFactory缓存)
    iterate():每次都会发出查询满足sql条件的实体主键,放入session缓存和sessionFactory缓存,
    第二次到session缓存和sessionFactory缓存找对应的id
    如果存在直接返回实体属性,
    如果不存在,数据库查找对应实体主键的实体属性(支持session缓存,sessionFactory缓存)
    list():先到查询缓存查找当前sql对应的实体主键,
    如果存在到数据库查询实体主键对应实体属性,
    如果不存在到数据库查询sql且将实体主键存入查询缓存

    一级缓存:key:实体主键 value:实体属性 get,load
    二级缓存:key:实体主键 value:实体属性  load,iterate,list只存不取
    查询缓存:key:sql/hql value:实体主键对应的实体属性 list


    二 一级缓存
    生命周期 是session的生命周期 因此也叫session级缓存或事务级缓存



    a)支持一级缓存
    1. get()
    /**
    * 控制台输出:
    * Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
     
    * @return @void
    */
    @Test
    public void testGet()
    {
    session.beginTransaction();
    Category c1 = (Category) session.get(Category.class, 1);
    Category c2 = (Category) session.get(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
    session.getTransaction().commit();
    }

    2. load()
    /**
    * 控制台输出:
    * Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
     
    * @return @void
    */
    @Test
    public void testLoad()
    {
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class, 1);
    Category c2 = (Category) session.load(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
    session.getTransaction().commit();
    }



    3. iterate(Session缓存是实体级别的缓存,就是只有在查询对象级别的时候才使用,如果使用HQL和SQL是查询属性级别的,是不使用一级缓存的!切记!!!!)

    /**
    * Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.id=1
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.id=1
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@1bbbafc[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@1bbbafc[id=1,name=c0]
    * @return @void
    */
    @Test
    public void testIterate()
    {
    session.beginTransaction();
    Iterator c1 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    Iterator c2 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c1.hasNext())
    {
    Category c = (Category) c1.next();
    System.out.println("Category :"+c);
    }
    while(c2.hasNext())
    {
    Category c = (Category) c2.next();
    System.out.println("Category :"+c);
    }
    session.getTransaction().commit();
    }



    b)管理一级缓存:
    ● evit(Object obj)将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象. 
    ● clear()将一级缓存中的所有持久化对象清除,释放其占用的内存资源 
    ● contains(Object obj)判断指定的对象是否存在于一级缓存中. 
    ● flush()刷新一级缓存区的内容,使数据库数据保持同步. 
    @Test
    public void testClear()
    {
    session.beginTransaction();
    Iterator c1 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    Iterator c2 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c1.hasNext())
    {
    Category c = (Category) c1.next();
    System.out.println("Category :"+c);
    }
    while(c2.hasNext())
    {
    Category c = (Category) c2.next();
    System.out.println("Category :"+c);
    }
    //清除session
    session.clear();
    Iterator c3 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c3.hasNext())
    {
    Category c = (Category) c3.next();
    System.out.println("Category :"+c);
    }
    session.getTransaction().commit();
    }
    @Test
    public void testEvict()
    {
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class, 1);
    Category c2 = (Category) session.load(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
     
    //清除的是放入session的对象 不能是Query,Iterator
    session.evict(c1);
    session.evict(c2);
     
    Category c3 = (Category) session.load(Category.class, 1);
     
    System.out.println("Category3 :"+c3);
     
    session.getTransaction().commit();
    }

    c)避免大量实体数据导入内存溢出
    先flush,再clear

    d)如果数据量特别大时 考虑使用jdbc的操作存储过程
    Oracle数据库为例子.创建一个名为UserUpdate的存储过程...然后在程序中进行调用...

    UserUpdate的存储过程代码:
     
    create or replace procadure UserUpdate(u_age in number) as
    begin
    update users set age=age+1 where age>u_age;
    end;

      
      下面的是在程序中如何调用我们命名的存储过程 
                         
    Session session =SessionFactory.openSession();
    Transaction tx =session.beginTransaction();
    Connection conn=session.connection();
    String str="{call UserUpdate(?)}";
    CallableStatement cstmt= conn.prepareCall(str);
    cstmt.setInt(1,0);
    cstmt.executeUpdate();
    tx.commit();

    注意:开源的MySQL中不支持存储过程的..
    用JDBC API的好处是这样的..
    它不用把大批量的数据事先加载到内存中,然后再进行更新与修改..所以不会消耗大量内存....
    (小程序中是看不出什么差别的..当数据的记录达到一定的数据量的时候自然会发现用Hibernate API 与JDBC API的差别)


    三 二级缓存(实体类com.liuhao.hibernate4.demo.secondlevelcache
                            测试类com.liuhao.hibernate4.demo.secondleved.test)

    二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存)
    二级缓存可以被所有的session共享,
    二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
    二级缓存一般使用第三方的产品,如EHCache

    生命周期:
    二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存

    管理:
    ● 注意4.3.10以前版本
    ● evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源. 
    sessionFactory.evict(Customer.class, new Integer(1));

    evict(Class arg0)将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源 
    sessionFactory.evict(Customer.class);

    evictCollection(String arg0)将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源. 
    sessionFactory.evictCollection("Customer.orders");
     ● 4.3.10以后版本
    Cache cache = sfactory.getCache();
     
    //清除所有
    cache.evictAllRegions();
    //cache.evictEntityRegion(Category.class);//清除指定类
    //cache.evictEntity(Category.class,1);//清除指定类的指定数据
     
    /*清除集合
    cache.evictCollectionRegions();
    cache.evictCollectionRegion(arg0);
    cache.evictCollection(arg0, arg1);
    */


    一级缓存与二级缓存比较?
    一级缓存session管理(session消亡clear,evict,close,一级缓存就没有了)
    二级缓存SessionFactory管理(SessionFactory  close,evict,就没有了),SessionFactory管理session


    准备:
    1. jar包
    Hibernate版本4.3.10官网下载(注意版本!)
    ehcache.jar --> hibernate4.3.10的lib\optional\ehcache

    slf4j可以不用

    2. ehcahe.xml
    官网下载包中
    project\hibernate-ehcache\src\test\resources目录下有一个ehcache.xml
    将配置文件放置在src下

    ehcache.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
    <!--如果缓存中的对象存储超过指定的缓存数量的对象存储的磁盘地址-->
    <diskStore path="D:/ehcache"/>
    <!-- 默认cache:如果没有对应的特定区域的缓存,就使用默认缓存 -->
    <defaultCache maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600"
    overflowToDisk="false"/>
    <!-- 指定区域cache:通过name指定,name对应到Hibernate中的区域名即可-->
    <cache name="cn.javass.h3test.model.UserModel"
    eternal="false"
    maxElementsInMemory="100"
    timeToIdleSeconds="1200"
    timeToLiveSeconds="1200"
    overflowToDisk="false">
    </cache>
    </ehcache>


    配置文件解释:
    ehcache配置(ehcache.xml) 
    cache参数详解: 
    ● name:指定区域名 
    ● maxElementsInMemory :缓存在内存中的最大数目 
    ● maxElementsOnDisk:缓存在磁盘上的最大数目 
    ● eternal :设置是否永远不过期 
    ● overflowToDisk : 硬盘溢出数目 
    ● timeToIdleSeconds :对象处于空闲状态的最多秒数后销毁 
    ● timeToLiveSeconds :对象处于缓存状态的最多秒数后销毁 
    ● memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、LFU 

    关于缓存算法,常见有三种: 
    ● LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象 
    ● LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象 
    ● LFU:(First In First Out)把最早进入二级缓存的对象替换掉 

     
    常见的缓存提供商:

    Cache

    Provider class

    Type

    Cluster Safe

    Query Cache Supported

    Hashtable (not intended for production use)

    org.hibernate.cache.HashtableCacheProvider

    memory

     

    yes

    EHCache

    org.hibernate.cache.EhCacheProvider

    memory, disk

     

    yes

    OSCache

    org.hibernate.cache.OSCacheProvider

    memory, disk

     

    yes

    SwarmCache

    org.hibernate.cache.SwarmCacheProvider

    clustered (ip multicast)

    yes (clustered invalidation)

     

    JBoss TreeCache

    org.hibernate.cache.TreeCacheProvider

    clustered (ip multicast), transactional

    yes (replication)

    yes (clock sync req.)




    3. hibernate.cfg.xml配置
    <!-- 二级缓存默认打开,这里只是显示打开一下 -->
    <property name="cache.use_second_level_cache">true</property>
     
    <!-- 注意3和4者区别 -->
    <!-- hibernte 3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte 4.3.10 -->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>

    4. cache配置
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public class Category {
    private int id;
    private String name;
     
    @Id
    @GeneratedValue
    public int getId() {
    return id;
    }
    //set get...
    }

    设置二级缓存策略 
    ● READ_ONLY:实体只读缓存 
       只读缓存不允许更新,将报错Can't write to a readonly object。 
       允许新增,(从2.0以后新增直接添加到二级缓存) 
    //确保数据库中有标识符为1的FarmModel
    FarmModel farm = (FarmModel) session.get(FarmModel.class, 1);
    //如果修改将报错,只读缓存不允许修改
    //farm.setName("aaa");


    ● NONSTRICT_READ_WRITE:实体非严格读/写缓存 
       允许更新,更新后缓存失效,需再查询一次。 
       允许新增,新增记录自动加到二级缓存中。 
       整个过程不加锁。 
    ● READ_WRITE:实体读/写缓存 
       允许更新,更新后自动同步到缓存。 
       允许新增,新增记录后自动同步到缓存。 
       保证read committed隔离级别及可重复读隔离级别(通过时间戳实现) 
       整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的 
       事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。 
       读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁 
    ● TRANSACTIONAL:实体事务缓存 
       缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境 
    ● Collection集合缓存 
    @Cache(usage =CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public SortedSet<Ticket> getTickets(){
    return tickets;
    }

    和实体并发策略有相同含义; 
    但集合缓存只缓存集合元素的标识符,在二级缓存中只存放相应实体的标识符,然后再通过标识符去二级缓存查找相应的实体最后组合为集合返回 
    Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。 
    这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新 

    5. 测试
     ● 基本测试
     ● load
     
    @Test
    public void testload()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class,1);
    System.out.println("Category :"+c1);
    session.close();
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class,1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● iterate
     
    @Test
    public void testiterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    Iterator<?> iterator = q1.iterate();
    while(iterator.hasNext())
    {
    Category c = (Category) iterator.next();
    System.out.println("Category :"+c);
    }
    session.close();
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Query q2 = (Query) session2.createQuery("from Category where name='c0'");
    Iterator<?> iterator2 = q2.iterate();
    while(iterator2.hasNext())
    {
    Category c = (Category) iterator2.next();
    System.out.println("Category :"+c);
    }
    session2.close();
    sfactory.close();
    }

     ● iterate_list
     
    @Test
    public void testiterate_load() {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
     
    session.beginTransaction();
     
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    Iterator<?> iterator = q1.iterate();
    while (iterator.hasNext()) {
    Category c = (Category) iterator.next();
    System.out.println("Category :" + c);
    }
     
    session.close();
     
    // 第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
     
    Category c2 = (Category) session2.load(Category.class, 1);
     
    System.out.println("Category :" + c2);
     
    session2.close();
    sfactory.close();
    }

     ● list
     
    /**
    *
    * list 只存不取
    * @return @void
    */
    @SuppressWarnings("unchecked")
    @Test
    public void testlist_load() {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    for (Category c : (List<Category>)q1.list()) {
    System.out.println("Category :" + c);
    }
     
    session.close();
    // 第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class, 1);
     
    System.out.println("Category :" + c2);
     
    session2.close();
    sfactory.close();
    }


     ● 二级缓存管理
    注意4.3.10版本放弃了evict,改用getCache()统一管理
     
    @Test
    public void testEvict()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class,1);
    System.out.println("Category :"+c1);
    session.close();
    //4.3中evict过时,使用getCache
    //sfactory.evict(Category.class);
    Cache cache = sfactory.getCache();
     
    //清除所有
    cache.evictAllRegions();
    //cache.evictEntityRegion(Category.class);//清除指定类
    //cache.evictEntity(Category.class,1);//清除指定类的指定数据
     
    /*清除集合
    cache.evictCollectionRegions();
    cache.evictCollectionRegion(arg0);
    cache.evictCollection(arg0, arg1);
    */
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class,1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● 二级缓存的交互

    CacheMode参数用于控制具体的Session如何与二级缓存进行交互。

     CacheMode.NORMAL - 从二级缓存中读、写数据。

     CacheMode.IGNORE- session不会与二级缓存交互。

     CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。

     CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。

     CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过hibernate.cache.use_minimal_puts(是否优化二级缓存操作,最小化缓存写入操作(适用与集群缓存))的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

    如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。

    Map cacheEntries = sessionFactory.getStatistics()
    .getSecondLevelCacheStatistics(regionName)
    .getEntries();

    此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。

    hibernate.generate_statistics true
    hibernate.cache.use_structured_entries true

     ● GET
    /**
    * 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。
    * @return @void
    */
    @Test
    public void testMutual_GET()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.GET);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //不能取 所有两条sql
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
     
    session2.close();
    sfactory.close();
    }

     ● PUT
    /**
    * 仅向二级缓存写数据,但不从二级缓存中读数据。
    * @return @void
    */
    @Test
    public void testMutual_PUT()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.PUT);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //一条sql 二级缓存有 且能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● REFRESH
    /**
    * 仅向二级缓存写数据,但不从二级缓存中读数据。
    * 通过hibernate.cache.use_minimal_puts
    * (是否优化二级缓存操作,最小化缓存写入操作(适用与集群缓存))的设置,
    * 强制二级缓存从数据库中读取数据,刷新缓存内容。
    * @return @void
    */
    @Test
    public void testMutual_REFRESH()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
     
    session2.setCacheMode(CacheMode.REFRESH);
    //两条sql 二级缓存有 但不能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● IGONORE
    /**
    * session与二级缓存之间没有交互
    * @return @void
    */
    @Test
    public void testMutual_IGONORE()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.IGNORE);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //两条sql 二级缓存有 但不能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }
    总结:
    load默认使用二级缓存,iterate默认使用二级缓存
    list默认向二级缓存中加数据,但是查询时候不使用


    四 查询缓存

    原理:查询缓存 key值是hql,value值是实体主键,主要作用是缓存普通属性查询。

    查询缓存的生命周期:当前关联的表发生修改,那么查询缓存生命周期结束

    查询缓存的配置:
    1. 查询缓存默认关闭,并且需要打开二级缓存(查看二级缓存的配置)
    <!-- 打开查询缓存 -->
    <property name="cache.use_query_cache">true</property>
    <!-- 二级缓存默认打开,这里只是显示打开一下 -->
    <property name="cache.use_second_level_cache">true</property>
     
    <!-- 注意3和4区别 -->
    <!-- hibernte 3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>
    -->
    <!-- hibernte 4.3.10 -->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>

    2. 指定缓存指定类
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public class Category


    3. 查询缓存的使用:
    Query q = session.createQuery("from Category where name = 'c0'");
    //手动打开查询缓存 
    q.setCacheable(true);

    查询缓存的方法:list,iterator;
    测试类:
    list,iterator方法
    public void testquerylist(SessionFactory sfactory)
    {
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q = session.createQuery("from Category where name = 'c0'");
    q.setCacheable(true);
    List<Category> list = (List<Category>)q.list();
    for(Category c : list)
    {
    System.out.println("Category : "+c);
    }
    session.getTransaction().commit();
    session.close();
    }
    public void testqueryiterator(SessionFactory sfactory)
    {
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q = session.createQuery("from Category where name = 'c0'");
    q.setCacheable(true);
    Iterator iterate = q.iterate();
    while(iterate.hasNext())
    {
    Category c = (Category) iterate.next();
    System.out.println("Category : "+c);
    }
    session.getTransaction().commit();
    session.close();
    }

    两次都执行list
    /**
    * 第一次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@17b2b2[id=1,name=c0]
    第二次list 查询
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@43da1b[id=1,name=c0]
     
    * 第一次会将发出sql,然后将满足条件的实体主键放入查询缓存中,
    * 查询缓存中key是hql,value是查询到的实体主键
    * 第二次发出sql时,list先到查询缓存中查询,获取满足条件的id然后到数据库获取实体属性,
    * 如果没有直接到数据库查询
    * @return @void
    */
    @Test
    public void testQuerylist_list()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    }

    两次都执行iterator
    /**
    * 第一次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@1c4a5ec[id=1,name=c0]
    第二次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@21447f[id=1,name=c0]
    iterator每次都是查询满足条件的实体主键,
    然后到数据库中获取对应主键的实体属性
    * @return @void
    */
    @Test
    public void testQueryiterate_iterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    }

    第一次list,第二次iterator
    /**
    * 第一次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@17b2b2[id=1,name=c0]
    第二次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@37165f[id=1,name=c0]
    list 将查询到的实体主键放入查询缓存中,
    literator对查询缓存的策略是不存,不取
    * @return @void
    */
    @Test
    public void testQuerylist_iterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    System.out.println("第二次iterator 查询");
    testqueryiterator(sfactory);
    }

    第一次iteraotr,第二次list
    /**
    * 第一次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@1c4a5ec[id=1,name=c0]
    第二次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@1a1ff9[id=1,name=c0]
    * @return @void
    */
    @Test
    public void testQueryiterate_list()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    System.out.println("第二次list 查询");
    testquerylist(sfactory);
    }

    总结:
     ● iterator:对查询缓存不操作,不存不取,每次查询满足sql的实体主键放入session(一级缓存中),然后到一级缓存中查询对应id的实体属性,没有就到数据库中查询。
     ● list:对查询缓存,即存也取,
    先到查询缓存获取满足sql条件的实体主键,
    如果有到满足sql条件的实体主键,到数据库查询实体主键对应的实体属性,
    如果没有到数据库查询满足sql条件的实体属性,且将实体主键放入查询缓存中



    一 1+N问题
    二 一级缓存
    三 二级缓存
    四 查询缓存

    一 1+N问题
    1. 注意session.clear运用,尤其是在分页循环
    可能内存泄露         //
    2. 1+N问题(一对多,多对一时 一个对象外键连接另一个对象默认设置fetch = eger时,默认加载关联的对象,所以将A对象外键链接的B类所有数据查询)
    a) lazy(fetch = lazy)
    @ManyToOne(fetch=FetchType.LAZY)
    @OneToMany(fetch=FetchType.LAZY)


    b)BatchSize
    @BatchSize(size=100)
    public class Category
     
    @ManyToOne
    @BatchSize(size=5)
    public Category getCategory() {
    return category;
    }

    c) join fetch()
    List<Topic> lists = (List<Topic>)session.createQuery("from Topic t left join t.category").list();
    d)criteria
    //Criteria默认使用外连接
    //List<Topic> lists = (List<Topic>)session
    //.createCriteria(Topic.class)
    //.list();


    list,iterator区别?

    List接口:query.list()不使用一级缓存,每次都要发出SQL eg:(select * from tudent)。
    Iterator接口: query.iterate();使用一级缓存。
    1. 首先是要发出一条SQL来取得ID存入缓存,(eg: select。id from student;) 
    2. 然后把ID拿到缓存中去匹配, 如果有,就直接取,
    3. 如果没有,就要再发出SQL。如果都没有,将发出N+1条SQL,这就是N+1问题。

    iterator
    /**
    * 首先将查询的主键获取
    * Hibernate:
    select
    topic0_.id as col_0_0_
    from
    Topic topic0_
    * @return @void
    */
    @Test
    public void testItrator()
    {
    session.beginTransaction();
    Query q1 = session.createQuery("from Topic t");
    Iterator<Topic> topics = q1.iterate();
    q1.setCacheable(true);
     
    session.getTransaction().commit();
    }

    /**
    * Iterator
    * 每次查询后将结果放入session,
    * 在同一生命周期的session,iterate查询 如果session有将不会再到数据库查找,直接返回值
    * @return @void
    */
    @Test
    public void testIterator1()
    {
    session.beginTransaction();
    Query q1 = session.createQuery("from Topic t");
    Iterator<Topic> ts = q1.iterate();
    while(ts.hasNext())
    {
    Topic t = ts.next();
    System.out.println("Topic :"+t.getTitle());
    }
    Query q2 = session.createQuery("from Topic t");
    Iterator<Topic> ts2 = q2.iterate();
    while(ts2.hasNext())
    {
    Topic t = ts.next();
    System.out.println("Topic :"+t.getTitle());
    }
    session.getTransaction().commit();
    }

    List
    /**
    * List每次默认发出sql查询
    * @return @void
    */
    @Test
    public void testList1()
    {
    session.beginTransaction();
    Query q1 = session.createQuery("from Topic t");
    List<Topic> list1 = q1.list();
    for(Topic t :list1)
    {
    log.debug("Topic :"+t);
    }
    Query q2 = session.createQuery("from Topic t");
    List<Topic> list2 = q2.list();
     
    for(Topic t :list2)
    {
    log.debug("Topic :"+t);
    }
    session.getTransaction().commit();
    }


  • 缓存总结:
    缓存原理生命周期配置方法管理一级缓存session级别的缓存session的生命周期无● get,
    ● load,
    ● iterate
    ● evit(Object obj)
    ● clear()
    ● contains(Object obj)
    ● flush()
    二级缓存
    二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存)
    二级缓存可以被所有的session共享,
    二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
    二级缓存一般使用第三方的产品,如EHCache
    二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存● jar包
    ● ehcache.xml
    ● hibernate.cfg.xml
    <property name="cache.use_second_level_cache">true</property>

    <!--注意三者区别-->
    <!-- hibernte3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte4.2
    <property name="cache.region.factory_class">
    org.hibernate.cacheehcache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte4.3.10-->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>
    ● entity类
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    publicclassCategory
    ● load默认使用二级缓存,
    ● iterate默认使用二级缓存
    ● list默认向二级缓存中加数据,但是查询时候不使用
    ● evict
    ● 注意4.3.10版本放弃了evict,改用getCache()统一管理查询缓存查询缓存 key值是hql,value值是实体主键,主要作用是缓存普通属性查询。当前关联的表发生修改,那么查询缓存生命周期结束
    <!-- 打开查询缓存 -->
    <propertyname="cache.use_query_cache">true</property>
    ●list
    iterator不起作用

    get():直接查询(支持session缓存)
    load():延迟查询(支持session缓存,sessionFactory缓存)
    iterate():每次都会发出查询满足sql条件的实体主键,放入session缓存和sessionFactory缓存,
    第二次到session缓存和sessionFactory缓存找对应的id
    如果存在直接返回实体属性,
    如果不存在,数据库查找对应实体主键的实体属性(支持session缓存,sessionFactory缓存)
    list():先到查询缓存查找当前sql对应的实体主键,
    如果存在到数据库查询实体主键对应实体属性,
    如果不存在到数据库查询sql且将实体主键存入查询缓存

    一级缓存:key:实体主键 value:实体属性 get,load
    二级缓存:key:实体主键 value:实体属性  load,iterate,list只存不取
    查询缓存:key:sql/hql value:实体主键对应的实体属性 list


    二 一级缓存
    生命周期 是session的生命周期 因此也叫session级缓存或事务级缓存



    a)支持一级缓存
    1. get()
    /**
    * 控制台输出:
    * Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
     
    * @return @void
    */
    @Test
    public void testGet()
    {
    session.beginTransaction();
    Category c1 = (Category) session.get(Category.class, 1);
    Category c2 = (Category) session.get(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
    session.getTransaction().commit();
    }

    2. load()
    /**
    * 控制台输出:
    * Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@98bbf6[id=1,name=c0]
     
    * @return @void
    */
    @Test
    public void testLoad()
    {
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class, 1);
    Category c2 = (Category) session.load(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
    session.getTransaction().commit();
    }



    3. iterate(Session缓存是实体级别的缓存,就是只有在查询对象级别的时候才使用,如果使用HQL和SQL是查询属性级别的,是不使用一级缓存的!切记!!!!)

    /**
    * Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.id=1
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.id=1
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category :com.liuhao.hibernate4.demo.query2.Category@1bbbafc[id=1,name=c0]
    Category :com.liuhao.hibernate4.demo.query2.Category@1bbbafc[id=1,name=c0]
    * @return @void
    */
    @Test
    public void testIterate()
    {
    session.beginTransaction();
    Iterator c1 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    Iterator c2 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c1.hasNext())
    {
    Category c = (Category) c1.next();
    System.out.println("Category :"+c);
    }
    while(c2.hasNext())
    {
    Category c = (Category) c2.next();
    System.out.println("Category :"+c);
    }
    session.getTransaction().commit();
    }



    b)管理一级缓存:
    ● evit(Object obj)将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象. 
    ● clear()将一级缓存中的所有持久化对象清除,释放其占用的内存资源 
    ● contains(Object obj)判断指定的对象是否存在于一级缓存中. 
    ● flush()刷新一级缓存区的内容,使数据库数据保持同步. 
    @Test
    public void testClear()
    {
    session.beginTransaction();
    Iterator c1 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    Iterator c2 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c1.hasNext())
    {
    Category c = (Category) c1.next();
    System.out.println("Category :"+c);
    }
    while(c2.hasNext())
    {
    Category c = (Category) c2.next();
    System.out.println("Category :"+c);
    }
    //清除session
    session.clear();
    Iterator c3 = (Iterator) session.createQuery("from Category where id = 1").iterate();
    while(c3.hasNext())
    {
    Category c = (Category) c3.next();
    System.out.println("Category :"+c);
    }
    session.getTransaction().commit();
    }
    @Test
    public void testEvict()
    {
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class, 1);
    Category c2 = (Category) session.load(Category.class, 1);
    System.out.println("Category :"+c1);
    System.out.println("Category :"+c2);
     
    //清除的是放入session的对象 不能是Query,Iterator
    session.evict(c1);
    session.evict(c2);
     
    Category c3 = (Category) session.load(Category.class, 1);
     
    System.out.println("Category3 :"+c3);
     
    session.getTransaction().commit();
    }

    c)避免大量实体数据导入内存溢出
    先flush,再clear

    d)如果数据量特别大时 考虑使用jdbc的操作存储过程
    Oracle数据库为例子.创建一个名为UserUpdate的存储过程...然后在程序中进行调用...

    UserUpdate的存储过程代码:
     
    create or replace procadure UserUpdate(u_age in number) as
    begin
    update users set age=age+1 where age>u_age;
    end;

      
      下面的是在程序中如何调用我们命名的存储过程 
                         
    Session session =SessionFactory.openSession();
    Transaction tx =session.beginTransaction();
    Connection conn=session.connection();
    String str="{call UserUpdate(?)}";
    CallableStatement cstmt= conn.prepareCall(str);
    cstmt.setInt(1,0);
    cstmt.executeUpdate();
    tx.commit();

    注意:开源的MySQL中不支持存储过程的..
    用JDBC API的好处是这样的..
    它不用把大批量的数据事先加载到内存中,然后再进行更新与修改..所以不会消耗大量内存....
    (小程序中是看不出什么差别的..当数据的记录达到一定的数据量的时候自然会发现用Hibernate API 与JDBC API的差别)


    三 二级缓存(实体类com.liuhao.hibernate4.demo.secondlevelcache
                            测试类com.liuhao.hibernate4.demo.secondleved.test)

    二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存)
    二级缓存可以被所有的session共享,
    二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
    二级缓存一般使用第三方的产品,如EHCache

    生命周期:
    二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存

    管理:
    ● 注意4.3.10以前版本
    ● evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源. 
    sessionFactory.evict(Customer.class, new Integer(1));

    evict(Class arg0)将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源 
    sessionFactory.evict(Customer.class);

    evictCollection(String arg0)将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源. 
    sessionFactory.evictCollection("Customer.orders");
     ● 4.3.10以后版本
    Cache cache = sfactory.getCache();
     
    //清除所有
    cache.evictAllRegions();
    //cache.evictEntityRegion(Category.class);//清除指定类
    //cache.evictEntity(Category.class,1);//清除指定类的指定数据
     
    /*清除集合
    cache.evictCollectionRegions();
    cache.evictCollectionRegion(arg0);
    cache.evictCollection(arg0, arg1);
    */


    一级缓存与二级缓存比较?
    一级缓存session管理(session消亡clear,evict,close,一级缓存就没有了)
    二级缓存SessionFactory管理(SessionFactory  close,evict,就没有了),SessionFactory管理session


    准备:
    1. jar包
    Hibernate版本4.3.10官网下载(注意版本!)
    ehcache.jar --> hibernate4.3.10的lib\optional\ehcache

    slf4j可以不用

    2. ehcahe.xml
    官网下载包中
    project\hibernate-ehcache\src\test\resources目录下有一个ehcache.xml
    将配置文件放置在src下

    ehcache.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
    <!--如果缓存中的对象存储超过指定的缓存数量的对象存储的磁盘地址-->
    <diskStore path="D:/ehcache"/>
    <!-- 默认cache:如果没有对应的特定区域的缓存,就使用默认缓存 -->
    <defaultCache maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600"
    overflowToDisk="false"/>
    <!-- 指定区域cache:通过name指定,name对应到Hibernate中的区域名即可-->
    <cache name="cn.javass.h3test.model.UserModel"
    eternal="false"
    maxElementsInMemory="100"
    timeToIdleSeconds="1200"
    timeToLiveSeconds="1200"
    overflowToDisk="false">
    </cache>
    </ehcache>


    配置文件解释:
    ehcache配置(ehcache.xml) 
    cache参数详解: 
    ● name:指定区域名 
    ● maxElementsInMemory :缓存在内存中的最大数目 
    ● maxElementsOnDisk:缓存在磁盘上的最大数目 
    ● eternal :设置是否永远不过期 
    ● overflowToDisk : 硬盘溢出数目 
    ● timeToIdleSeconds :对象处于空闲状态的最多秒数后销毁 
    ● timeToLiveSeconds :对象处于缓存状态的最多秒数后销毁 
    ● memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、LFU 

    关于缓存算法,常见有三种: 
    ● LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象 
    ● LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象 
    ● LFU:(First In First Out)把最早进入二级缓存的对象替换掉 

     
    常见的缓存提供商:

    Cache

    Provider class

    Type

    Cluster Safe

    Query Cache Supported

    Hashtable (not intended for production use)

    org.hibernate.cache.HashtableCacheProvider

    memory

     

    yes

    EHCache

    org.hibernate.cache.EhCacheProvider

    memory, disk

     

    yes

    OSCache

    org.hibernate.cache.OSCacheProvider

    memory, disk

     

    yes

    SwarmCache

    org.hibernate.cache.SwarmCacheProvider

    clustered (ip multicast)

    yes (clustered invalidation)

     

    JBoss TreeCache

    org.hibernate.cache.TreeCacheProvider

    clustered (ip multicast), transactional

    yes (replication)

    yes (clock sync req.)




    3. hibernate.cfg.xml配置
    <!-- 二级缓存默认打开,这里只是显示打开一下 -->
    <property name="cache.use_second_level_cache">true</property>
     
    <!-- 注意3和4者区别 -->
    <!-- hibernte 3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>-->
    <!-- hibernte 4.3.10 -->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>

    4. cache配置
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public class Category {
    private int id;
    private String name;
     
    @Id
    @GeneratedValue
    public int getId() {
    return id;
    }
    //set get...
    }

    设置二级缓存策略 
    ● READ_ONLY:实体只读缓存 
       只读缓存不允许更新,将报错Can't write to a readonly object。 
       允许新增,(从2.0以后新增直接添加到二级缓存) 
    //确保数据库中有标识符为1的FarmModel
    FarmModel farm = (FarmModel) session.get(FarmModel.class, 1);
    //如果修改将报错,只读缓存不允许修改
    //farm.setName("aaa");


    ● NONSTRICT_READ_WRITE:实体非严格读/写缓存 
       允许更新,更新后缓存失效,需再查询一次。 
       允许新增,新增记录自动加到二级缓存中。 
       整个过程不加锁。 
    ● READ_WRITE:实体读/写缓存 
       允许更新,更新后自动同步到缓存。 
       允许新增,新增记录后自动同步到缓存。 
       保证read committed隔离级别及可重复读隔离级别(通过时间戳实现) 
       整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的 
       事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。 
       读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁 
    ● TRANSACTIONAL:实体事务缓存 
       缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境 
    ● Collection集合缓存 
    @Cache(usage =CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public SortedSet<Ticket> getTickets(){
    return tickets;
    }

    和实体并发策略有相同含义; 
    但集合缓存只缓存集合元素的标识符,在二级缓存中只存放相应实体的标识符,然后再通过标识符去二级缓存查找相应的实体最后组合为集合返回 
    Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。 
    这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新 

    5. 测试
     ● 基本测试
     ● load
     
    @Test
    public void testload()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class,1);
    System.out.println("Category :"+c1);
    session.close();
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class,1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● iterate
     
    @Test
    public void testiterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    Iterator<?> iterator = q1.iterate();
    while(iterator.hasNext())
    {
    Category c = (Category) iterator.next();
    System.out.println("Category :"+c);
    }
    session.close();
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Query q2 = (Query) session2.createQuery("from Category where name='c0'");
    Iterator<?> iterator2 = q2.iterate();
    while(iterator2.hasNext())
    {
    Category c = (Category) iterator2.next();
    System.out.println("Category :"+c);
    }
    session2.close();
    sfactory.close();
    }

     ● iterate_list
     
    @Test
    public void testiterate_load() {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
     
    session.beginTransaction();
     
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    Iterator<?> iterator = q1.iterate();
    while (iterator.hasNext()) {
    Category c = (Category) iterator.next();
    System.out.println("Category :" + c);
    }
     
    session.close();
     
    // 第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
     
    Category c2 = (Category) session2.load(Category.class, 1);
     
    System.out.println("Category :" + c2);
     
    session2.close();
    sfactory.close();
    }

     ● list
     
    /**
    *
    * list 只存不取
    * @return @void
    */
    @SuppressWarnings("unchecked")
    @Test
    public void testlist_load() {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q1 = (Query) session.createQuery("from Category where name='c0'");
    for (Category c : (List<Category>)q1.list()) {
    System.out.println("Category :" + c);
    }
     
    session.close();
    // 第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class, 1);
     
    System.out.println("Category :" + c2);
     
    session2.close();
    sfactory.close();
    }


     ● 二级缓存管理
    注意4.3.10版本放弃了evict,改用getCache()统一管理
     
    @Test
    public void testEvict()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session = sfactory.openSession();
    session.beginTransaction();
    Category c1 = (Category) session.load(Category.class,1);
    System.out.println("Category :"+c1);
    session.close();
    //4.3中evict过时,使用getCache
    //sfactory.evict(Category.class);
    Cache cache = sfactory.getCache();
     
    //清除所有
    cache.evictAllRegions();
    //cache.evictEntityRegion(Category.class);//清除指定类
    //cache.evictEntity(Category.class,1);//清除指定类的指定数据
     
    /*清除集合
    cache.evictCollectionRegions();
    cache.evictCollectionRegion(arg0);
    cache.evictCollection(arg0, arg1);
    */
    //第二个session但是SessionFactory没有关闭
    Session session2 = sfactory.openSession();
    Category c2 = (Category) session2.load(Category.class,1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● 二级缓存的交互

    CacheMode参数用于控制具体的Session如何与二级缓存进行交互。

     CacheMode.NORMAL - 从二级缓存中读、写数据。

     CacheMode.IGNORE- session不会与二级缓存交互。

     CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。

     CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。

     CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过hibernate.cache.use_minimal_puts(是否优化二级缓存操作,最小化缓存写入操作(适用与集群缓存))的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

    如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。

    Map cacheEntries = sessionFactory.getStatistics()
    .getSecondLevelCacheStatistics(regionName)
    .getEntries();

    此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。

    hibernate.generate_statistics true
    hibernate.cache.use_structured_entries true

     ● GET
    /**
    * 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。
    * @return @void
    */
    @Test
    public void testMutual_GET()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.GET);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //不能取 所有两条sql
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
     
    session2.close();
    sfactory.close();
    }

     ● PUT
    /**
    * 仅向二级缓存写数据,但不从二级缓存中读数据。
    * @return @void
    */
    @Test
    public void testMutual_PUT()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.PUT);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //一条sql 二级缓存有 且能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● REFRESH
    /**
    * 仅向二级缓存写数据,但不从二级缓存中读数据。
    * 通过hibernate.cache.use_minimal_puts
    * (是否优化二级缓存操作,最小化缓存写入操作(适用与集群缓存))的设置,
    * 强制二级缓存从数据库中读取数据,刷新缓存内容。
    * @return @void
    */
    @Test
    public void testMutual_REFRESH()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
     
    session2.setCacheMode(CacheMode.REFRESH);
    //两条sql 二级缓存有 但不能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }

     ● IGONORE
    /**
    * session与二级缓存之间没有交互
    * @return @void
    */
    @Test
    public void testMutual_IGONORE()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    Session session1 = sfactory.openSession();
    session1.setCacheMode(CacheMode.IGNORE);
    Category c1 = (Category) session1.load(Category.class, 1);
    System.out.println("Category :"+c1);
    session1.close();
    System.out.println("-----------------------");
    long count = sfactory.getStatistics()
    .getSecondLevelCachePutCount();
    System.out.println("Cache : "+count);
    Session session2 = sfactory.openSession();
    //两条sql 二级缓存有 但不能取
    Category c2 = (Category) session2.load(Category.class, 1);
    System.out.println("Category :"+c2);
    session2.close();
    sfactory.close();
    }
    总结:
    load默认使用二级缓存,iterate默认使用二级缓存
    list默认向二级缓存中加数据,但是查询时候不使用


    四 查询缓存

    原理:查询缓存 key值是hql,value值是实体主键,主要作用是缓存普通属性查询。

    查询缓存的生命周期:当前关联的表发生修改,那么查询缓存生命周期结束

    查询缓存的配置:
    1. 查询缓存默认关闭,并且需要打开二级缓存(查看二级缓存的配置)
    <!-- 打开查询缓存 -->
    <property name="cache.use_query_cache">true</property>
    <!-- 二级缓存默认打开,这里只是显示打开一下 -->
    <property name="cache.use_second_level_cache">true</property>
     
    <!-- 注意3和4区别 -->
    <!-- hibernte 3
    <property name="cache.provider_class">
    org.hibernate.cache.EhCacheRegionFactory
    </property>
    -->
    <!-- hibernte 4.3.10 -->
    <property name="cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>

    2. 指定缓存指定类
    @Entity
    @Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public class Category


    3. 查询缓存的使用:
    Query q = session.createQuery("from Category where name = 'c0'");
    //手动打开查询缓存 
    q.setCacheable(true);

    查询缓存的方法:list,iterator;
    测试类:
    list,iterator方法
    public void testquerylist(SessionFactory sfactory)
    {
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q = session.createQuery("from Category where name = 'c0'");
    q.setCacheable(true);
    List<Category> list = (List<Category>)q.list();
    for(Category c : list)
    {
    System.out.println("Category : "+c);
    }
    session.getTransaction().commit();
    session.close();
    }
    public void testqueryiterator(SessionFactory sfactory)
    {
    Session session = sfactory.openSession();
    session.beginTransaction();
    Query q = session.createQuery("from Category where name = 'c0'");
    q.setCacheable(true);
    Iterator iterate = q.iterate();
    while(iterate.hasNext())
    {
    Category c = (Category) iterate.next();
    System.out.println("Category : "+c);
    }
    session.getTransaction().commit();
    session.close();
    }

    两次都执行list
    /**
    * 第一次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@17b2b2[id=1,name=c0]
    第二次list 查询
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@43da1b[id=1,name=c0]
     
    * 第一次会将发出sql,然后将满足条件的实体主键放入查询缓存中,
    * 查询缓存中key是hql,value是查询到的实体主键
    * 第二次发出sql时,list先到查询缓存中查询,获取满足条件的id然后到数据库获取实体属性,
    * 如果没有直接到数据库查询
    * @return @void
    */
    @Test
    public void testQuerylist_list()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    }

    两次都执行iterator
    /**
    * 第一次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@1c4a5ec[id=1,name=c0]
    第二次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@21447f[id=1,name=c0]
    iterator每次都是查询满足条件的实体主键,
    然后到数据库中获取对应主键的实体属性
    * @return @void
    */
    @Test
    public void testQueryiterate_iterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    }

    第一次list,第二次iterator
    /**
    * 第一次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@17b2b2[id=1,name=c0]
    第二次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@37165f[id=1,name=c0]
    list 将查询到的实体主键放入查询缓存中,
    literator对查询缓存的策略是不存,不取
    * @return @void
    */
    @Test
    public void testQuerylist_iterate()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次list 查询");
    testquerylist(sfactory);
    System.out.println("第二次iterator 查询");
    testqueryiterator(sfactory);
    }

    第一次iteraotr,第二次list
    /**
    * 第一次iterator 查询
    Hibernate:
    select
    category0_.id as col_0_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Hibernate:
    select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_
    from
    Category category0_
    where
    category0_.id=?
    Category : com.liuhao.hibernate4.demo.query2.Category@1c4a5ec[id=1,name=c0]
    第二次list 查询
    Hibernate:
    select
    category0_.id as id1_0_,
    category0_.name as name2_0_
    from
    Category category0_
    where
    category0_.name='c0'
    Category : com.liuhao.hibernate4.demo.query2.Category@1a1ff9[id=1,name=c0]
    * @return @void
    */
    @Test
    public void testQueryiterate_list()
    {
    SessionFactory sfactory = HibernateUtil.getSessionFactory();
    System.out.println("第一次iterator 查询");
    testqueryiterator(sfactory);
    System.out.println("第二次list 查询");
    testquerylist(sfactory);
    }

    总结:
     ● iterator:对查询缓存不操作,不存不取,每次查询满足sql的实体主键放入session(一级缓存中),然后到一级缓存中查询对应id的实体属性,没有就到数据库中查询。
     ● list:对查询缓存,即存也取,
    先到查询缓存获取满足sql条件的实体主键,
    如果有到满足sql条件的实体主键,到数据库查询实体主键对应的实体属性,
    如果没有到数据库查询满足sql条件的实体属性,且将实体主键放入查询缓存中



    原创粉丝点击