Hibernate 的一级缓存就是指 Session 缓存, 此 Session 非 http 的 session 会话技术, 可以理解为 JDBC 的 Connection, 连接会话, Session 缓存就是一块内存空间, 用来存放相互管理的 java 对象, 在使用 Hibernate 查询对象的时候, 首先使用对象的 OID(Object ID) 在 Hibernate 的一级缓存空间进行查找, 如果通过 OID 匹配到了对象, 就直接从一级缓存中取出使用, 如果没有找到匹配该 OID 值的对象, 这才会进行查询数据库. 当从数据库中查询数据的时候, 该数据就会被放入到 Session 缓存中, 目的就是为了减少数据库的访问次数, 从而提高性能.
Hibernate 缓存特点
当应用程序调用 Session 接口的 save(), update(), saveOrUpdate() 时候, 如果缓存中没 有相应的对象, Hb 就会自动的把查询信息加入到缓存.
当应用程序调用 Session 接口的 load(), get(), list() 等查询方法的时候, 会进行判断缓存中是否有数据, 同摘要.
当应用程序调用 Session 接口的 close() Session 缓存会被清空
接下来进行测试一级缓存
首先测试一级缓存是否存在? 对 user 表的主键为 "1" 的分别进行两次查询:
- public void demo1 (){
- Session session = HibernateUtils.openSession();
- Transaction transaction = session.beginTransaction();
- User user1 = session.get(User.class, "1");
- System.out.println(user1);
- System.out.println("-------------");
- User user2 = session.get(User.class, "1");
- System.out.println(user2);
- System.out.println(user1 == user2);
- transaction.commit();
- session.close();
- }
运行结果:
我们发现第一次执行 Session 的 get() 方法的时候, 由于一级缓存中没有数据, 所以会向数据库发送一条 sql 语句进行查询, 第二次调用 get() 的时候, 则不会发送 sql 语句, 而是从一级缓存中取的, 所以 user 和 user2 的内存地址相等
### 一级缓存的快照区
Hibernate 向一级缓存放入数据的时候, 同时复制一份放到 Hibernate 快照中, 当使用 commot() 提交事务的时候, 同时会清理 Session 的一级缓存, 这是会用 OID 判断一级缓存中的对象和快照中的对象是否一致, 如果一致则执行 update 语句, 将缓存中的内容同步到数据库, 并且更新快照, 这也就实现了不使用 update 语句就可以自动更新数据库, Hibernate 快照的作用也就是为了保持缓存中的数据和数据库中的数据的一致性.
快照区代码测试
我们在 get 的时候设置断点, 看到右侧变量 session->persitenceContext->entitlxxx->head, 也就是缓存中是为空的
现在继续执行
我们发现在缓存区域已经有了数据了, 同时 Hibernate 也会在快照区域创建数据, 在 Hibernate3 里面它使用的是 Map 进行存储的, map 的 key 作为一级缓存去, value 作为一级快照区, 当进行查询的时候, 会同时给缓存和快照去放入相同的数据, 当 orm 对象属性发生变化的时候, 会首先改变缓存区的值, 当进行事务提交的时候, 会比较缓存和快照的内容, 如果一致则不操作, 不一致则更新数据库.
继续执行:
commin() 的时候, 自动进行更新, clear().evict() 的时候会清除缓存 (删除表中数据的时候也会)
- public void demo1 (){
- Session session = HibernateUtils.openSession();
- Transaction transaction = session.beginTransaction();
- User user1 = session.get(User.class, "1");
- System.out.println(user1);
- System.out.println("-------------");
- session.clear();
- User user2 = session.get(User.class, "1");
- System.out.println(user2);
- System.out.println(user1 == user2);
- transaction.commit();
- session.close();
- }
输入如下:
- Hibernate:
- select
- user0_.uid as uid1_1_0_,
- user0_.name as name2_1_0_,
- user0_.username as username3_1_0_,
- user0_.password as password4_1_0_,
- user0_.email as email5_1_0_,
- user0_.telephone as telephon6_1_0_,
- user0_.sex as sex7_1_0_
- from
- user user0_
- where
- user0_.uid=?
- User{uid=1, username=baiChaoHua, password=null, name=null, email=null, telephone=null, birthady=null, sex=null, state=0, code=null}
- -------------
- Hibernate:
- select
- user0_.uid as uid1_1_0_,
- user0_.name as name2_1_0_,
- user0_.username as username3_1_0_,
- user0_.password as password4_1_0_,
- user0_.email as email5_1_0_,
- user0_.telephone as telephon6_1_0_,
- user0_.sex as sex7_1_0_
- from
- user user0_
- where
- user0_.uid=?
- User{uid=1, username=baiChaoHua, password=null, name=null, email=null, telephone=null, birthady=null, sex=null, state=0, code=null}
- false
可以看出执行执行了两次 sql,user 对象的内存地址也就自然不一样
一级缓存常用的 API:
clear(): 清空一级缓存.
evict(): 清空一级缓存中指定的某个对象.
refresh(): 重新查询数据库, 用数据库中的信息来更新一级缓存与快照区.
未完待续...
来源: https://www.cnblogs.com/bc8web/p/java.html