什么是缓存?现在我们来看一个模拟的缓存操作
模拟一个缓存例子
package cn.itcast.hibernate;
import java.util.HashMap;
import java.util.Map;
import cn.itcast.hibernate.domain.User;
public class CacheDemo {
static Map cache = new HashMap();
/**
* @param args
*/
public static void main(String[] args) {
User u = getUser(1);
User u1 = getUser(1);
}
public static void update(User user){
updateDB(user);
String key = User.class.getName() + user.getId();
cache.remove(key);
}
public static User getUser(int id) {
String key = User.class.getName() + id;
User user = (User) cache.get(key);
if (user != null)
return user;
user = getFromDB();
cache.put(key, user);
return user;
}
private static void updateDB(User user) {
// TODO Auto-generated method stub
}
private static User getFromDB() {
// TODO Auto-generated method stub
return null;
}
}
这里实现的是简单的模拟缓存原理,只是简单的把数据放入到Map集合中去
牵扯的数据有键值更新加入删除操作
缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、
删除缓存中的无效数据。
一级缓存,Session级共享。
save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。
理解:Session级缓存是相当短暂的,一个请求的完成,Session就会关闭,一级缓存是由Session去控制的,如果Session关闭
缓存数据也随之不存在,下次访问数据,还是要去访问数据库
二级缓存
二级缓存,SessionFactory级共享。
实现为可插拔,通过修改cache.provider_class参数来改变;
hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持,
可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。
在hibernate.cfg.xml中加入:
<class-cache class="className" usage="read-only"/>
或在映射文件的class元素加入子元素:
<cache usage="read-write"/>
其中usage:read-only,read-write,nonstrict-read-write,transactional
Session的:save(这个方法不适合native生成方式的主键),
update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,
但只有(没打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)。
Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;修改cache.use_query_cache为true打开对查询的缓存,
并且调用query.setCacheable(true)或criteria.setCacheable(true)。
SessionFactory中提供了evictXXX()方法用来清除缓存中的内容。
统计信息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息。
下面我们来看二级缓存的一些重要参数
##########################
### Second-level Cache ###
##########################
## optimize chache for minimal "puts" instead of minimal "gets" (good for clustered cache)
#hibernate.cache.use_minimal_puts true
## set a prefix for cache region names
hibernate.cache.region_prefix hibernate.test
## disable the second-level cache
#hibernate.cache.use_second_level_cache false
## enable the query cache
#hibernate.cache.use_query_cache true
## store the second-level cache entries in a more human-friendly format
#hibernate.cache.use_structured_entries true
## choose a cache implementation
#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
## choose a custom query cache implementation
#hibernate.cache.query_cache_factory
解释:
#hibernate.cache.use_second_level_cache false
这个是用来打开二级缓存的,一般在hibernate的hibernate.cfg.xml文件中去配置
配置方法:
1<property name="cache.use_second_level_cache">true</property>
这里只是配置二级缓存打开,缺省的就是true
2还要进一步配置,其二级缓存是用什么框架实现的
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
并且需要把OSCache的配置文件加入到工程的src根目录下 oscache.properties
同时需要引入OSCache框架包 oscache-2.1.jar
3还需要配置哪些类的对象需要二级缓存
<class-cache usage="read-only" class="vo.util.bean.Department"/>
usage节点是配置缓存策略 策略有四种
- read-only 只能对其读取用到,如果对所配置的类修改就报异常,效率比较高
- read-write 严格的读写限制
- nonstrict-read-write
- transactional 事务型缓存
还有一种在映射文件中去配置缓存的对象 这相对一些比较简单 推荐方式
直接在映射文件中的class节点下加入 <cache usage="read-only"/>
完整的映射文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="vo.util.bean">
<class name="Department">
<!--是否使用SessionFactory缓存-->
<cache usage="read-only"/>
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!-- 配置一对多的映射关系 -->
<set name="ems" cascade="all">
<!-- 关联关系的外键 -->
<key column="depaer_id"/>
<!-- 关联关系的实体类配置 -->
<one-to-many class="Employee" />
</set>
</class>
</hibernate-mapping>
4 OSCacheProvider提供者的配置文件
使用统计来分析缓存是否数据的正确
<!-- 统计数 -->
<property name="generate_statistics">true</property>
完整的hibernate的配置文件
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.url">jdbc:mysql:///test</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<!-- 指定其方言 方言的作用就是告诉hibernate是哪种数据库 -->
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<!-- 指定其hibernate是否根据映射文件自动创建表 -->
<property name="hbm2ddl.auto">create</property>
<property name="show_sql">true</property>
<!-- 配置二级缓存是否打开 -->
<property name="cache.use_second_level_cache">true</property>
<!--二级缓存的提供者 这里需要注意的是你的工程中有没有OScache包 oscache-2.1.jar
并且需要把oscache.properties文件拷贝进工程类路径的src目录下 在这个文件里配置
cache.capacity参数
-->
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<!-- 这里配置哪些类的对象需要缓存 usage是缓存策略 -->
<!--class-cache usage="read-only" class="vo.util.bean.Department"/-->
<!-- 统计数 -->
<property name="generate_statistics">true</property>
<mapping resource="vo/util/bean/Department.hbm.xml" />
<mapping resource="vo/util/bean/Employee.hbm.xml" />
</session-factory>
</hibernate-configuration>
然后就可以执行
Statistics st = HibernateUtil.getSessionFactory().getStatistics();
System.out.println("put:" + st.getSecondLevelCachePutCount());
System.out.println("hit:" + st.getSecondLevelCacheHitCount());
System.out.println("miss:" + st.getSecondLevelCacheMissCount());
通过执行这代码就可以知道二级缓存数据的读取成功与否
st.getSecondLevelCachePutCount() 放入缓存数据次数
st.getSecondLevelCacheHitCount() 读取缓存成功次数
st.getSecondLevelCacheMissCount() 读取缓存失败次数
分析 一般程序在第一次与数据库进行交互的时候,hibernate会从缓存中去找,首先从
一级缓存中去找,然后从二级缓存中去找,如果没有找到才去访问数据库,第一次运行时
缓存数据是没有的,所以二级缓存miss一次,同时它会put一次,第二次执行时,所以就会
报成功一次
一级缓存数据的清理
session=HibernateUtil.getSession();
session.evict(arg0) 清理单个某一类Class的数据
session.clear() 清理全部一级缓存数据
二级缓存数据的清理
HibernateUtil.getSessionFactory().evict(arg0) 清理单个缓存对象的数据
HibernateUtil.getSessionFactory().evict(arg0,id) 清理某一条单个缓存对象的某一条数据
二级缓存,SessionFactory级共享。
实现为可插拔,通过修改cache.provider_class参数来改变;
hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持,
可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。
在hibernate.cfg.xml中加入:
<class-cache class="className" usage="read-only"/>
或在映射文件的class元素加入子元素:
<cache usage="read-write"/>
其中usage:read-only,read-write,nonstrict-read-write,transactional
Session的:save(这个方法不适合native生成方式的主键),
update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,
但只有(没打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)。
Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;修改cache.use_query_cache为true打开对查询的缓存
,并且调用query.setCacheable(true)或criteria.setCacheable(true)。
SessionFactory中提供了evictXXX()方法用来清除缓存中的内容。
统计信息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息。
e
分布式缓存
一个系统搭建多台服务器,一个数据库 并发访问共享出现问题
解决 中央缓存
使用缓存的条件
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。
事务
单个数据库(一个SesisonFactory对应一个数据库),由JDBC实现。
Session session = null;
Transaction tx =null;
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
//process
tx.commit();
} catch(HibernateException e){
if(tx != null)tx.rollback();throw e;
}finally {
if (session != null)session.close();
}
connection.setAutoCommit(false);
connection.commit();conn.rollback();
JTATransaction
可以简单的理解成跨数据库的事物,由应用JTA 容器实现;
使用JTATransaction需要配置hibernate.transaction.factory_class参数,
该参数缺省值是org.hibernate.transaction. JDBCTransactionFactory,
当使用JTATransaction时需要将该参数改成org.hibernate.transaction.JTATransactionFactory,
并配置jta.UserTransaction参数JNDI名(Hibernate在启动JTATransaction时要用该值到JNDI的上
下文Context中去找javax.transaction.UserTransaction)。
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
try{
tx.begin();
//多个数据库的session操作;
//session1….
//session2….
tx.commit();
}catch(Exception e){
tx.rollback(); throw e;
}
end 完毕!