1.配置c3p0连接池(了解)
* 引入c3p0-0.9.1.jar
* 在hibernate.cfg.xml文件中增加如下配置
<!-- C3P0连接池设定-->
<!-- 使用c3po连接池 配置连接池提供的供应商-->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider </property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>
<!-- 设置自动提交 -->
<property name="connection.autocommit">true</property>
* Hibernate也有一个自带的连接池,默认的,有bug
* 实际上实在spring上面配置,这里可以配置的东西太少了
2.管理session
* 尽管让程序自主管理 Session 对象的生命周期也是可行的, 但是在实际 Java 应用中, 把管理 Session 对象的生命周期交给 Hibernate 管理, 可以简化 Java 应用程序代码和软件架构
* Hibernate 3 自身提供了三种管理 Session 对象的方法
Session 对象的生命周期与本地线程绑定
Session 对象的生命周期与 JTA 事务绑定
Hibernate 委托程序管理 Session 对象的生命周期
* 在Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定Session 管理方式, 可选值包括
@Test
public void testThread(){
// Session session=sf.openSession();
// Transaction tx=session.beginTransaction();
// Session s1=sf.openSession();
// Session s2=sf.openSession();
// System.out.println(s1==s2);//s1和s2表示不同的对象
// tx.commit();
// session.close();
//
//测试使用本地线程绑定
//获取当前的session
Session s1=sf.getCurrentSession();
Session s2=sf.getCurrentSession();
System.out.println(s1==s2);//s1和s2表示相同的对象
}
* 配置了 <property name="hibernate.current_session_context_class">thread</property> s1==s2 没有配置 会报错 org.hibernate.HibernateException: No CurrentSessionContext configured!
3.理解缓存定义
5.缓存中存放的数据
不是很重要的数据, 允许出现偶尔的并发问题
* 不适合放入二级缓存中的数据
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
2 开启二级缓存
<property name="hibernate.cache.use_second_level_cache">true</property>
3 要指定缓存的供应商
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider</property>
4 指定使用二级缓存的类
* 方法一 在使用类的*.hbm.xml配置
选择需要使用二级缓存的持久化类, 设置它的二级缓存的并发访问策略, <class> 元素的 cache 子元素表明 Hibernate 会缓存对象的简单属性, 但不会缓存集合属性, 若希望缓存集合属性中的元素, 必须在 <set> 元素中加入 <cache> 子元素
<!-- 指定使用二级缓存的类 放在maping下面 -->
<!-- 配置类级别的二级缓存 -->
<class-cache class="cn.itcast.c3p0.Customer" usage="read-write"/>
<class-cache class="cn.itcast.c3p0.Order" usage="read-write"/>
<!-- 配置集合级别的二级缓存 -->
<collection-cache collection="cn.itcast.c3p0.Customer.orders"
usage="read-write"/>
5 配置ehcache默认的配置文件ehcache.xml(名字固定)(放在类路径下)
<diskStore path="java.io.tmpdir"/>
* 默认的缓存路径 变量Temp的值
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<defaultCache>: 设置缓存的默认数据过期策略
<cache> 设定具体的命名缓存的数据过期策略
每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。
name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字
maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。 如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
@Test
public void testSecondCache(){
/**************************************************************************************************/
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
/**
* 由于开启二级缓存
* * 下面的查询查询出id=1的客户后,
* * 放入该对象到一级缓存一份,
* * 同时还放入该数据到二级缓存中一份,放置查出的Customer对象到类级别的缓存区域中
* * 产生select语句
*/
Customer c=(Customer)session.get(Customer.class, 1);
System.out.println(c.getAge());
tx.commit();
session.close(); //一级缓存消失
/**************************************************************************************************/
session=sf.openSession(); //开启一个新的session
tx=session.beginTransaction();
/*
*下面的查询查询出id=1的客户的过程
* * 先到session的一级缓存区查找id=1的客户
* * 如果找到 直接返回
* * 如果没有找到,到sessionFactory的二级缓存中查找id=1的客户对象
* * 如果找到 直接返回
* * 如果没有找到,在查询数据库
*
*/
//从二级缓存中获取Customer对象
Customer c1=(Customer)session.get(Customer.class, 1);
System.out.println(c1.getAge());
System.out.println("c1 "+c1); // cn.itcast.cache.Customer@14b5f4a
tx.commit();
session.close();//
/************************************************************************************************/
session=sf.openSession();
tx=session.beginTransaction();
//从二级缓存中获取Customer对象
Customer c2=(Customer)session.get(Customer.class, 1);
System.out.println(c2.getAge());
System.out.println("c2 "+c2); // cn.itcast.cache.Customer@ae533a
tx.commit();
session.close();
/*************************************************************************************************/
}
* 后面两次查询得到的Customer对象不是同一个对象 cn.itcast.cache.Customer@4d28c7 c2 cn.itcast.cache.Customer@2d0483
在srping下能找到
..\lib\concurrent\backport-util-concurrent.jar
..\lib\commons-logging.jar
@Test
public void testUpdate(){
/**************************************************************************************************/
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Customer c=(Customer)session.get(Customer.class, 1);
System.out.println(c.getAge());
c.setAge(45);
tx.commit();
session.close();
session=sf.openSession(); //开启一个新的session
tx=session.beginTransaction();
//从二级缓存中获取Customer对象
Customer c1=(Customer)session.get(Customer.class, 1);
System.out.println(c1.getAge());
tx.commit();
session.close();//
/*************************************************************************************************/
}
* 执行结果为
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.age as age0_0_ from customers customer0_ where customer0_.id=?
41
Hibernate: update customers set name=?, age=? where id=?
45
所以一级缓存更新数据会同步到二级缓存
@Test
public void testCollectionUpdate(){
/**************************************************************************************************/
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
//Order对象放置放置类级别的二级缓存中
/*
* 客户关联的订单集合存放到集合级别的缓存中,此时集合级别的缓存中存放的该订单集合中订单的id
* 1 ,2 ,3, 4, 5, 6,7,8,9,10
*/
Customer c=(Customer)session.get(Customer.class, 1);
System.out.println(c.getAge());
tx.commit();
session.close();
session=sf.openSession(); //开启一个新的session
tx=session.beginTransaction();
/**
* 从二级缓存中获取Customer对象
* 再次查询客户关联的订单集合
* * 到session的一级缓存中区查找,没有找到
* * 到二级缓存中,到集合级别的二级缓存中查找订单,集合级别的二级缓存中放置到订单的id[ 1 ,2 ,3, 4, 5, 6,7,8,9,10]
* 获取集合中订单的时候,select * from orders where id= 1 ,2 ,3, 4, 5, 6,7,8,9,10
* 所以会产生10调价语句
*/
Customer c1=(Customer)session.get(Customer.class, 1);
System.out.println(c1.getAge());
tx.commit();
session.close();//
/*************************************************************************************************/
}
* Order对象没有设置类级别的缓存,第二次查询时会有对应客户的10条查询订单sql语句
<class-cache class="cn.itcast.cache.Order" usage="read-write"/>
下面的配置存在
<class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
<collection-cache collection="cn.itcast.cache.Customer.orderes" usage="read-write"/>
表示二级缓存中不能放置Order对象,能放置Customer对象和Customer对象关联的订单集合
类级别的缓存
* id=1的Customer对象
集合级别的缓存
* 存放的是订单id[[1-10]
* 从二级缓存的类级别的缓存中获取Customer对象,不产生select语句
* 客户关联的订单集合
* 从集合级别的缓存获取查询条件订单id[[1-10]
* 以订单id为条件到二级缓的类级别的缓存中去查找Order对象
* 如果二级缓的类级别的缓存中存在Order对象,不会产生select语句
* 如果二级缓的类级别的缓存中不存在Order对象,会产生select语句
select * from orders where id=1
select * from orders where id=10
@Test
public void testUpdateTimeStamp(){
/**************************************************************************************************/
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
//查询id=1的客户,放置该客户对象到一级缓存和二级缓存,同时还要把查询的时间放置到类级别的时间戳区域T1
Customer c=(Customer)session.get(Customer.class, 1); //select
System.out.println(c.getAge());
// //修改的时候把修改的时间记录到更新时间戳缓存区域 T2
// //修改年龄(insert update delete)
Query query=session.createQuery("update Customer c set c.age=90 where c.id=1");
query.executeUpdate();
tx.commit();
session.close();
session=sf.openSession(); //开启一个新的session
tx=session.beginTransaction();
/*
* 比对T1和T2的时间
* * T1>T2 不查询数据库
* * T1<T2 查询数据库
*/
Customer c1=(Customer)session.get(Customer.class, 1); //
System.out.println(c1.getAge());
tx.commit();
session.close();//
/*************************************************************************************************/
}
* 这种是直接执行更新语句,跟sesion的一级缓存无关,不管属性变了还是没变都会执行sql语句
时间戳缓存区域: org.hibernate.cahce.UpdateTimestampCache
时间戳缓存区域存放了对于查询结果相关的表进行插入, 更新或删除操作的时间戳. Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期, 其运行过程如下:
T1 时刻执行查询操作, 把查询结果存放在 QueryCache 区域, 记录该区域的时间戳为 T1
T2 时刻对查询结果相关的表进行更新操作, Hibernate 把 T2 时刻存放在 UpdateTimestampCache 区域.
T3 时刻执行查询结果前, 先比较 QueryCache 区域的时间戳和 UpdateTimestampCache 区域的时间戳, 若 T2 >T1, 那么就丢弃原先存放在 QueryCache 区域的查询结果, 重新到数据库中查询数据, 再把结果存放到 QueryCache 区域; 若 T2 < T1, 直接从 QueryCache 中获得查询结果
* 类级别,集合级别,查询级别都有一个时间区域,用于与时间戳的时间区域进行对比
应用程序运行时经常使用查询语句
很少对与查询语句检索到的数据进行插入, 删除和更新操作
配置二级缓存, 因为查询缓存依赖于二级缓存
在 hibernate 配置文件中启用查询缓存
<property name="cache.use_query_cache">true</property>
对于希望启用查询缓存的查询语句, 调用 Query 的 setCacheable(true) 方法
@Test
public void testQueryCache(){
/**************************************************************************************************/
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
/**
* 如果没有配置,则类级别的二级缓存中,不能存放Customer对象
* <class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
*
* * 执行query查询session.createQuery("from Customer");
* * 目的查询所有的Customer对象,则对象的id放置查询缓存【1 2 3】
* * 对象的实体类级别的二级缓存中不能存放Customer对象
*/
Query query=session.createQuery("from Customer");
//启用查询缓存
query.setCacheable(true);
query.list();
tx.commit();
session.close();//
/*************************************************************************************************/
session=sf.openSession();
tx=session.beginTransaction();
/**
* 执行query查询session.createQuery("from Customer");
* * 到查询缓存中获取查询条件id【1 2 3】
* * 以id为条件到类级别的缓存中,获取Customer对象
* * 如果存在 不再查询数据库
* * 如何不存在 查询数据库 select * customers where id=1...
* select * customers where id=3
*/
query=session.createQuery("from Customer");
//启用查询缓存
query.setCacheable(true);
//直接从二级缓存中获取数据
query.list();
tx.commit();
session.close();//
}
* 查询缓存 存放的永远是查询条件,不能存放对象的实体,而实体永远存放到类级别的缓存中