花了一天时间把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; } }
<?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>
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)
(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)
第五、综合阐述
综合上面,我们做的比较,可以看出,要是开启了二级缓存,只要是对同一个sessionFactory创建的session执行了同一个数据库对象的操作,注意这里并不是完全相同,只要是查询,不一定是查询同一记录,因为实体类news对应的数据库记录类都缓存在了session中,只要通过key-value去取相应实体就可以了。
如果是同一个session下,执行完全相同操作,即使操作同一数据表都是查询但是id不一样也是不同的,也要进行多次数据库的连接查找,只要找的记录是同一条,才会去session中完全相同的记录。
以上是我菜鸟的一点点自己想法,如果哪位大神无意看到,请勿喷。