Hibernate一级缓存Session和对象的状态
建议看原文:https://blog.csdn.net/qq_42402854/article/details/81461496
一、session简介
首先,SessionFactory 是线程安全的,SessionFactory 用到了工厂模式。
session接口:
Session 接口负责执行被持久化对象的CRUD操作。 Session 接口是一个非线程安全的,避免多个线程共享一个Session实例。
Session 被称为持久化管理器。Session对象是Hibernate技术的核心,持久化化对象的生命周期、事务的管理和持久化对象的查询、更新和删除都是通过Session对象来完成的。
Session 是一个轻量级对象。通常将每一个Session实例和一个数据库事务绑定。每执行一个数据库事务,不论执行成功与否,最后都因该调用Session的 Close() 方法,关闭Session释放占用的资源。
session作用:
1)减少访问数据库的频率
2)保证缓存中的对象与数据库中的相关记录数据保持同步
session清理缓存的时机
清空缓存
当调用 session.evict(customer); 或者session.clear(); 或者session.close()方法时,Session的缓存被清空。
清理缓存
Session具有一个缓存,又叫 Hibernate 的一级缓存,位于缓存中的对象处于持久化状态,它和数据库中的相关记录对应,Session能够在某些时间点,按照缓存中持久化对象的属性变化来同步更新数据库,这一过程被称为清理缓存。
在默认情况下,Session会在下面的时间点清理缓存。
当应用程序调用org.hibernate.Transaction的commit()方法的时候,commit()方法先清理缓存,然后在向数据库提交事务;
当应用程序调用Session的list()或者iterate()时(【注】get()和load()方法不行),如果缓存中持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能能反映持久化对象的最新状态;
当应用程序显式调用Session的flush()方法的时候
在Session清理缓存的时候,会自动进行脏检查,如果发现Session缓存中的对象和数据库记录不一致,就会根据对象的最新属性去同步更新数据库。
Hibernate5 清理缓存的模式不止三种
Junit测试类:
SessionFactory sessionFactory = null;
Session session = null;
Transaction transaction = null;
@Before
public void init() {
// 1. 创建一个 SessionFactory 工厂类: 通过它建立一个与数据库连接回话 session
// 配置类: 封装有我们的配置文件里的配置信息, 返回的 configuration 包含有配置文件里的具体信息
Configuration configuration = new Configuration().configure();
// Hibernate5规定: 所有配置或服务要生效, 必须将其注册到一个服务注册类中
StandardServiceRegistry serviceRegistry = configuration.getStandardServiceRegistryBuilder().build();
sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
// 2. 通过工厂类开启 Session 对象
session = sessionFactory.openSession();
// 3. 开启事务
transaction = session.beginTransaction();
}
@After
public void destory() {
// 5. 提交事务
transaction.commit();
// 6. 关闭 Session
session.close();
// 7. 关闭工厂类
sessionFactory.close();
}
1. get方法 load方法
@Test
public void testGet() {
//get方法/load方法: 通过id,获取数据中对应记录数据,返回对象,放到 session 的缓存中
Student student = session.get(Student.class, 1); //发起sql查询语句
student = null;
Student student2 = session.get(Student.class, 1); //没有发起sql, 而是从缓存中获取数据
System.out.println(student);
System.out.println(student2);
}
结论: session作用一证实
get() 方法与 load() 方法区别
1. get 是立即索引, load 是延迟索引
执行 get() 方法:会立即加载对象
执行 load() 方法: 若不使用该对象,则不会立即执行查询操作,而是生成一个代理对象,
2. load() 方法可能会抛出 LazyInitializationException异常:在需要初始化代理对象之前已经关闭了session.
3. 若数据表中没有对应id 记录, session 也没有关闭
get方法 返回 null
load 方法:若不使用该对象的任何属性,没问题;若需要初始化,则抛出ObjectNotFoundException异常
2. flush方法与reflush方法
@Test
public void testFlush() {
Student student = session.get(Student.class, 1); //发起sql查询语句
student.setName("张三"); // 该student对象处理后,放进session缓存中
// 此时,session缓存中的student对象数据,和数据库中的数据不一致。
//session.flush();//判断,一致:不会发起sql, 不一致: 发起一条update语句,但是不会提交事务, 可省略不写
//transaction.commit(); // 真正提交事务之前(且只会)针对于持久对象,自动调用一次flush方法判断。
}
@Test
public void testReflush() {
Student student = session.get(Student.class, 1); //发起sql查询语句
student.setName("李四"); //修改缓存中的数据
System.out.println(student);
// 此时,session缓存中的student对象数据,和数据库中的数据不一致。
session.refresh(student);//不判断是否一致:都会发起sql,在判断缓存中的对象数据和数据库中非数据,一致: 不动作, 不一致,一条update语句,修改缓存中的数据与数据库数据一致,数据库记录没变化
System.out.println(student);
}
结论: session作用二证实
flush() 方法与 reflush() 方法区别:
flush() 方法: 强制让session缓存中的数据与数据库中的记录数据保持一致,
若不一致,则发起update语句修改数据库数据让其一致。
a. flush() 可能会发送sql语句,但不会提交事务
b. 在事务commit()提交之前先自动调用session的flush()方法,然后提交事务。
reflush() 方法:强制发送一条select语句,保证让数据库中的记录数据与session缓存中的数据保持一致,
若不一致,会修改session缓存中的数据让其一致
3. clear() 方法 : 清空session缓存中的数据
@Test
public void testClear() {
Student student = session.get(Student.class, 1); //发起sql查询语句
System.out.println(student);
session.clear(); //清空session中的数据
//缓存中中不到id=1的student对象数据,重新发起sqlsql查询语句
Student student2 = session.get(Student.class, 1);
System.out.println(student2);
}
二、对象的主要三种状态:
1. 临时 / 瞬态状态(transient):OID 通常为 null
没有session与其关联,数据库中也没有数据与之对应,一般是new出来的对象.超过作用域会被 JVM 垃圾回收器回收.
@Test
public void test1() {
//student是Hibernate将要处理的对象,刚new出来,此时对象处于临时(瞬态)状态,即student为临时对象
Student student = new Student("王五","男",new Date());
//把临时对象放到session缓存中,并发起insert语句(在事务提交时才真正插入到数据库),此时临时对象转为持久状态,即student为持久对象:
session.save(student);
/*持久对象特征:
* 1. 该对象被session托管,在session的缓存中要存在该对象
* 2. 该对象的数据要在数据库的记录中有与之对应(对象OID和数据库记录id)的数据记录,这个Hibernate会拿到数据记录对象的id,即OID
*/
transaction.commit();
}
2、持久状态(persistent): OID 不为 null
有相关联的session,并且相关联的session没有关闭,事务没有提交。数据库中也有数据与之对应.持久态对象发生改变,
数据库中相关联的对象也会跟着改变.在事务提交时会影响到数据库,hibernate能够检测得到它的改变。
@Test
public void test1() {
//student为临时对象
Student student = new Student("王五","男",new Date());
session.save(student); //student为持久对象
student.setName("zs");
transaction.commit(); // flush 会判断
}
@Test
public void test() {
//student为持久对象
Student student = session.get(Student.class, 1);
student.setName("zs");
transaction.commit(); //(且只会)针对于持久对象,自动调用一次flush方法判断。
}
@Test
public void test() {
//student为持久对象
Student student = session.load(Student.class, 1);
student.setName("lisi");
session.clear();
transaction.commit(); //不动作, 无update语句。
}
3、游离 / 脱管状态(detached): OID 不为 null ,不在 session 缓存中。
数据库中有数据与之对应,但没有 session 与其关联,脱管态的对象发生改变,hibernate 是检测不到的。
1)save() 方法与 update() 方法
Hibernate规定: 持久状态对象的id值不准修改,临时状态对象的id可以修改,游离状态对象的id可以被修改,但是没任何效果,,Hibernate会按照自己的机制,重构 id.
@Test
public void test() {
//临时对象
Student student = new Student("李四","男",new Date());
//游离对象: student有id,但是这个对象是new出来的,没有被session托管,即没在session缓存里。
student.setId(1); //id=1;数据库中有id=1的记录
//studet从游离状态转为持久状态
//session.save(student); //对游离状态的对象,设置的id无效, Hibernate会按照自己的机制发起insert语句,插入id值.
//studet从游离状态转为持久状态
//update作用: 更新,将处理的对象,先放到session缓存中,然后做更新数据库中对应id的记录值(无对应id报错)
session.update(student);
//student.setId(2); //再次修改id 提交报错, Hibernate规定 持久状态对象的id值不准修改,临时状态对象的id可以修改
transaction.commit();
}
2)delete() 方法作用:把student对象从session缓存中删除,然后删除数据库对应id的记录
@Test
public void test3() {
//临时对象
Student student = new Student("李四","男",new Date());
//游离对象
student.setId(1); //id=1;数据库中有id=1的记录
//studet从游离状态转为临时状态, 同时数据库对应id的记录会被删除,发起delete语句。
session.delete(student);
//delete方法作用:把student对象从session缓存中删除,然后删除数据库对应id的记录
transaction.commit();
}
3)saveOrUpdate() 方法
saveOrUpdate方法作用:save方法和update的综合体,
如果student对象为临时状态: 调用save方法
如果student对象为游离状态: 调用update方法
@Test
public void test4() {
//临时对象
Student student = new Student("admin","男",new Date());
Student student2 = new Student("admin","女",new Date());
//游离对象
student.setId(2); //id=2;数据库中有id=2的记录
session.saveOrUpdate(student); //调用update方法
session.saveOrUpdate(student2); //调用save方法
transaction.commit();
}
4)update() 方法
Hibernate 不允许session缓存中有两个或两个以上相同 ID 值的对象存在。
@Test
public void test5() {
//student持久对象
Student student = session.get(Student.class, 2);
//student2临时对象
Student student2 = new Student();
//student2游离对象
student2.setId(2); //id=2;数据库中有id=2的记录
session.update(student2); //update:将student2对象放进缓存时,报错NonUniqueObjectException
transaction.commit();
}
三、Hibernate 拿到 jdbc 原生的 connection
doWork() 方法: jdbc 操作 存储过程,批量操作。
import org.hibernate.jdbc.Work;
@Test
public void test() {
session.doWork(new Work() {
public void execute(Connection arg0) throws SQLException {
System.out.println(arg0); //com.mysql.jdbc.JDBC4Connection@30b34287
}
});
}