Hibernate学习笔记之缓存

一、持久化缓存的范围

缓存的范围决定了缓存的生命周期以及可以被谁访问。
事务级缓存:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。每个事务都有独自的缓存。
应用级缓存:缓存被应用内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命周期。
分布式缓存:在分布式环境中,缓存被一个机器或者多个机器的应用共享。缓存中的数据被复制到分布式环境中的每个应用节点,应用通过远程通信来保证缓存中的数据的一致性。对大多数应用来说,应该慎重地考虑是否需要使用分布式范围的缓存,因为访问的速度不一定会比直接访问数据库数据的速度快多少。

 

二、Hibernate数据缓存(Cache)分为两个层次:
1、Session Level缓存
Session的缓存是内置的,不能被卸载,也被称为Hibernate的一级缓存。
2、SessionFactory Level 缓存
SessionFactory的缓存是应用级缓存,包含二个部分:内置缓存和外置缓存。SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句。SessionFactory的外置缓存是一个可配置的插件,通常说的Hibernate二级缓存就是SessionFactory外置缓存,在默认情况下,Hibernate并没有打开二级缓存。

 

三、Hibernate中,缓存在以下情况下发挥作用:
1、通过id(主键)加载数据时
包括根据id查询数据的Session.load/get方法,以及Query/Criteria的iterate方法 。
2、延迟加载
通过延迟加载读取的数据对象可以通过id从缓存读取。

 

四、一级缓存
Hibernate的Session内部缓存也称为一级缓存,属于事务级缓存。Session在内部维护了一个Map数据类型,其中保持了所有与当前Session相关联的数据对象。

若需要通过Sessoin加载某个数据对象,Session首先会根据所要加载的数据类型和id,在一级缓存中寻找是否已有些数据的缓存实例,如果存在且状态有效,则以此数据实例作为返回结果。
当Session从数据库加载数据时,也会将其以对象的形式存放到一级缓存中加以管理的。
一级缓存是Session的私有数据,伴随Session实例的创建而创建,消亡而消亡。一级缓存不能在Session间共享。
一级缓存正常情况下是由Hibernate自动维护的,但也可通过Session.evict方法、Session.clear方法进行干预,evict方法将某个特定对象从一级缓存中清除。clear方法则清空整个一级缓存。

 

五、二级缓存
在Hibernate中的二级缓存可以是应用级缓存,也可是分布式缓存。
二级缓存是SessionFactory级别的全局缓存。Session在进行数据查询时,如在自身的一级缓存中查询未果,则将在二级缓存中查找,若命中,则以命中的数据对象作为结果返回。

 

六、二级缓存策略
缓存策略决定了数据对象在缓存中的存取规则。
要使用Hibernate的二级缓存,需为每个实体类指定相应的缓存策略。
Hibernate提供了四种内置的缓存策略:
read-only:只读,如果修改会抛出异常。对于从来不会修改的数据,如参考数据,可以使用只读型缓存。
nonstrict-read-write:不严格的读/写缓存。不保证缓存与数据库中数据的一致性。若程序对并发访问下的数据同步要求不是很严格,且数据更新操作频率较低,可采用此策略缓存。性能比读写缓存好。
read-write:严格读写缓存。用于对数据同步要求严格的情况,对于经常被读、较少修改的数据,可以采用此策略缓存。不支持分布式缓存。实际应用最广泛的缓存策略。
读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,就直接查询取数据库。如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到超时。不严格读写缓存不锁定缓存中的数据。

transactional:事务型缓存。缓存支持事务,发生异常的时候,缓存也能够回滚。必须运行在JTA事务环境中。适用于对关键数据的缓存。

 

ps:使用Annotation @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)写了一个小例子,但是修改缓存中的数据对象后其他对象并没有去数据库取,而是取到了修改后的对象,在此标注一下后续研究。

 

@Entity @Table(name="cui_user") @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) public class TUser { private Integer id; private String name; private List<TAddress> addresses=new ArrayList<TAddress>();//注意一定要实例化 @Id //标识主键 @GeneratedValue(strategy=GenerationType.AUTO)//指定主键值的产生策略由Hibernate根据数据库字段选择 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="user")//FetchType.LAZY延迟加载,CascadeType.ALL所有操作都同步 @OrderBy(value="id asc")//所去地址列表按id升序排列 @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) public List<TAddress> getAddresses() { return addresses; } public void setAddresses(List<TAddress> addresses) { this.addresses = addresses; } } //测试方法 public void test() { //通过Id缓存单个对象,关闭查询缓存 TUser user=(TUser)session.load(TUser.class, 200); System.out.println("1---"+user.getName()); session.close(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Session session2=HibernateSessionFactory.getSession(); Transaction tx=session2.beginTransaction(); TUser user2=(TUser)session2.load(TUser.class, 200); user2.setName("cuisea"); session2.saveOrUpdate(user2);//更新对象,应该在此加锁 System.out.println("2---"+user2.getName()); Session session3=HibernateSessionFactory.getSession(); TUser user3=(TUser)session3.load(TUser.class, 200);//session2应该将对象锁定,所以这里应该去数据库取数据 System.out.println("3---"+user3.getName()); tx.commit(); }

Hibernate生成sql语句:

Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_ from cui_user tuser0_ where tuser0_.id=?
1---test         //将对象加载到缓存
2---cuisea     //从缓存中读取数据,并修改name
3---cuisea    //奇怪这里也是从缓存取数,注HibernateSessionFactory.getSession()方法取当前线程对应的Session,这样session3和session2是同一个session才导致这样的问题--2011-12-13
Hibernate: update cui_user set name=? where id=?

你可能感兴趣的:(数据库,Hibernate,session,cache,user,Integer)