Hibernate 是常用的开源ORM框架,提供了缓存机制。Hibernate缓存分为一级缓存和二级缓存。
1.一级缓存
一级缓存是Hibernate默认就支持的,即session级别的缓存。如果不清楚什么是session级别的缓存,请看下面的例子:
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.wicresoft.model.Teacher; public class TeacherTest { public static void main(String[] args) { Teacher t = new Teacher(); t.setId(11); t.setName("TT"); t.setTitle("LL"); Configuration cfg = new Configuration(); // 读取hibernate.cfg.xml中的配置 cfg.configure(); // 获取SessionFactory SessionFactory sf = cfg.buildSessionFactory(); // 获取Session Session session = sf.openSession(); // 开启事务 session.beginTransaction(); // 保存 session.save(t); // 提交事务 session.getTransaction().commit(); // 关闭连接 session.close(); sf.close(); } }这个例子中,我们通过sessionFactory的一个实例,调用openSession方法,获取到了一个session。通过session去操作数据对象(CRUD操作),操作完成后,session会被关闭掉。从session的open到close,session的生命周期就结束了。session级别的缓存的意思就是在session的这个生命周期内缓存才起作用,一旦session关闭了,那么缓存就失效了,也就意味着,另一个session是无法获取这个session里面的缓存的。下面的例子说明了session级别的缓存。
package com.wicresoft.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.wicresoft.model.Teacher; public class TeacherTest { public static void main(String[] args) { Configuration cfg = new Configuration(); // 读取hibernate.cfg.xml中的配置 cfg.configure(); // 获取SessionFactory SessionFactory sf = cfg.buildSessionFactory(); // 获取Session Session session = sf.openSession(); // 开启事务 session.beginTransaction(); // 保存 //session.save(t); Teacher teacher1 = (Teacher)session.load(Teacher.class, 1); System.out.println(teacher1.getName()); Teacher teacher2 = (Teacher)session.load(Teacher.class, 1); teacher2.getName(); System.out.println(teacher2.getName()); // 提交事务 session.getTransaction().commit(); // 关闭连接 session.close(); sf.close(); } }这个例子中,我们会两次去和数据库发生交互,由于我们启用了show_sql特性,在控制台会输出sql.结果如下:
Hibernate: select teacher0_.id as id1_0_0_, teacher0_.name as name2_0_0_, teacher0_.title as title3_0_0_ from Teacher teacher0_ where teacher0_.id=? T1 T1从结果看,只发起了一次sql请求,说明session级别的缓存确实起了作用。不同的session的请求是不能访问其他session的缓存的,下面的例子能说明这个特性。
package com.wicresoft.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.wicresoft.model.Teacher; public class TeacherTest { public static void main(String[] args) { Teacher t = new Teacher(); t.setId(11); t.setName("TT"); t.setTitle("LL"); Configuration cfg = new Configuration(); // 读取hibernate.cfg.xml中的配置 cfg.configure(); // 获取SessionFactory SessionFactory sf = cfg.buildSessionFactory(); // 获取Session Session session = sf.openSession(); // 开启事务 session.beginTransaction(); // 保存 //session.save(t); Teacher teacher1 = (Teacher)session.load(Teacher.class, 1); System.out.println(teacher1.getName()); Teacher teacher2 = (Teacher)session.load(Teacher.class, 1); teacher2.getName(); System.out.println(teacher2.getName()); // 提交事务 session.getTransaction().commit(); Session session2 = sf.openSession(); session2.beginTransaction(); Teacher teacher3 = (Teacher)session2.load(Teacher.class, 1); System.out.println(teacher3.getName()); Teacher teacher4 = (Teacher)session2.load(Teacher.class, 1); teacher2.getName(); System.out.println(teacher4.getName()); session2.getTransaction().commit(); // 关闭连接 session.close(); session2.close(); sf.close(); } }这里两个session去发起sql,虽然访问的内容相同,但是属于不同的session,所以不能互相访问缓存。结果如下:
Hibernate: select teacher0_.id as id1_0_0_, teacher0_.name as name2_0_0_, teacher0_.title as title3_0_0_ from Teacher teacher0_ where teacher0_.id=? T1 T1 Hibernate: select teacher0_.id as id1_0_0_, teacher0_.name as name2_0_0_, teacher0_.title as title3_0_0_ from Teacher teacher0_ where teacher0_.id=? T1 T1这就是一级缓存的效果。
2.二级缓存
Hibernate默认是没有启用二级缓存的,这个设置在hibernate.cfg.xml里面
<property name="cache.provider_class"> org.hibernate.cache.NoCacheProvider </property>如何开启二级缓存呢?这里我们使用EhCache为例:
1)配置hibernate.cfg.xml
<!-- Enable the second-level cache --> <property name="cache.use_second_level_cache">true</property> <!-- <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property> --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>注意:这是Hibernate4及以后的配置方式,如果是 Hibernate3,使用注释中的那种配置。
2)增加EhCache.xml
<ehcache> <!-- Sets the path to the directory where cache .data files are created. If the path is a Java System Property it is replaced by its value in the running VM. The following properties are translated: user.home - User's home directory user.dir - User's current working directory java.io.tmpdir - Default temp file path --> <diskStore path="java.io.tmpdir"/> <!--Default Cache configuration. These will applied to caches programmatically created through the CacheManager. The following attributes are required for defaultCache: maxInMemory - Sets the maximum number of objects that will be created in memory eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired. timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used if the element is not eternal. Idle time is now - last accessed time timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used if the element is not eternal. TTL is now - creation time overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="1200" overflowToDisk="true" /> <!--Predefined caches. Add your cache configuration settings here. If you do not have a configuration for your cache a WARNING will be issued when the CacheManager starts The following attributes are required for defaultCache: name - Sets the name of the cache. This is used to identify the cache. It must be unique. maxInMemory - Sets the maximum number of objects that will be created in memory eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired. timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used if the element is not eternal. Idle time is now - last accessed time timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used if the element is not eternal. TTL is now - creation time overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. --> <!-- Sample cache named sampleCache1 This cache contains a maximum in memory of 10000 elements, and will expire an element if it is idle for more than 5 minutes and lives for more than 10 minutes. If there are more than 10000 elements it will overflow to the disk cache, which in this configuration will go to wherever java.io.tmp is defined on your system. On a standard Linux system this will be /tmp" --> <cache name="sampleCache1" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> <!-- Sample cache named sampleCache2 This cache contains 1000 elements. Elements will always be held in memory. They are not expired. --> <cache name="sampleCache2" maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" /> --> <!-- Place configuration for your caches following --> </ehcache>
注意:EhCache.xml与Hibernate.cfg.xml必须在同一个目录下;配置中eternal=false表示缓存可以更改,非常建议不要更改;
3)导入Hibernate cache jar 包
导入hibernate lib optional 中的jar包 (lib\optional\ehcache) ehcache-core-2.4.3.jar/hibernate-ehcache-4.3.10.Final.jar/slf4j-api-1.6.1.jar
否则会报错
我们依然使用下面的例子:
package com.wicresoft.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.wicresoft.model.Teacher; public class TeacherTest { public static void main(String[] args) { Teacher t = new Teacher(); t.setId(11); t.setName("TT"); t.setTitle("LL"); Configuration cfg = new Configuration(); // 读取hibernate.cfg.xml中的配置 cfg.configure(); // 获取SessionFactory SessionFactory sf = cfg.buildSessionFactory(); // 获取Session Session session = sf.openSession(); // 开启事务 session.beginTransaction(); // 保存 //session.save(t); Teacher teacher1 = (Teacher)session.load(Teacher.class, 1); System.out.println(teacher1.getName()); Teacher teacher2 = (Teacher)session.load(Teacher.class, 1); teacher2.getName(); System.out.println(teacher2.getName()); // 提交事务 session.getTransaction().commit(); Session session2 = sf.openSession(); session2.beginTransaction(); Teacher teacher3 = (Teacher)session2.load(Teacher.class, 1); System.out.println(teacher3.getName()); Teacher teacher4 = (Teacher)session2.load(Teacher.class, 1); teacher2.getName(); System.out.println(teacher4.getName()); session2.getTransaction().commit(); // 关闭连接 session.close(); session2.close(); sf.close(); } }这个例子中,我们有两个session,按照我们的设定,不同session之间可以共享缓存,在结果中只输出一个sql
Hibernate: select teacher0_.id as id1_0_0_, teacher0_.name as name2_0_0_, teacher0_.title as title3_0_0_ from Teacher teacher0_ where teacher0_.id=? T1 T1 T1 T1 2015-7-25 13:58:53 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH000030: Cleaning up connection pool [jdbc:sqlserver://localhost:1433;DatabaseName=HibernateDB]这个结果正是我们需要的结果,证明二级缓存已启用。
3.总结
在ORM级别,缓存能够解决应用与数据库服务器的通信网络瓶颈问题,从很大程度上解决了数据的延迟加载问题。
对于通常不会变更的数据,比如用户权限等,放在缓存中,能够提高应用程序的性能