资讯专栏INFORMATION COLUMN

Hibernate 缓存机制

DevTTL / 478人阅读

摘要:通过查询实例调用上面的方法这里就算方法体里面执行三个查询操作,也只会执行一条查询,因为使用的同一个对象,这就有使用到的一级缓存。方法会使用二级缓存,而方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。

一、什么是缓存

缓存是内存中少部分数据的复制品,所以CPU到缓存中寻找数据时,也会出现找不到的情况(因为这些数据没有从内存复制到缓存中去),这时CPU还是会到内存中去找数据,这样系统的速率就慢下来了,不过CPU会把这些数据复制到缓存中去,以便下一次不要再到内存中去取。

二、Hibernate的一级缓存

(1)使用
Hibernate的一级缓存是默认开启的,当获取到一个Session对象,并执行save、update、saveOrUpdate、get方法时就会用到Hibernate一级缓存,当然也可以调用清除的放方法,Session为清除缓存提供了clear(清除所有的一级缓存)、evict(清除实例对象缓存)、refresh(重新查询数据并刷新缓存)。

例子:

//泛型查询实例
public E find(S s) {
    //使用Spring 获取一个Session对象
    Session session = getSession();
    //执行查询操作
    E bean = (E)session.get(entityClass, (Serializable) s);
    //清除实例对象缓存
    session.evict(bean);
    //返回查询的对象
    return bean;
}

(2)状态

Hibernate缓存状态分为瞬时状态、持久状态、脱管状态。

瞬时状态: 创建一个POJO,还未将对象数据保存到数据库时,Session中也没有当前POJO实例。

例子:

//创建一个POJO实例
Account account = new Account();
//给实例添加数据
account.setPhone("12345678931");
//这时实例并没有保存

持久状态: POJO对象被添加到Session缓存中,数据库也要有对应的数据。

例子:

public Integer saveStatus(Account entity) {
    //获取Session对象
    Session session = getSession();
    //执行保存方法
    Integer id = (Integer)session.save(entity);
    //修改被持久化的POJO对象
    entity.setState(4);
    //返回对象ID
    return id;
}

在上面的例子中,当保存事务还未提交,这时数据已经被持久化。这里会执行两条SQL,一条添加SQL,一条修改SQL(并没有调用修改方法为什么会执行修改SQL呢?因为Hibernate被持久化的POJO对象在被重新赋值时会触发更新操作。)

脱管状态: 在缓存中已经被持久化的POJO对象,接着POJO对象执行了evict方法,这时POJO对象会从缓存中托管,但数据库中是有对应的数据。

例子:

public Integer saveStatus(Account entity) {
    //获取Session对象
    Session session = getSession();
    //执行保存方法
    Integer id = (Integer)session.save(entity);
    //脱管当前POJO对象
    session.evict(entity);
    //修改被持久化的POJO对象
    entity.setState(4);
    //返回对象ID
    return id;
}

调用evict方法将POJO对象传入,清除实例的持久化,修改POJO实例将不会在触发更新操作。

(3)缓存绑定

Hibernate的一级缓存是绑定Session的,当获取到一个Session对象,在执行Sessinon里面的方法都能使用Hibernate默认提供的一级缓存,执行完成Session对象消亡即缓存数据也跟着消亡(一级缓存的数据是放在栈中)。

例子:

//通过id查询POJO实例
public E find(S s) {
    Session session = getSession();
    E bean = (E)session.get(entityClass, (Serializable) s);
    return bean;
}
//调用三次上面的方法
accountService.find(1);
accountService.find(1);
accountService.find(1);

这里会执行三条查询SQL,这是因为里面获取的Session对象是线程安全的,彼此并没有任何关联(这也是为什么Spring不能用到Hibernate的一级缓存,其实不是Spring的问题)。

//通过id查询POJO实例
public E find(S s) {
    Session session = getSession();
    E bean  = (E)session.get(entityClass, (Serializable) s);
    E bean1 = (E)session.get(entityClass, (Serializable) s);
    E bean2 = (E)session.get(entityClass, (Serializable) s);
    return bean;
}
//调用上面的方法
accountService.find(1);

这里就算方法体里面执行三个查询操作,也只会执行一条查询SQL,因为使用的同一个Session对象,这就有使用到Hibernate的一级缓存。

二、Hibernate的二级缓存
(1)使用

Hibernate的二级缓存是默认关闭的(二级缓存的数据是放在堆中),如果需要开启二级缓存则需要额外的配置。

(2)配置

第一步:POJO对象设置

@Entity//POJO注解
@Table(name = "account")//对应的数据库表名称
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)//二级缓存配置,读写模式
public class Account {
    ...
}

第二步:配置Hibernate session工厂


    
    
        
            update
            org.hibernate.dialect.MySQLDialect
            true
            true
            true
            true
            org.hibernate.cache.ehcache.EhCacheRegionFactory
            classpath:applicationContext-ehcache.xml
        
    
    
        
            com.test.entity
        
    

第三步:创建二级缓存配置文件



    
    //maxElementsInMemory--缓存对象的最大数目
    //eternal--对象是否永不过期,设置为true,过期时间则无效
    //timeToldleSeconds--对象空闲多长时间未被使用就失效
    //timeToLiveSeconds--对象被缓存的时间
    //overflowToDisk--内存溢出时是否刷盘,如果为true则需要配置一个刷盘路径
    //diskExpiryThreadIntervalSeconds--磁盘失效线程运行时间间隔

三、选择正确的方法

(1) Hibernatne查询分为两类:一类是得到单个对象,另一类是得到结果集。

单个对象:

get()方法和load()方法的区别在于对二级缓存的使用上。load()方法会使用二级缓存,而get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。在使用中,对使用了二级缓存的对象进行查询时最好使用load()方法,以充分利用二级缓存来提高检索的效率。

结果集对象:

list方法介绍

list()方法在执行时,是直接运行查询结果所需要的查询语句,而iterator()方法则是先执行得到对象ID的查询,然后再根据每个ID值去取得所要查询的对象。因此,对于list()方式的查询通常只会执行一个SQL语句,而对于iterator()方法的查询则可能需要执行N+1条SQL语句(N为结果集中的记录数)。

list()方法只能使用二级缓存中的查询缓存,而无法使用二级缓存对单个对象的缓存(但是会把查询出的对象放入二级缓存中)。所以,除非重复执行相同的查询操作,否则无法利用缓存的机制来提高查询的效率。

list()方法会一次获得所有的结果集对象,而且它会依据查询的结果初始化所有的结果集对象。这在结果集非常大的时候必然会占据非常多的内存,甚至会造成内存溢出情况的发生。

iterator方法介绍

iterator()方法只是可能执行N+1条数据,具体执行SQL语句的数量取决于缓存的情况以及对结果集的访问情况。

iterator()方法则可以充分利用二级缓存,在根据ID检索对象的时候会首先到缓存中查找,只有在找不到的情况下才会执行相应的查询语句。所以,缓存中对象的存在与否会影响到SQL语句的执行数量。

iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。因此在访问中可以控制缓存中对象的数量,以避免占用过多缓存,导致内存溢出情况的发生。使用iterator()方法的另外一个好处是,如果只需要结果集中的部分记录,那么没有被用到的结果对象根本不会被初始化。所以,对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素。

部分信息来自网络,欢迎大家指出错误。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/62023.html

相关文章

  • Hibernate 缓存机制

    摘要:通过查询实例调用上面的方法这里就算方法体里面执行三个查询操作,也只会执行一条查询,因为使用的同一个对象,这就有使用到的一级缓存。方法会使用二级缓存,而方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。 一、什么是缓存 缓存是内存中少部分数据的复制品,所以CPU到缓存中寻找数据时,也会出现找不到的情况(因为这些数据没有从内存复制到缓存中去),这时CPU还是会到内存中去...

    rickchen 评论0 收藏0
  • Hibernate最全面试题

    摘要:中怎样实现类之间的关系如一对多多对多的关系中怎样实现类之间的关系如一对多多对多的关系它们通过配置文件中的来实现类之间的关联关系的。 Hibernate常见面试题 Hibernate工作原理及为什么要用? Hibernate工作原理及为什么要用? 读取并解析配置文件 读取并解析映射信息,创建SessionFactory 打开Sesssion 创建事务Transation 持久化操作 提...

    张利勇 评论0 收藏0
  • Hibernate与mybatis比较

    摘要:一级缓存又叫的缓存,是事物范围的缓存,默认开启二级缓存又叫的缓存,默认关闭。二级缓存存放数据一般是不经常修改的数据,不会被并发访问的数据,常量数据访问数据顺序是一级缓存二级缓存数据库。 Hibernate与mybatis比较 1、先说底层:   a)Jdbc:全称java数据库连接,是java语言用来规范客户端如何访问数据库的程序接口。   b) 一般步骤: i.加载驱动程序 ...

    wqj97 评论0 收藏0
  • 初识Hibernate

    摘要:使用反射机制,而不是字节码增强程序来实现透明性。工具类初始化失败为空,请检查配置文件瞬时对象与持久化对象测试代码当前在数据库中没有记录进行关联,所以此时是瞬时对象。将持久化当前在数据库有唯一一条记录对应,所以此时是持久化对象。 showImg(https://segmentfault.com/img/bVbo4at?w=2313&h=642); 一、什么是Hibernate? Hibe...

    tomato 评论0 收藏0
  • 近期Java高级工程师面试总结

    摘要:面试总结最近两周面试了几家公司高级工程师的职位,主要有宜信网信金融阿里高德口袋购物。目前有部分公司已经面试通过,两家在等消息。今天趁热把常见面试内容总结一下。可以用来完成统一命名服务状态同步服务集群管理分布式应用配置项等管理工作。 面试总结 最近两周面试了几家公司Java高级工程师的职位,主要有宜信、网信金融、阿里高德、口袋购物。目前有部分公司已经面试通过,两家在等消息。今天趁热把常见...

    raoyi 评论0 收藏0

发表评论

0条评论

DevTTL

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<