1 hibernate 中 持久类对象状态介绍
1.1hibernate 规定三种状态:瞬时态、持久态、脱管态
瞬时态:transient, session没有缓存对象,数据库也没有对应记录。
OID特点:没有值
持久态:persistent, session缓存对象,数据库最终会有记录。(事务没有提交)
OID特点:有值
脱管态:detached, session没有缓存对象,数据库有记录。
OID特点:有值
1.2 状态之间的转换
1.3 瞬时态/临时态
- 获得:一般都是直接创建(new)
- 瞬时态---->持久态
一般操作:save方法、saveOrUpdate
3 瞬时态------>脱管态
一般操作:通过setId方法设置数据
例如:
User user = new User(); //瞬时态
user.setUid(1); //脱管态
1.4 持久态
1.获得:
查询操作:get、loat、createQuery、createCriteria 等 获得都是持久态
执行save之后持久态
执行update之后持久态
2. 持久态------->瞬时态
官方规定执行delete() --民间:删除态
3. 持久态--------->脱管态
session没有记录
session.close () 关闭
session.clear() 清除所有
session.evict(obj) 清除指定的PO对象
1.5 脱管态/游离态
1.获得:
创建、并设置OID的
通过api获得
2. 脱管态------->瞬时态
手动去除OID,设置成默认值
3.脱管态-------->持久态
一般操作:update()、saveOrUpdate
1.6 测试
@Test public void demo01(){ User user = new User(); //瞬时态 user.setUsername("jack"); user.setPassword("1234"); //瞬时态(与oid没有关系)
Session session = factory.openSession(); session.beginTransaction();
session.save(user); //持久态 //---- 持久态就应该有持久态的行为(特性) //user.setUsername("rose"); //持久态对象 被修改后,hibernate将自动生成update语句 //session.flush(); session.getTransaction().commit(); session.close(); System.out.println(user); //脱管态 } |
2 一级缓存
一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。
2.1 证明一级缓存
cfg.xml中配置数据库语句显示
测试一
@Test public void demo02(){ //证明一级缓存 Session session = factory.openSession(); session.beginTransaction(); //1 查询 id = 1 User user = (User) session.get(User.class, 1); System.out.println(user); //2 再查询 -- 不执行select语句,将从一级缓存获得 User user2 = (User) session.get(User.class, 1); System.out.println(user2); session.getTransaction().commit(); session.close(); } |
测试二
@Test public void demo03(){ //清除缓存 Session session = factory.openSession(); session.beginTransaction(); User user = (User) session.get(User.class, 1); //--select System.out.println(user); //清除 //session.clear(); session.evict(user); // 一级缓存没有缓存对象,从数据库直接查询 User user2 = (User) session.get(User.class, 1); //--select System.out.println(user2); session.getTransaction().commit(); session.close(); } |
2.2 一级缓存快照
快照:
与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。
如果一级缓存修改了,
执行commit提交时,将自动刷新一级缓存,
执行update语句,将一级缓存的数据更新到数据库。
refresh 刷新
refresh 保证 一级缓存的数据 与 数据库的数据 保持一致。
将执行select语句查询数据库,将一级缓存中的数据覆盖掉。只要执行refresh都将执行select语句。
@Test public void demo04(){ //刷新 Session session = factory.openSession(); session.beginTransaction();
User user = (User) session.get(User.class, 1); //--select System.out.println(user);
session.refresh(user);
session.getTransaction().commit(); session.close(); } |
快照演示(一级缓存刷新)
@Test public void demo05(){ //快照 Session session = factory.openSession(); session.beginTransaction();
User user = (User) session.get(User.class, 1); //--select System.out.println(user);
//修改持久态对象内容(一级缓存内容)--默认在commit时,将触发update语句。 user.setUsername("rose2");
session.getTransaction().commit(); session.close(); } |
- 问题:一级缓存什么时候刷新?(了解)
默认情况提交(commit())刷新。
@Test public void demo06(){ //设置刷新时机 Session session = factory.openSession(); session.beginTransaction();
//1 设置 session.setFlushMode(FlushMode.MANUAL);
User user = (User) session.get(User.class, 1); user.setUsername("rose4");
//1 查询所有 -- AUTO , 查询之前先更新,保存一级缓存和数据库一样的 //List<User> allUser = session.createQuery("from User").list();
//2手动刷新 --MANUAL 将执行update,注意:一级缓存必须修改后的 session.flush();
// 如果MANUAL 在执行commit 不进行update session.getTransaction().commit(); session.close(); } |
三 二级缓存
缓存(Cache): 计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存
缓存:程序<--(内存)-->硬盘
3.1 什么是二级缓存
- hibernate 提供缓存机制:一级缓存、二级缓存
一级缓存:session级别缓存,在一次请求中共享数据。
二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
- SessionFactory的缓存两部分: 内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。
2 外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。
3.2 二级缓存内部结构
二级就是由4部分构成
类级别缓存
集合级别缓存
时间戳缓存
查询缓存(二级缓存的第2大部分,三级缓存)
3.3 并发访问策略
- 访问策略:读写型(read-write)、只读型(read-only)
3.4 应用场景
- 适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题
2.不适合放入二级缓存中的数据:
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
3.5 二级缓存提供商
- EHCache: 可作为进程(单机)范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。--支持集群。
- OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
- SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
- JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存
X表示支持
3.6 ehcache配置二级缓存
1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
2.开启二级缓存(我要使用二级缓存)
- 在hibernate.cfg.xml 配置二级缓存
<!-- 9.1 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> |
3.确定二级缓存提供商(我要使用哪个二级缓存)
- hibernate.cfg.xml 配置
<!-- 9.2 提供商 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> |
4.确定需要缓存内容
在hibernate.cfg.xml 确定 类缓存 和集合缓存配置项
<!-- 9.3 确定缓存内容 --> <!-- 类缓存 --> <class-cache usage="read-write" class="com.test.Customer"/> <class-cache usage="read-write" class="com.test.Order"/> <!-- 集合缓存 --> <collection-cacheusage="read-write"collection="com.itheima.a_init.Customer.orderSet"/> |
1>配置需要缓存的类
2>配置需要缓存的集合
5.配置ehcache自定义配置文件
步骤1:从jar包复制xml文件
步骤2:将xml重命名“ehcache.xml”
步骤3:将修改后的xml,拷贝到src下
6 测试
6.1 证明
@Test public void demo01(){ //1 证明二级缓存存在 // * 修改toString() // * 如果二级缓存开启,查询3 没有select语句,表示从二级缓存获得的。 // * 将二级缓存关闭,查询3将触发select语句。 Session s1 = factory.openSession(); s1.beginTransaction(); //1 查询id=1 -- 执行select (查询后,将数据存放在一级缓存,之后由一级缓存同步到二级缓存) Customer c1 = (Customer) s1.get(Customer.class, 1); System.out.println(c1); //2 查询id=1 --从一级缓存获取 Customer c2 = (Customer) s1.get(Customer.class, 1); System.out.println(c2);
s1.getTransaction().commit(); s1.close();
System.out.println("----------");
Session s2 = factory.openSession(); s2.beginTransaction(); //3 查询id=1 -- 从二级缓存获取 Customer c3 = (Customer) s2.get(Customer.class, 1); System.out.println(c3);
s2.getTransaction().commit(); s2.close(); } |
6.2 类缓存
- 类缓存:只存放数据
- 一级缓存:存放对象本身
@Test public void demo02(){ //2 类缓存:只存放数据,散装数据。 // * 使用默认的toString(); Session s1 = factory.openSession(); s1.beginTransaction(); //1 查询id=1 -- 执行select Customer c1 = (Customer) s1.get(Customer.class, 1); System.out.println(c1); //2 查询id=1 -- 从一级缓存获取,一级缓存存放对象本身 Customer c2 = (Customer) s1.get(Customer.class, 1); System.out.println(c2);
s1.getTransaction().commit(); s1.close();
System.out.println("----------");
Session s2 = factory.openSession(); s2.beginTransaction(); //3 查询id=1 -- 对象不一样,数据一样 Customer c3 = (Customer) s2.get(Customer.class, 1); System.out.println(c3);
s2.getTransaction().commit(); s2.close(); } |
6.3 集合缓存
@Test public void demo03(){ //3 集合缓存:只存放关联对象OID的值,如果需要数据,从类缓存中获取。 // * 3.1 默认:第一条select 查询客户,第二天 select 查询客户所有订单 // * 3.2 操作:在hibernate.cfg.xml 将 Order 类缓存删除 // *** <!-- <class-cache usage="read-write" class="com.itheima.a_init.Order"/>--> // *** 多了10条select,通过订单的id查询订单 Session s1 = factory.openSession(); s1.beginTransaction();
//1 查询id=1 Customer c1 = (Customer) s1.get(Customer.class, 1); System.out.println(c1); //2 获得订单 for (Order o1 : c1.getOrderSet()) { System.out.println(o1); }
s1.getTransaction().commit(); s1.close();
System.out.println("----------");
Session s2 = factory.openSession(); s2.beginTransaction();
//3 查询id=1 Customer c3 = (Customer) s2.get(Customer.class, 1); System.out.println(c3); //4 获得订单 for (Order o2 : c3.getOrderSet()) { System.out.println(o2); }
s2.getTransaction().commit(); s2.close();
} |
7 时间戳
- 时间戳:任何操作都在时间戳中记录操作时间。
@Test public void demo04(){ //4 时间戳: 所有的操作都会在时间戳中进行记录,如果数据不一致,将触发select语句进行查询 // * 修改toString() Session s1 = factory.openSession(); s1.beginTransaction();
//1 查询id=1 Integer cid = 1; Customer c1 = (Customer) s1.get(Customer.class, cid); System.out.println(c1); //2 绕过一级和二级缓存,修改数据库,修改客户cname=大东哥 s1.createQuery("update Customer set cname = ? where cid = ?") .setString(0, "大东哥") .setInteger(1, cid) .executeUpdate(); //3打印 System.out.println(c1);
s1.getTransaction().commit(); s1.close();
System.out.println("----------");
Session s2 = factory.openSession(); s2.beginTransaction();
//4 查询id=1 -- ? Customer c3 = (Customer) s2.get(Customer.class, 1); System.out.println(c3);
s2.getTransaction().commit(); s2.close(); } |
8 查询缓存
- 查询缓存又称为三级缓存(民间)
- 查询缓存默认不使用。需要手动开启
- 查询缓存:将HQL语句与 查询结果进行绑定。通过HQL相同语句可以缓存内容。
默认情况Query对象只将查询结果存放在一级和二级缓存,不从一级或二级缓存获取。
查询缓存就是让Query可以从二级缓存获得内容。
步骤一:开启查询缓存
<!-- 9.4 开启查询缓存 --> <property name="hibernate.cache.use_query_cache">true</property>
|
步骤二:在查询query对象,设置缓存内容(注意:存放和查询 都需要设置)
@Test public void demo05(){ //5 查询缓存 Session s1 = factory.openSession(); s1.beginTransaction();
//1 query查询 Query q1 = s1.createQuery("from Customer"); q1.setCacheable(true); List<Customer> a1 = q1.list(); for (Customer c1 : a1) { System.out.println(c1); }
//2 cid =1 -- 一级缓存获得 Customer customer = (Customer) s1.get(Customer.class, 1); System.out.println(customer);
s1.getTransaction().commit(); s1.close();
System.out.println("----------");
Session s2 = factory.openSession(); s2.beginTransaction();
//2 cid =1 -- 二级缓存获得 Customer customer2 = (Customer) s2.get(Customer.class, 1); System.out.println(customer2);
//3 query查询 Query q2 = s2.createQuery("from Customer"); q2.setCacheable(true); List<Customer> a2 = q2.list(); for (Customer c2 : a2) { System.out.println(c2); }
s2.getTransaction().commit(); s2.close(); } |
9 ehcache配置文件
- <diskStore path="java.io.tmpdir"/> 设置临时文件存放位置。(缓存一般内存,一定程度时,写入硬盘。)
- 缓存详细设置
<defaultCache> 所有的缓存对象默认的配置
<cache name="类"> 指定对象单独配置
- 参数设置
maxElementsInMemory="10000" 内存最大数
eternal="false" 是否永久(内存常驻留)
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true" 内存满了,是否写入到硬盘
maxElementsOnDisk="10000000" 硬盘最大数
diskPersistent="false" 关闭JVM,是否将内存保存硬盘中
diskExpiryThreadIntervalSeconds="120" 轮询
memoryStoreEvictionPolicy="LRU"
Least Recently Used (specified as LRU).
First In First Out (specified as FIFO)
Less Frequently Used (specified as LFU)
|