`
suhuanzheng7784877
  • 浏览: 704236 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Ff8d036b-05a9-33b5-828a-2633bb68b7e6
读金庸故事,品程序人生
浏览量:47744
社区版块
存档分类
最新评论

用xmemcache作为JPA(Hibernate实现)二级缓存

 
阅读更多

1.  持久层的缓存

Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate自身进行管理的,一般情况下无需进行干预,默认一级缓存也是打开的;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存比较重量级,可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。

每个事务都有单独的第一级缓存进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享并发访问策略。由于每个事务都拥有单独的第一级缓存,因此一级缓存不会出现并发问题,无需提供并发访问策略,由于多个事务会同时访问第二级缓存中相同数据,因此二级缓存必须提供适当的并发访问策略,来保证特定的事务隔离级别数据过期策略没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间物理存储介质内存内存和硬盘。

对象的散装数据首先存放在基于内存的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。缓存的软件实现是在HibernateSession的实现中包含了缓存的实现由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider),用于把特定的缓存插件集成到Hibernate中。启用缓存的方式只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行操作。用户可以在单个类或类集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,例如常量数据集合,就可以考虑使用第二级缓存。

只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。用户管理缓存的方式第一级缓存的物理介质为内存,内存虽然存取速度比较快,奈何由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。

对应于一级缓存,当应用程序调用Sessionsave()update()savaeOrUpdate()get()load(),以及调用查询接口的list()iterate()filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。Session为应用程序提供了两个管理缓存的方法:evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。

Hibernate二级缓存策略的一般过程如下:
1)
条件查询的时候,总是发出一条select语句(选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。

2) 把获得的所有数据对象根据ID放入到第二级缓存中。

3) Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

4) 删除、更新、增加数据的时候,同时更新缓存。

Hibernate二级缓存策略,是针对于ID(主键)查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache,就是之前说的查询缓存。更新缓存的代价比从缓存中查询的代价大得多,因为缓存底层是个大Map或者是个大Item(org.hibernate.cache.ReadWriteCache.Item)进行存储的。而且存储的一般都是对象,空间移动,存取,修改的代价可想而知。那么什么样的数据适合存放到第二级缓存中?

1) 很少被修改的数据

2) 不是很重要的数据,允许出现偶尔并发的数据

3) 不会被高并发访问的数据

4) 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

那么什么样的数据不适合存放到第二级缓存中?

1) 经常被修改的数据,代价太大了,得不偿失。

2) 金钱敏感数据,绝对不允许出现并发

3) 与其他应用共享的数据。

Hibernate的二级缓存往往要借助第三方的工具,下面是几种常用的缓存工具:

1):EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

2):OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。

3):SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

4):JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。

5):当然了还有我们这次要介绍的Memcacahe

Memcacahe这个开源项目,是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的Hash表,能够用来存储各种格式的数据。其实Memcache最开始是作为高可用集群的缓存解决方案的。一些互联网的非敏感信息就放到Memcache中,用户第一次访问还是从数据库里面进行查询,之后放到缓存中,之后用户的访问都从缓存中去取。至于缓存更新,根据业务不同,有不同的方案,比如一旦数据发生变更,就是先更新数据库后,立即更新缓存数据,还有一种做法就是对于十分不敏感的数据仅仅更新缓存,定时向数据库进行同步和更新。它还可以作为高分布式系统的Session的解决方案。Memcache还可以作为Hibernate的二级缓存插件。hibernate-memcached这个java类库用于在Hibernate中使用Memcached作为一个二级分布式缓存。支持实体和查询缓存。

2.  搭建环境

hibernate-memcachedGoogle的托管项目,开发源代码。

http://code.google.com/p/hibernate-memcached/可以下载

 

Memcache自身需要一个服务端,windows版本,笔者找了好久,终于在http://code.jellycan.com/memcached/找到,读者可自己下载。下载后是一个exe文件。

要想在我们自己的项目中使用好Memcache,还得使用一个Memcacahe客户端程序。笔者极力推崇dennis兄的Xmemcached作为客户端。可以从

http://code.google.com/p/xmemcached/下载。当然memcache还需要依赖slf4j-api包。

之后就是JPA规范与Hibernate相关包的导入了,当然可以借助IDE了,笔者前面也有相关笔记写到,在此就不再赘述了。搭建好环境后的工作空间如下

 3.  编写业务代码

首先先配置JPA的属性

	<persistence-unit name="HibernateSecondCachePU"
		transaction-type="RESOURCE_LOCAL">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<class>pojo.Productsmessageinfo</class>
		<class>pojo.Products</class>
		<properties>
			<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
			<property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/tgweb" />
			<property name="hibernate.connection.username" value="liuyan" />
			<property name="hibernate.connection.password" value="111111" />
			<property name="hibernate.hbm2ddl.auto" value="update" />
			<property name="hibernate.cache.use_second_level_cache"
				value="true" />
			<property name="hibernate.show_sql" value="true" />
			<!-- 结构化方式存储 -->
			<property name="hibernate.cache.use_structured_entries"
				value="true" />
			<!-- 查询缓存 -->
			<property name="hibernate.cache.use_query_cache" value="true" />
			<!-- 二级缓存服务类 -->
			<property name="hibernate.cache.provider_class"
				value="com.googlecode.hibernate.memcached.MemcachedCacheProvider" />
			<!-- 二级缓存服务地址和端口 -->
			<property name="hibernate.memcached.servers" value="127.0.0.1:11211" />
			<!-- memcache的调用客户端 -->
			<property name="hibernate.memcached.memcacheClientFactory"
				value="net.rubyeye.xmemcached.utils.hibernate.XmemcachedClientFactory" />
		</properties>
	</persistence-unit>

当着明人不说暗话,其实这段配置中,其他配置都好说,就是hibernate.cache.use_structured_entries这个配置,笔者查了半天,不是很明白。文档说是结构化存储,没说怎么个结构化存储~网上都说是人性化存储,笔者的疑问是,什么是人性化存储?怎么样进行人性化存储?不是特别明白,这个大家可以讨论。

下面咱们来看实体POJO

package pojo;

import static javax.persistence.GenerationType.IDENTITY;

import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "products", catalog = "tgweb")
@org.hibernate.annotations.Cache(usage =CacheConcurrencyStrategy.READ_WRITE)
public class Products implements java.io.Serializable {

	// Fields

	private Long id;
	private Productsmessageinfo productsmessageinfo;
	private Double nowPrice;
	private String picture;
	private String productMess;
	private String productName;
	private Double sourcePrice;
	private Timestamp updateTime;
	private String productsMessageInfo;

	// Constructors

	/** default constructor */
	public Products() {
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "id", unique = true, nullable = false)
	public Long getId() {
		return this.id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "productsMessageInfo_ID")
	public Productsmessageinfo getProductsmessageinfo() {
		return this.productsmessageinfo;
	}
    …………………………省去setter/getter

}

 其中类上的标注,详细解释请看http://www.iteye.com/problems/49111它是二级缓存的读取策略。针对不同的实体需求,配置不同的访问策略。请注意注解的全路径。

DAO代码就不全给出了,比较简单,仅仅给出片段即可

	public static EntityManager getEntityManager() {
		EntityManager manager = threadLocal.get();		
		if (manager == null || !manager.isOpen()) {
			manager = emf.createEntityManager();
			threadLocal.set(manager);
		}
		return manager;
	}

	public Products findById(Long id) {
		try {
			Products instance = getEntityManager().find(Products.class, id);
			return instance;
		} catch (RuntimeException re) {
			throw re;
		}
	}

 4.  测试

public void test02() {

		Products products1 = productsDAO.findById(1l);
		System.out
				.println(products1.getId() + ":" + products1.getProductName());
		Products products2 = productsDAO.findById(1l);
		System.out
				.println(products2.getId() + ":" + products2.getProductName());

	}

 我们先将Hibernate的二级缓存在配置文件中关掉。就是设为false运行测试代码试试。

运行后控制台效果如下

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
2011-6-29 16:19:37 dao.EntityManagerHelper log
信息: finding Products instance with id: 1
Hibernate: select products0_.id as id1_0_, products0_.nowPrice as nowPrice1_0_, products0_.picture as picture1_0_, products0_.productMess as productM4_1_0_, products0_.productName as productN5_1_0_, products0_.productsMessageInfo as products6_1_0_, products0_.productsMessageInfo_ID as products9_1_0_, products0_.sourcePrice as sourcePr7_1_0_, products0_.updateTime as updateTime1_0_ from tgweb.products products0_ where products0_.id=?
2011-6-29 16:19:37 dao.EntityManagerHelper log
信息: finding Products instance with id: 1
1:商品
1:商品

 从输出的sql语句可以看到执行后,先从数据库查询,之后去取记录从Hibernate的一级缓存中获得,就不在查库了。好的,我们再次运行测试代码,输出的效果和第一次运行一样,还是要先查一道数据库,控制台会输出sql语句。

下面我们开启memcached服务,将Hibernate的二级缓存配置打开。运行测试代码,第一次运行和没开启二级缓存前效果一样,但是第二次运行测试代码,效果如下

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
2011-6-29 16:31:08 dao.EntityManagerHelper log
信息: finding Products instance with id: 1
1:商品
2011-6-29 16:31:08 dao.EntityManagerHelper log
信息: finding Products instance with id: 1
1:商品

 此时并没有再次访问数据库,而是直接从memcached缓存中获取。之后数次运行测试代码,效果都是从缓存获取,那么我们关闭memcached服务。再运行测试代码,效果如下

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
2011-6-29 16:33:25 dao.EntityManagerHelper log
信息: finding Products instance with id: 1
Hibernate: select products0_.id as id1_0_, products0_.nowPrice as nowPrice1_0_, products0_.picture as picture1_0_, products0_.productMess as productM4_1_0_, products0_.productName as productN5_1_0_, products0_.productsMessageInfo as products6_1_0_, products0_.productsMessageInfo_ID as products9_1_0_, products0_.sourcePrice as sourcePr7_1_0_, products0_.updateTime as updateTime1_0_ from tgweb.products products0_ where products0_.id=?
1:商品
1:商品
2011-6-29 16:33:26 dao.EntityManagerHelper log
信息: finding Products instance with id: 1

 发现依然和没开启缓存的效果是一样的,从数据库去取。证明缓存生效。

而且我们还可以通过XMemcached客户端代码获取缓存对象,代码如下

package dao;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeoutException;

import net.rubyeye.xmemcached.KeyIterator;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.utils.AddrUtil;

import org.hibernate.cache.ReadWriteCache.Item;

public class MemcacheClientTest {
	/**
	 * @param args
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws IOException {
		MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil
				.getAddresses("localhost:11211"));
		MemcachedClient memcachedClient = null;
		try {
			memcachedClient = builder.build();
			// 取所有key
			KeyIterator it = memcachedClient.getKeyIterator(AddrUtil
					.getOneAddress("localhost:11211"));
			while (it.hasNext()) {
				String key = it.next();
				System.out.println("key------:" + key);
			}

			Item item = memcachedClient.get("pojo.Products:0:1");
			HashMap productsHashMap = (HashMap) item.getValue();
			System.out.println("products:" + productsHashMap.get("id") + ":"+ productsHashMap.get("productName"));
		} catch (MemcachedException e) {
			System.err.println("MemcachedClient operation fail");
			e.printStackTrace();
		} catch (TimeoutException e) {
			System.err.println("MemcachedClient operation timeout");
			e.printStackTrace();
		} catch (InterruptedException e) { 
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			memcachedClient.shutdown();
		} catch (IOException e) {
			System.err.println("Shutdown MemcachedClient fail");
			e.printStackTrace();
		}
	}
}

 5.  总结

当然,就像刚开始说的,Hibernate还有很多其他缓存组件可用,不过笔者认为memcache最大的优点就是达到了分布式的二级缓存。尤其是面向集群系统,并发量又比较大,多个服务器之间公用一个缓存,减轻各个Node的负载,当然放到二级缓存的数据并发量不能太大。读取操作倒是可以,如果遇到更新操作会有极小的数据有效性的延迟。就是数据更新了,缓存还没来得及更新呢,就被另一个线程取走了。这个时候就是无效的,当然客户端XMemcachedCAS进行乐观锁锁定,显示报出异常等等措施,但是对于系统的运行效率上就得牺牲一些。总得来说现在使用memcache作为ORM二级缓存渐渐成为了趋势。

 

PS:参考资料

http://developer.51cto.com/art/200909/153715.htm

  • 大小: 44.7 KB
8
4
分享到:
评论
2 楼 zarkk 2011-10-24  
非常好的一个技术,对我非常有用
1 楼 uuid198909 2011-07-12  

顶起
···

相关推荐

    springboot+jpa(hibernate配置redis为二级缓存) springboot2.1.4

    在本文中,我们将深入探讨如何在Spring Boot 2.1.4.RELEASE项目中结合JPA(Java Persistence API)和Hibernate实现Redis作为二级缓存。首先,我们需要理解这些技术的基本概念。 Spring Boot 是一个用于简化Spring...

    springmvc4+spring4+hibernate5.1.3+二级缓存ehcache+fastjson配置

    在本配置中,Hibernate版本为5.1.3,支持JPA规范,提供了二级缓存功能,提高了数据访问性能。 4. **二级缓存(Ehcache)**: Ehcache是Hibernate的一个可选二级缓存插件,用于存储数据库查询结果,减少对数据库的...

    JPA-5 使用二级缓存

    1. **选择缓存提供商**:JPA本身并不提供二级缓存实现,而是依赖于供应商,如Hibernate、EclipseLink等。以Hibernate为例,我们可以选择Hibernate的内置缓存,如Infinispan或 Ehcache。 2. **配置缓存**:在...

    Hibernate二级缓存+分页功能

    Hibernate二级缓存通过插件实现,其中Ehcache是最常用的二级缓存实现。 二级缓存的配置主要包括以下步骤: 1. 引入Ehcache依赖。 2. 配置hibernate.cfg.xml,启用二级缓存并指定Ehcache配置文件。 3. 在实体类上...

    spring-boot-starter-data-jpa 的二级缓存

    总之,Spring Boot通过starter-data-jpa模块和Hibernate、Ehcache整合,为我们提供了一个强大的工具集,可以有效地实现二级缓存机制,优化应用性能,减少数据库压力。开发者需要掌握这些知识点,以确保在实际的项目...

    Hibernate二级缓存实例.rar

    压缩包中的"Hibernate二级缓存实例"应该包含了具体的配置文件、实体类、以及如何使用缓存的代码示例。通过学习这个实例,你可以了解到如何在项目中设置和利用Hibernate的二级缓存,从而提升应用的性能。 总结来说,...

    Hibernate二级缓存的应用

    1. 添加二级缓存库,如本例中的`ehcache-core-2.4.3.jar`,这是Hibernate常用的一种二级缓存实现。 2. 在`pom.xml`或`build.gradle`中引入依赖。 3. 配置`hibernate.cfg.xml`,指定使用二级缓存插件,并设置缓存策略...

    jpa中hibernate实现相关jar包

    在JPA中,Hibernate作为持久化提供者,通过一系列的jar包实现了JPA规范,这些jar包涵盖了从实体管理、事务处理到查询语言等多个方面。在标题和描述中提到的"jpa中hibernate实现相关jar包"是指一组完整的Hibernate库...

    jpa + hibernate 例子

    而**Hibernate**则是一个实现了JPA规范的开源ORM框架,它简化了数据库操作,使得开发者可以使用Java对象来操作数据库,而无需编写大量的SQL语句。 在**JPA + Hibernate**的例子中,通常会涉及以下几个核心概念: 1...

    jpa_hibernate项目 所有jar包

    7. **事务管理**:学习如何使用Hibernate或JPA进行事务控制,确保数据一致性。 8. **性能优化**:掌握查询优化技巧,如避免N+1查询问题,使用批处理等。 **实践应用**: 1. 创建一个简单的JPA_Hibernate项目,从零...

    JPA jar包(基于JPA的Hibernate实现)

    在实际项目中,将这个jar包加入到项目的类路径中,就可以使用Hibernate来实现JPA的功能,进行数据库的交互。 总的来说,JPA结合Hibernate提供了一种高效、便捷的方式来处理Java应用中的数据库操作,大大简化了传统...

    JPA-Hibernate包

    Hibernate不仅实现了JPA规范,还额外提供了一些特性,如二级缓存、更灵活的查询选项和性能优化工具。 **JPA核心概念:** 1. **实体(Entity)**:在JPA中,实体是映射到数据库表的Java类。通过在类上添加`@Entity`...

    Java Web高级编程 涵盖WebSockets、Spring Framework、JPA Hibernate和Spring Security

    Java Web高级编程是一门涵盖多种技术的课程,这些技术包括WebSockets、Spring Framework、JPA(Java Persistence API)以及Hibernate和Spring Security。下面将详细介绍这些知识点。 WebSockets是一种网络通信技术...

    JPA和Hibernate的关系

    ### JPA与Hibernate的关系 ...而Hibernate作为JPA的一种强大实现,不仅完全兼容JPA,还提供了更多的高级功能和灵活性。对于希望利用标准化API进行数据库操作的开发者而言,JPA和Hibernate都是非常有价值的工具。

    Spring + JPA + Hibernate配置

    此外,可能还需要编写DAO接口和实现类,使用JPA或Hibernate的API进行数据操作。 通过这篇博文链接(虽然此处未给出实际链接),开发者可以学习到如何将这三个框架集成到一个项目中,包括如何设置相应的Maven或...

    JPA-Hibernate实现所需的完整Jar包

    Hibernate不仅实现了JPA规范,还提供了许多额外的功能,如二级缓存、 Criteria查询和HQL(Hibernate Query Language)。这使得Hibernate成为企业级Java应用中首选的ORM框架之一。 在使用JPA和Hibernate进行开发时,...

    velocity spring jpa hibernate 整合

    - 定义JPA实体类,并使用Hibernate的注解进行映射。 - 创建Spring的DAO或Repository接口,利用JPA的Query方法进行数据查询。 - 在Velocity模板中,通过Spring的Model传递数据,生成动态内容。 这样的整合使得开发者...

    JPA Hibernate

    JPA Hibernate 帮你详细解释jpa中注解的详细用法 帮你更好的去了解和运用

    JPA注解 和hibernate 建表

    JPA和Hibernate的关系可以概括为:JPA是一个抽象层次,而Hibernate是一个具体的实现。 七、建表过程 建表过程可以分为两种方式: * 先建表:根据数据库表编写配置文件的实体Bean * 先编写文件和实体Bean,然后...

    jpa(hibernate 持久层)所需要的jar包

    Hibernate不仅实现了JPA规范,还提供了额外的功能,如缓存机制、查询语言HQL和CGLIB动态代理等。 在使用JPA和Hibernate进行开发时,我们需要一些特定的**jar包**来支持我们的应用程序。这些jar包通常包括以下几个...

Global site tag (gtag.js) - Google Analytics