1、二级缓存所需要的 jar 包
这三个 jar 包实在 hibernate 解压缩文件夹的 lib\optional\ehcache 目录下
2、配置 ehcache.xml
<ehcache> <!-- 指定当缓存数据超出规定缓存大小的时候,超出的缓存数据写入到磁盘的位置 --> <diskStore path="D:/cache/tmp"/> <!-- maxInMemory - 允许在二级缓存中的持久化对象数量 eternal - 缓存中的持久化对象是否允许销毁 false:表示可以销毁 timeToIdleSeconds - 当激活时间到期,持久化对象可以存活的时间(钝化时间) timeToLiveSeconds - 缓存中对象的激活时间(有效时间) overflowToDisk - 是否允许缓存数据序列化到磁盘 --> <defaultCache maxElementsInMemory="10" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> </ehcache>
3、配置 hibernate.cfg.xml
在配置文件添加如下代码:
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
具体如下:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.show_sql">true</property> <!-- 二级缓存配置 --> <!-- 开启二级缓存(默认是开启二级缓存的) --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 配置二级缓存的视实现类(第三方插件包) 以下这个缓存类的包路径是错误的 <property name="hibernate.cache.region.factory_class">org.hibernate.cache.internal.EhCacheRegionFactory</property> --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <mapping resource="learn\hibernate\bean\Person.hbm.xml"/> </session-factory> </hibernate-configuration>
4、支持二级缓存持久化类配置,有两种配置方式
第一种在持久化配置文件中配置:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="learn.hibernate.bean"> <class name="Person" table="t_person"> <!-- 配置持久化类支持二级缓存的读写操作 --> <cache usage="read-write"/> <id name="id" column="person_id"> <generator class="native"/> </id> <property name="name" column="t_name"/> <property name="age"/> <property name="passwork"/> <property name="birthday"/> </class> </hibernate-mapping>
第二种在 hibernate.cfg.xml 文件中配置:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.show_sql">true</property> <!-- 二级缓存配置 --> <!-- 开启二级缓存(默认是开启二级缓存的) --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 配置二级缓存的视实现类(第三方插件包) 以下这个缓存类的包路径是错误的 <property name="hibernate.cache.region.factory_class">org.hibernate.cache.internal.EhCacheRegionFactory</property> --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <mapping resource="learn\hibernate\bean\Person.hbm.xml"/> <!-- 持久化类的二级缓存配置: 1、在持久化类配置文件中配置 <cache usage="read-write"/> 2、在 hibernate.cfg.xml 文件中配置,如下:(推荐) --> <class-cache usage="read-write" class="learn.hibernate.bean.Person"/> </session-factory> </hibernate-configuration>
5、代码测试
/** * 在配置二级缓存的查询中步骤如下: * 先搜索 一级缓存------> 二级缓存 ------> SQL查询-------> 将数据写入缓存中 * 二级缓存是可以在多个 session 中共享的 * get()、load() 方法支持二级缓存的 读写 操作 */ @Test public void testQuery(){ session = factory.openSession(); Person p = (Person)session.get(Person.class, 1); System.out.println(p); session.close(); System.out.println("-------------------"); session = factory.openSession(); Person p2 = (Person)session.get(Person.class, 1); System.out.println(p2); session.close(); } /** * list() 方法支持二级缓存的写,不支持读 */ @Test public void testQuery2(){ String hql = "from Person"; session = factory.openSession(); Query query = session.createQuery(hql); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } session.close(); System.out.println("---------------------------"); session = factory.openSession(); Query query2 = session.createQuery(hql); List<Person> list2 = query2.list(); for(Person p : list2){ System.out.println(p); } session.close(); System.out.println("---------------------------"); session = factory.openSession(); Person p = (Person)session.get(Person.class, 6); System.out.println(p); session.close(); } /** * iterate() 方法支持二级缓存的写读操作 */ @Test public void testQuery3(){ String hql = "from Person"; session = factory.openSession(); Query query = session.createQuery(hql); Iterator<Person> it = query.iterate(); while(it.hasNext()){ System.out.println(it.next()); } session.close(); System.out.println("---------------------------"); session = factory.openSession(); Query query2 = session.createQuery(hql); Iterator<Person> it2 = query2.iterate(); while(it2.hasNext()){ System.out.println(it2.next()); } session.close(); System.out.println("---------------------------"); session = factory.openSession(); Person p = (Person)session.get(Person.class, 6); System.out.println(p); session.close(); }
6、二级缓存的管理
/** * 二级缓存的管理 * */ @Test public void testQuery() throws InterruptedException{ String hql = "from Person"; session = factory.openSession(); // 获得二级缓存的操作句柄(对象) Cache cache = factory.getCache(); Query query = session.createQuery(hql); Iterator<Person> it = query.iterate(); while(it.hasNext()){ System.out.println(it.next()); } System.out.println("--------------------------"); // 判断缓存中是否存在该条数据 boolean flag = cache.containsEntity(Person.class, 7); System.out.println(flag); // 将持久化类从缓存中剔除 cache.evictEntity(Person.class, 7); // 将一个持久化类型从缓存中全部剔除 cache.evictEntityRegion(Person.class); session.close(); }
7、验证超出规定数量的持久化对象会不会被写入到指定的目录下
package learn.hibernate.test; import static org.junit.Assert.*; import java.util.Iterator; import java.util.List; import learn.hibernate.bean.Person; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestHibernate2 { SessionFactory factory = null; Session session = null; Transaction tx = null; /** * 测试之前初始化数据 * @throws Exception */ @SuppressWarnings("deprecation") @Before public void setUp() throws Exception { System.out.println("---------初始化数据----------"); Configuration config = new Configuration().configure(); ServiceRegistry sr = new ServiceRegistryBuilder() .applySettings(config.getProperties()).buildServiceRegistry(); factory = config.buildSessionFactory(sr); //session = factory.openSession(); } /** * 测试之后释放(销毁)数据 * @throws Exception */ @After public void tearDown() throws Exception { System.out.println("---------释放数据----------"); /*if(session.isOpen()){ session.close(); }*/ } /** * 测试缓存中的对象数量大于配置文件后,是否会将多余的对象写入到在磁盘上生成文件 * 关闭 factory 是否会将磁盘文件释放删除 * @throws InterruptedException */ @Test public void testQuery() throws InterruptedException{ String hql = "from Person"; session = factory.openSession(); Query query = session.createQuery(hql); Iterator<Person> it = query.iterate(); while(it.hasNext()){ System.out.println(it.next()); } session.close(); System.out.println("--------sleep-start------"); // 为了观察二级缓存序列化操作过程,让程序暂停短暂时间,观察磁盘文件是否生成 Thread.sleep(10000); System.out.println("--------sleep-end------"); factory.close(); } }
8、查询缓存
首先要做 hibernate.cfg.xml 文件中配置如下代码:
<property name="hibernate.cache.use_query_cache">true</property>
开启查询缓存,默认情况下查询缓存是关闭的;查询缓存是基于二级缓存的
测试:
/** * 只有 list() 支持查询缓存,其他的方法不支持 * 查询缓存的生命周期是很短暂的,即便查询条件的值发生变化也会重新查询 * */ @Test public void testQuery2(){ String hql = "from Person"; session = factory.openSession(); Query query = session.createQuery(hql); /** * 启动查询缓存 * 告诉 hibernate 先到查询缓存中搜索有没有相同的查询语句查询过,有就将之前的查询结果直接返回 */ query.setCacheable(true); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } session.close(); System.out.println("---------------------------"); session = factory.openSession(); Query query2 = session.createQuery(hql); // 如果查询没有变,那么接下来的查询结果将从上一次的查询中获取 query2.setCacheable(true); query2.setCacheable(true); List<Person> list2 = query2.list(); for(Person p : list2){ System.out.println(p); } session.close(); }
9、hibernate 的优化
(1)、使用 Configuration 装载映射文件时,不要使用绝对路径装载。最好的方式是通过 getResourceAsStream() 装载映射文件,这样 hibernate 会从 classpath 中寻找已配置的映射文件。
(2)、SessionFactory 的创建非常消耗资源,整个应用一般只要一个 SessionFactory 就够了,只有多个数据库的时候才会使用多个 SessionFactory。
(3)、在整个应用中,Session 和事务应该能够统一管理。(Spring 为 Hibernate 提供了很好的支持)
(4)、将所有的集合属性配置设为懒加载(lazy=true)
(5)、在定义关联关系时,集合首选 Set,如果集合中的实体存在重复,则选择 List(定义配置文件时,可以将 List定义为 idbag),数组的性能最差。
(6)、在一对多的双向关联中,一般将集合的 inverse 属性设置为 true,让集合的对方维护关联关系。例如:Person-Address,由 Address 来维护 Person 和 Address 的关联关系。
让拥有外键的一端来进行关联关系的维护比较好
(7)、在执行更新的时候尽量配置 dynamic-update=”true”,表示没有修改的属性不参与更新操作
(8)、HQL 子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分
(9)、如果可以,使用乐观锁代替悲观锁
(10)、如果要很好的掌握 Hibernate,熟练掌握关系数据理论和 SQL 是前提条件