Hibernate一级二级缓存

花了一天时间把hibernate二级缓存研究了下,突然搞出一些门道,为了防止我的小容量的脑袋忘记,于是在这里准备把我了解到的一些东西讲一下。

第一、一级缓存二级缓存的概念解释(参考文章Hibernate中一级缓存和二级缓存使用详解)

(1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个

session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据;

(2)二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库,注意只需进行了相同数据库操作,可以不是同一个session

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


第二、hibernate3和hibernate4中的EhCache的配置

(1)话说我用的hibernate3.6.10 final,发现其lib/optional/下根本就没有ehcache目录的jar包,很是奇怪,于是我下了hibernate4.2.1,发现其相同路径下是有的,所以我把它路径下的所有包加到了自己自定义了一个User Libraries,同时去网上搜索下载添加了commons-logging和backport-util-concurrent两个jar包。到此所有包添加完毕。

(2)如果是hibernate3的hibernate.cfg.xml配置:  

 <!-- 开启二级缓存 -->
 <property name="hibernate.cache.use_second_level_cache">true</property>
 <!-- 设置缓存提供者 -->
 <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
如果是hibernate4的配置:

 

 <!-- 开启二级缓存 -->
 <property name="hibernate.cache.use_second_level_cache">true</property>

 <property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory </property>

第三、程序的实现

(1)创建一个java项目,创建一个News类(MyHibernateSecondCache/src/shamrock/model/News.java):

package shamrock.model;

public class News {
     private Integer id;
     private String title;
     private String content;
     
     public News(){
    	 
     }

	public Integer getId() {
		return id;
	}

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

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
     
}

创建一个News相应的配置文件(MyHibernateSecondCache/src/shamrock/model/News.hbm.xml):

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2015-5-11 11:22:43 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="shamrock.model">
    <class name="News" table="news_table">
    <span style="color:#FF0000;">  <cache usage="read-only"/></span>
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="title" type="java.lang.String">
            <column name="TITLE" />
        </property>
        <property name="content" type="java.lang.String">
            <column name="CONTENT" />
        </property>
    </class>
</hibernate-mapping>

(2)创建运行程序Manager (MyHibernateSecondCache/src/shamrock/run/Manager.java):

package shamrock.run;

import java.util.List;
import java.util.Map;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import shamrock.model.News;

public class Manager {
    static Configuration conf = new Configuration().configure(); 
    static SessionFactory sf = conf.buildSessionFactory();
    
    public static void main(String[] args){
    	Manager mgr = new Manager();
    	mgr.secondCacheTest();
    	mgr.stat();
    }
    
    public void secondCacheTest(){
    	<span style="color:#FF0000;">Session sess = sf.getCurrentSession();</span>
    	sess.beginTransaction();
    	<span style="color:#FF0000;">List names = sess.createQuery("from News news").list();</span>
    	sess.getTransaction().commit();
    	System.out.println("-------------------");
    	<span style="color:#FF0000;">Session sess2 = sf.getCurrentSession();</span>
    	sess2.beginTransaction();
    	<span style="color:#FF0000;">News news = (News) sess2.load(News.class, 2);</span>
    	System.out.println(news.getTitle());
    	<span style="color:#FF0000;">Session sess3 = sf.getCurrentSession();</span>
    	sess3.beginTransaction();
    	<span style="color:#FF0000;">News newss = (News) sess3.load(News.class, 3);</span>
    	System.out.println(newss.getTitle());
    	sess2.getTransaction().commit();
    }
    private void stat(){
    	Map cacheEntries = sf.getStatistics().getSecondLevelCacheStatistics("shamrock.model.News").getEntries();
    	System.out.println(cacheEntries);
    	
    }
}
(3)ehcahce.xml(MyHibernateSecondCache/src/ehcache.xml)的配置,该文件在hibernate官方包中的project\etc可以找到,直接复制内容。这里就不贴内容了。


(4)hibernate.cfg.xml(MyHibernateSecondCache/src/hibernate.cfg.xml)的配置

<?xml version="1.0" encoding="UTF-8"?>
<!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="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.password">123456</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/myjava</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="hibernate.c3p0.min_size">1</property>
  <property name="hibernate.c3p0.timeout">5000</property>
  <property name="hibernate.c3p0.acquire_increment">2</property>
  <property name="hibernate.c3p0.idle_test_period">3000</property>
  <property name="hibernate.c3p0.max_size">20</property>
  <property name="hibernate.c3p0.max_statements">100</property>
<span style="color:#FF0000;">  <property name="hibernate.cache.use_second_level_cache">true</property>
  
  <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property></span>
  <!--  
  <property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory </property>
  -->
  <property name="hibernate.cache.use_structured_entries">true</property>
  <property name="hibernate.generate_statistics">true</property>
  <property name="hibernate.current_session_context_class">thread</property>
  <property name="hibernate.format_sql">true</property>
  <property name="hibernate.show_sql">true</property>
  <property name="hibernate.hbm2ddl.auto">update</property>
  <mapping resource="shamrock/model/News.hbm.xml"/>
 </session-factory>
</hibernate-configuration>
第四、数据库表内容

<span style="font-size:14px;">ID     TITLE    CONTENT
1       11        111
2       22        222
3       33        333
4       44        444</span>

第五、缓存结果比较

(1)上面的程序是开启了二级缓存,运行结果如下:

五月 11, 2015 8:57:19 下午 com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
五月 11, 2015 8:57:19 下午 com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10]
五月 11, 2015 8:57:19 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@963f5a0d [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@abf33e4b [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bqpdea99ilr5cck4icj5|101b9cf, idleConnectionTestPeriod -> 3000, initialPoolSize -> 1, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 5000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 1, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@96b69ade [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99ilr5cck4icj5|317bdd, jdbcUrl -> jdbc:mysql://localhost:3306/myjava, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99ilr5cck4icj5|38f07b, numHelperThreads -> 3 ]
Hibernate: 
    select
        news0_.ID as ID0_,
        news0_.TITLE as TITLE0_,
        news0_.CONTENT as CONTENT0_ 
    from
        news_table news0_
-------------------
22
33
{1={content=111, title=11, _version=null, _lazyPropertiesUnfetched=true, _subclass=shamrock.model.News}, 2={content=222, title=22, _version=null, _lazyPropertiesUnfetch
可以看出设置了二级缓存,不管操作是不是完全一样,只要打开的session是同一个操作,即查询操作,那就只进行第一次查询操作的数据库连接,后面两次直接去二级缓存里面去取,因为SessionFactory存在含有查询内容的Session实体的内容值。

(2)MyHibernateSecondCache/src/shamrock/model/News.hbm.xml中 <cache usage="read-only"/>去掉,运行结果如下:

五月 11, 2015 9:05:36 下午 com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10]
五月 11, 2015 9:05:36 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@30c6cffd [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@dc46c428 [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bqpdea99im1t6p9a9bry|1cd6a5b, idleConnectionTestPeriod -> 3000, initialPoolSize -> 1, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 5000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 1, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@1750a1c [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99im1t6p9a9bry|157ace, jdbcUrl -> jdbc:mysql://localhost:3306/myjava, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99im1t6p9a9bry|1355469, numHelperThreads -> 3 ]
Hibernate: 
    select
        news0_.ID as ID0_,
        news0_.TITLE as TITLE0_,
        news0_.CONTENT as CONTENT0_ 
    from
        news_table news0_
-------------------
Hibernate: 
    select
        news0_.ID as ID0_0_,
        news0_.TITLE as TITLE0_0_,
        news0_.CONTENT as CONTENT0_0_ 
    from
        news_table news0_ 
    where
        news0_.ID=?
22
Hibernate: 
    select
        news0_.ID as ID0_0_,
        news0_.TITLE as TITLE0_0_,
        news0_.CONTENT as CONTENT0_0_ 
    from
        news_table news0_ 
    where
        news0_.ID=?
33
Exception in thread "main" java.lang.NullPointerException
	at shamrock.run.Manager.start(Manager.java:39)
	at shamrock.run.Manager.main(Manager.java:19)

这里没有设置二级缓存,所有操作都不同,也不是在一个session中,所以,这里每一次查询都要进行数据库操作,不存在二级缓存故也不存在相应实体key-value所以mgr.stat();报错为null。

(3)在(2)的基础上,修改manager.java中News newss = (News) sess3.load(News.class, 3);修改为News newss = (News) sess3.load(News.class, 2);运行结果:

五月 11, 2015 9:11:16 下午 com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
五月 11, 2015 9:11:16 下午 com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10]
五月 11, 2015 9:11:17 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@2eeaac69 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@da6aa094 [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bqpdea99im93nne6to47|1cd6a5b, idleConnectionTestPeriod -> 3000, initialPoolSize -> 1, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 5000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 1, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@226df030 [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99im93nne6to47|157ace, jdbcUrl -> jdbc:mysql://localhost:3306/myjava, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 1bqpdea99im93nne6to47|1355469, numHelperThreads -> 3 ]
Hibernate: 
    select
        news0_.ID as ID0_,
        news0_.TITLE as TITLE0_,
        news0_.CONTENT as CONTENT0_ 
    from
        news_table news0_
-------------------
Hibernate: 
    select
        news0_.ID as ID0_0_,
        news0_.TITLE as TITLE0_0_,
        news0_.CONTENT as CONTENT0_0_ 
    from
        news_table news0_ 
    where
        news0_.ID=?
22
22
Exception in thread "main" java.lang.NullPointerException
	at shamrock.run.Manager.start(Manager.java:39)
	at shamrock.run.Manager.main(Manager.java:19)


这里虽然没有设置二级缓存,但是因为是同一个session下进行完全相同的操作即查询id=2的记录获取其title,所以这里hibernate默认是开启了一级缓存,所以直接去一级缓存里面取,二级缓存不存在,故stat()报错。


第五、综合阐述

综合上面,我们做的比较,可以看出,要是开启了二级缓存,只要是对同一个sessionFactory创建的session执行了同一个数据库对象的操作,注意这里并不是完全相同,只要是查询,不一定是查询同一记录,因为实体类news对应的数据库记录类都缓存在了session中,只要通过key-value去取相应实体就可以了。


如果是同一个session下,执行完全相同操作,即使操作同一数据表都是查询但是id不一样也是不同的,也要进行多次数据库的连接查找,只要找的记录是同一条,才会去session中完全相同的记录。



以上是我菜鸟的一点点自己想法,如果哪位大神无意看到,请勿喷。




你可能感兴趣的:(Hibernate,二级缓存)