项目结构:
实体类 User.java:
package com.hibernate.status;
import javax.persistence.*;
import java.util.Objects;
public class User {
private int id;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
映射文件 User.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.status">
<class name="User" >
<id name="id">
<generator class="native"/>
</id>
<property name="username"/>
</class>
</hibernate-mapping>
主配置文件 hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/hib_demo</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
测试程序 CacheDemo.java:
package com.hibernate.status;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import java.util.Iterator;
import java.util.List;
/**
* 为什么要用缓存?
* 减少对数据库的访问次数,从而提升 hibernate 的执行效率;
*
* hibernate 中的缓存分为 一级缓存,和 二级缓存;
*
* 一级缓存概念:
* 1、hibernate 中的一级缓存,也叫做 session 缓存,他可以在 session 范围内减少数据库的访问次数;
* 只在 session 范围有效,session 关闭,一级缓存失效;
* 2、不同的 session 具有不同的缓存区,不会共享缓存数据;
* 3、当调用 save/update/saveOrUpdate/get/load/list/iterator 等方法的时候,都会把对象放入 session 的缓存中;
* 4、session 的缓存由 hibernate 维护,用户不能操作缓存内容;如果想操作缓存,必须通过 hibernate 提供的
* flush()、evict()、clear() 方法;
*
* 一级缓存特点:
* 只在当前 session 范围有效,作用时间短,效果不是特别明显;
* 在短时间内多次操作数据库,效果比较明显;
*
* 操作缓存的几个方法:
* session.flush():刷新缓存,让缓存数据同步到数据库中;
* session.evict():清空一级缓存中指定的对象;
* session.clear():清空一级缓存中缓存的所有对象;
*
* 一般在批量操作时,需要自己管理缓存:
* 先同步数据库:session.flush()
* 然后清除缓存:session.clear()
*/
public class CacheDemo {
private static SessionFactory sessionFactory;
static {
sessionFactory = new Configuration()
.configure()
.addClass(User.class) // 自动加载映射文件,测试时候使用
.buildSessionFactory();
}
/**
* 一级缓存
*/
@Test
public void testCache() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// 获取数据
User user = session.get(User.class, 1);
System.out.println(user);
/**
* hibernate 获取对象数据以后,会把对象放入缓存(一级缓存)中;
* 修改对象属性的时候,会先把修改后的对象和 缓存中的对象进行比较;
* 如果对象一样,则不更改数据库,如果对象不一样,则更新数据库;
*/
user.setUsername("Tom");
/**
* 获取数据时,先检查缓存中是否有匹配的对象数据;如果没有,则向数据库查询数据;
* 如果缓存中有数据,则不向数据库查询,直接从缓存中获取;
* 此次获取数据,不会向数据库查询,因为前面已经获取到数据,存入缓存了;
*/
user = session.get(User.class, 1);
// 提交事务,关闭 session
transaction.commit();
session.close();
}
/**
* flush() 刷新缓存:让缓存数据同步到数据库
*/
@Test
public void testFlush() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// 获取数据
User user = session.get(User.class, 1);
/**
* 执行下面两行代码,只生成一条 update 语句,更新最后设置的数据到数据库;
* 因为执行上面的 get() 方法后,会把查询的 user 对象放入缓存中,下面修改对象的属性,
* 也是把修改后的对象放入缓存中,直到调用 transaction.commit() 方法,commit() 方法
* 中调用了 session.flush() 方法,刷新缓存,将缓存数据同步到数据库;
*/
user.setUsername("张三2号");
user.setUsername("张三3号");
// 手动刷新缓存:单步调试可以发现,调用 session.flush() 方法的时候,执行 update 语句,将缓存数据同步到数据库;
session.flush();
// 再次修改对象属性,执行 transaction.commit() 方法时,会将修改过的对象 重新更新到数据库;
user.setUsername("Tom");
transaction.commit();
session.close();
}
/**
* clear()、evict() 清除缓存
*/
@Test
public void testClear() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// 获取数据
User user = session.get(User.class, 2);
// 清除缓存:如果不清除缓存,两次 get() 方法只会向数据库查询一次,第二次查询直接从缓存中获取数据;
// 清除缓存后,两次 get() 方法都会向数据库查询数据
session.clear(); // 清除缓存中的所有内容
session.evict(user);// 清除缓存中指定的对象
user = session.get(User.class, 2);
transaction.commit();
session.close();
}
/**
* list 查询:
* 一次把所有的记录都查询出来,只生成一条 select 语句;
*/
@Test
public void testList() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// HQL 查询
Query query = session.createQuery("from User");
// list 方法:只生成一条 select 语句,一次性把数据库的数据全部查询出来
List<User> list = query.list();
for (User user : list) {
System.out.println(user);
}
transaction.commit();
session.close();
}
/**
* Iterator 查询:
* 根据生成的 sql 语句可以发现,
* 首先生成的一条 select 语句,是查询表中的所有 id;
* 然后再根据每个 id 从数据库 查询数据;
*
* 所以生成的 select 语句比表中的记录数多一条,叫做 N+1 查询;N 表示表中的记录数;
*/
@Test
public void testIterator() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// HQL 查询
Query query = session.createQuery("from User");
// Iterator 方法:N + 1 查询
Iterator iterate = query.iterate();
while (iterate.hasNext()){
User user = (User) iterate.next();
System.out.println(user);
}
transaction.commit();
session.close();
}
/**
* 测试 iterator 查询的数据会不会放入缓存,并从缓存获取:
* iterator 查询的数据会放入缓存,也会从缓存中获取数据;
*/
@Test
public void testIteratorCache() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// HQL 查询
Query query = session.createQuery("from User");
// 第一次调用 Iterator 方法
Iterator iterate = query.iterate();
while (iterate.hasNext()){
User user = (User) iterate.next();
System.out.println(user);
}
System.out.println("========================");
// 第二次调用 Iterator 方法
// 从生成的 sql 语句可以发现,第二次调用 iterator 的时候,只生成了一条查询所有 id 的 select 语句,
// 并没有生成从数据库查询数据的 select 方法;
// 说明第一次调用 iterator 的时候,已经将查询的数据放入了缓存;第二次调用 iterator 的时候,
// 只要查询到 id,然后再从缓存中根据 id 查询数据;
iterate = query.iterate();
while (iterate.hasNext()){
User user = (User) iterate.next();
System.out.println(user);
}
transaction.commit();
session.close();
}
/**
* 测试 list 查询的数据会不会放入缓存
* list 查询的数据会放入缓存,但不会从缓存中获取数据;
*/
@Test
public void testListCache() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// HQL 查询
Query query = session.createQuery("from User");
// 第一次调用 list 方法
List<User> list = query.list();
for (User user : list) {
System.out.println(user);
}
System.out.println("==========================");
// 第二次调用 list 方法
// 从生成的 sql 语句可以发现,第二次调用 list 方法的时候,同样生成了一条从数据库
// 查询数据的 select 方法,说明 list 方法不能从缓存中获取数据;
list = query.list();
for (User user : list) {
System.out.println(user);
}
System.out.println("==========================");
// 调用 Iterator 方法:从生成的 sql 语句发现,只生成了一条查询 id 的 select 语句,
// 说明前面 list 查询的数据放入了缓存中,此处 iterator 方法查询到 id 以后,直接
// 根据 id 从缓存中获取数据了;
Iterator iterate = query.iterate();
while (iterate.hasNext()){
User user = (User) iterate.next();
System.out.println(user);
}
transaction.commit();
session.close();
}
}
最后一个 “测试 list 查询的数据会不会放入缓存” 结果: