您的位置:首页 > 其它

关于hibernate一级,二级缓存以及事务隔离机制。

2014-07-21 00:22 351 查看
1、Hibernate的一级缓存

在Hibernate框架内部应用缓存提高应用系统的运行性能,Hibernate中的缓存分为为一级和二级缓存。

Hibernate的一级缓存是内置缓存,不能通过程序代码或者配置人为取消取消,并且一级缓存通过Session对象实现缓存,所以也被为“Session”缓存。一级缓存是事务级别的缓存,事务结束缓存中的所有数据失效。使用一级缓存可以在一个事务中减少查询和更新数据表的操作。如在一个事务中即使多次地修改一个持久对象,但在提交事务之时,Hibernate只会执行一个update子句更新数据库的数据,更新的数据包括事务提交之前所有的修改内容。

2、一级缓存的实现原理

在底层实现上,可以把一级缓存想象为Session对象维护一个Map对象,其中保存当前Session对象使用的数据。通过Session对象读取数据时,该对象会首先依据要加载对象的类型和标识符属性查找这个Map对象中是否已经加载过这个持久化对象。如果找到,则直接返回这个对象;否则从数据库中查询,加载数据后同时会把这个持久化对象保存在Map对象中。

3、管理一级缓存

管理一级缓存包括以下几种方法:

(1)、使用evict()方法从缓存中移除对象

不能取消一级缓存的使用,但是可以使用Session的evict()方法从一级缓存中移除缓存的持久对象,同时该对象的状态会从持久化状态转换为托管状态。如:

public void testRemoveL1Cache(){

Session session = HibernateUtils.getCurrentSession();

Transaction tx = session.beginTransaction();

Product p = (Product)session.get(Product.class, 1);

//移除一级缓存中的Product对象

session.evict(p);

Product p2 = (Product)session.get(Product.class, 1);

System.out.println(p==p2);

tx.commit();

session.close();

}

程序的执行结果如下:

Hibernate:select product0_.id as id3_0_, product0_.name as name3_0_,product0_.price as price3_0_, product0_.description as descript4_3_0_from tb_product product0_ where product0_.id=?

Hibernate: selectproduct0_.id as id3_0_, product0_.name as name3_0_, product0_.price asprice3_0_, product0_.description as descript4_3_0_ from tb_productproduct0_ where product0_.id=?

false

说明一级缓存中的对象被移除后,Hibernate只能从数据库中加载,同时加载后的对象与原来的对不再是同一个对象。

(2)、使用clear()方法从缓存中清除所有对象。

用法如下:

public void testRemoveAllL1Cache(){

Session session = HibernateUtils.getCurrentSession();

Transaction tx = session.beginTransaction();

Product p1 = (Product)session.get(Product.class, 1);

Product p2 = (Product)session.get(Product.class, 2);

//清除所有对象

session.clear();

Product p3 = (Product)session.get(Product.class, 1);

Product p4 = (Product)session.get(Product.class, 2);

System.out.println(p1==p3);

System.out.println(p2==p4);

tx.commit();

session.close();

}

程序执行结果如下:

Hibernate:select product0_.id as id3_0_, product0_.name as name3_0_,product0_.price as price3_0_, product0_.description as descript4_3_0_from tb_product product0_ where product0_.id=?

Hibernate: selectproduct0_.id as id3_0_, product0_.name as name3_0_, product0_.price asprice3_0_, product0_.description as descript4_3_0_ from tb_productproduct0_ where product0_.id=?

Hibernate: select product0_.id asid3_0_, product0_.name as name3_0_, product0_.price as price3_0_,product0_.description as descript4_3_0_ from tb_product product0_ whereproduct0_.id=?

Hibernate: select product0_.id as id3_0_,product0_.name as name3_0_, product0_.price as price3_0_,product0_.description as descript4_3_0_ from tb_product product0_ whereproduct0_.id=?

false

false

(3)、使用Session对象的contains()、flush()和setReadOly()方法

Session对象的contains()方法判断指定的对象是否缓存在一级缓存中,flush()方法刷新(同步)一级缓存中的数据,使其与数据库中的数据保存一致,setReadOnly()方法设置某个持久化对象为只读对象,而只读对象不执行脏数据检查。

Hiberante3 一级缓存总结
1. Session 级别的缓存,它同 session邦定。它的生命周期和 session相同。 Session消毁,它也同时消毁;管理一级缓存,一级缓存无法取消,用两个方法管理, clear(),evict()

2. 两个 session 不能共享一级缓存,因它会伴随 session的生命周期的创建和消毁;

3. Session缓存是实体级别的缓存,就是只有在查询对象级别的时候才使用,如果

使用 HQL和 SQL是查询属性级别的,是不使用一级缓存的!切记!!!!

4 . iterate 查询使用缓存,会发出查询 Id的 SQL和 HQL语句,但不会发出查实体的,

它查询完会把相应的实体放到缓存里边,一些实体查询如果缓存里边有,就从缓存中查询,但还是会发出查询 id的 SQL和 HQL语句。如果缓存中没有它会数据库中查询,然后将查询到的实体一个一个放到缓存中去,所以会有 N+1问题出现。

5 . List() 和 iterate 查询区别:

使用iterate,list查询实体对象*N+1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
所谓的N+1是在查询的时候发出了N+1条sql语句1:首先发出一条查询对象id列表的sqlN:
根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句list和iterate的区别?
list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题

6.Get()和load(),iterate方法都会使用一级缓存.

7.hiberate3 session 存储过程如下:

例如 object 对象

Session.save(object);

这时候不会把数据放到数据库,会先放到session缓存中去,数据库中没有相应记录,session.flush();才发SQL和HQL语句,数据库中有了相应记录,

但是数据库用select 查不到,这是跟数据库事物级别有关系 .

Session.beginTrransaction().commit();

事物提交后
可以查询到了。

Session.flush()语句但是为什么不写呢,因为commit()会默认调用flush();

Hiberante3 二级缓存总结
1. Hibernate3的( sessionFactory)二级缓存和 session级别的缓存一样都只对实体对象做缓存,不对属性级别的查询做缓存;二级缓存的生命周期和 sessionFactory的生命周期是一样的, sessionFactory可以管理二级缓存;

2. sessionFactory级别的缓存,需要手动配置;所有的 session可以共享 sessionFactory 级别的缓存;(一般把一些不经常变化的实体对象放到 sessionFactory级别的缓存中,适合放不经常变化的实体对象。)

3. Hiberante3二级缓存的配置和使用方法如下:

1. 必须把 ehcache.jar包导入,然后到 Hibernate3.2的 etc文件下把 ehcache.xml复制到工程 src目录下( ehcache.xml里边的参数里边有详细英文说明);

(说明: ehcache.jar是第三方法的缓存产品, hiberante只是把它做了集成,还有好多第三方 hibernate集成的缓存产品,相关说明请查阅 hiberante3开发手册; ehcache是不支持分布应用的,如果有分布式需求,请换成支持分布式的二级缓存产品, hiberate3开发手册都有相头说明。配置方法都类似);

4. Hibernate3的二级缓存默认是开起的,也可以指定开起。在 hibernate.cfg.xml 文件下配置如下:

*修改 hibernate.cfg.xml文件,开户二级缓存;

<property name=”hibernate.cache.use_second_level_cache”>true</property>

*指定二级缓存产品的提供商;

<property name=”hibernate.cache.provider_class”>
org.hibernate.cache.EhCacheProvider


</property>

要让那些实体使用二级缓存,在 hibernate.cfg.xml配置文件中加入:

<!—

让这个实体用二级缓存 也可以在实体中映射文件去配置即:

<cache usage="read-only"/>

-->

<class-cache class=”com.zzz.hibernate.ClassT” usage=”read-only”/>

Read-only一般使用这个策略,其它的 hibernate3开发手册中也有详细介绍;

CacheMode去 hibernate3开发手册中搜索这个关键字,可以找到一级缓存和二级缓存交互使用的问题;

@Entity
@Table(name = "T_JOB_LIST")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@SequenceGenerator(name = "SEQ_STORE", sequenceName = "SEQ_T_JOB_LIST", allocationSize = 1)
public class JobList extends BaseEntity {
@OneToMany(mappedBy = "jobList", cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@OrderBy(value = "id DESC")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private List<JobJdu> jduList = new ArrayList<JobJdu>();// 进度
}

@Entity
@Table(name = "T_JOB_JDU")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Indexed(index = "JOBJDU")
@SequenceGenerator(name = "SEQ_STORE", sequenceName = "SEQ_T_JOB_JDU", allocationSize = 1)
public class JobJdu extends BaseEntity {
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "joblist_id")
private JobList jobList;// 工作计划明细
}
http://www.360doc.com/content/10/1201/15/4875542_74048081.shtml
Hibernate脏数据检查

何谓脏数据(Dirty Data)?

这里的"脏"可能有些误解,脏数据并不是说非废弃或者无用的数据,而是指一个数据对象所携带的信息发生了改变之后的状态. 事务提交时,HIbernate会对Session中的PO进行检测,判断那些发生了变化,并装发生变化的数据更新到数据库中.

Hibernate脏数据的检查一般策略大致有下面两种:

数据对象监控

数据对象监控的实现方式,大体上是通过拦截器对数据对象的设值方法(setter)进行拦截,拦截器的实现可以借助DynamicProxy或者CGlib实现.一旦数据对象的设置方法被调用(通常这也意味着数据对象的内容发生变化),则将其标志为"待更新"状态,之后在数据库操作时将其更新到对应的库表.

数据版本比较

在持久层框架中维持数据对象的最近读取版本,当数据提交时数据与些版本进行比对 时,如果发生变化则将期同步到数据库相应的库Hibernate采取的是第二种检查策略

Hibernate数据缓存

Hibernate数据缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库与数据访问层之间,缓存(Cache)往往是提升系统性能的关键因素.

数据缓存策略

ORM数据缓存应包含以下几个层次的内容:

◆事务级缓存(Transaction Layer Cache)

◆应用级/进程级缓存(Application/Process Layer Cache)

◆分布式缓存(Cluster Layer Cache)

事务级缓存

这里的事务可能是一个数据库事务,也可能是某个应用级事务.对于Hibernate而言,事务级缓存是基于Session生命周期实现的.每个Session会在内部维持一个数据缓存,此缓存随着Session的创建(销毁)而存在(消亡),因此也叫Session LevelCache(也称内部缓存)

应用级缓存

此缓存可由多个事务(数据事务或者应用级事务)共享,事务之间的缓存共享策略与应用的事务隔离机制密切相关.在Hibernate中,应用级缓存在SessionFactory层实现,所有由此SessionFactory创建的Session实例共享此缓存,因此也称为SessionFactory Level Cache.

分布式缓存

分布式缓存由多个应用级缓存实例组成集群,通过某种远程机制(如RMI或JMS)实现各个缓存实例间的数据同步,任何一个实例的数据修改操作,将导致整个集群间的数据状态同步.

需要注意的是,如果当前的应用与其他应用共享数据库,也就是说,在当前应用过程中,其他应用可能同时更新数据库,那么缓存策略的制定就需要格外小心了,这种情况下,一般采取一些保守策略可能会更加稳妥.

Hibernate数据缓存

以Hibernate主义加以区分,Hibernate数据缓存可分为以下两个层次:

◆内部缓存(Session Level,也称一级缓存)

◆二级缓存(SessionFactory Level,也称为二级缓存)

Hibernate中,缓存将在以下情况中发挥作用:

通过主键id加载数据.这包括根据id查询数据的Session.load方法,以及Session.iterate等 批量查询方法(Session.iterate进行查询时,也是根据id在缓存 中查找数据,类似一个Session.load循环).

延迟加载

Session在内部维护了一个Map数据类型,些数据类型中保持了所有与当前Session相关联对象. 内部缓存正常情况下由Hibernate自动维护,如果需要手动干预,我们可以通过以下方法完成:

◆Session.evict,将某个特定的对象从内部缓存中清除

◆Session.clear,清空内部缓存

二级缓存

在引入二级缓存时,应该考虑以下问题:

◆数据库是否与其他应用共享

◆应用是否需要部署在集群环境中.

如果满足以下条件,将纳入缓存管理:

◆数据不会被第三方应用修改

◆数据大小(Data Size)在可接受的范围之内.

◆数据更新频率较低

◆同一数据可能会被系统频繁引用.

◆非关键数据(关键数据,如金融帐户数据)。

第三方缓存实现

Hibernate本身并未提供二级缓存的产品化实现,而是为众多的第三方缓存组件提供了接入接口,较常用的第三方组件有:

1. JCS2. EHCache3. OSCache4. JBossCache5.SwarmCacheHibernate中启用二级缓存,需要在hibernate.cfg.xml配置hibernate.cache.provider_class参数,之后,需要在映射文件中指定各个映射实体(以及collection)的缓存同步策略。Hibernate提供了一下4种内置的缓存同步策略:1. read-only只读。对于不会发生改变的数据,可使用只读型缓存。

2. nonstrict-read-write如果程序对并发访问下的数据同步要求不是非常严格,且数据更新操作频率较低,可以采用本选项,获得较好的性能。

3. read-write严格可读写缓存。基于时间戳判定机制,实现了“read committed”事务隔离等级。可用于对数据同步要求严格的情况,但不支持分布式缓存。这也是实际应用中使用最多的同步策略。

1.脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2.不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

3. 幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

补充:基于元数据的Spring声明性事务:

Isolation属性一共支持五种事务设置,具体介绍如下:

0 DEFAULT 使用数据库设置的隔离级别(默认),由DBA默认的设置来决定隔离级别.

l READ_UNCOMMITTED 会出现脏读、不可重复读、幻读(隔离级别最低,并发性能高)

2 READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行)

4 REPEATABLE_READ 会出幻读(锁定所读取的所有行)

8 SERIALIZABLE 保证所有的情况不会发生(锁表)

不可重复读的重点是修改:

同样的条件, 你读取过的数据, 再次读取出来发现值不一样了

幻读的重点在于新增或者删除

同样的条件, 第1次和第2次读出来的记录数不一样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: