Hibernate实体类的瞬时、持久、托管状态的转换与详解、一级缓存(session缓存)与快照、其它API的save与persist细节详解-day02下

第一节 Hibernate 实体类的三种状态

1.1 状态介绍

  • 实体Entity有三种状态:瞬时状态、持久状态、托管状态
  • 瞬时状态:transient, session没有缓存,数据库也没有记录,oid没有值
  • 持久状态:persistent, session有缓存, 数据库也有记录,oid有值
  • 脱管状态/游离状态:detached,session没有缓存,数据库有记录,oid有值

1.2 瞬时 转 持久

  • 新创建的一个对象,经过save,或者savaOrUpdate调用后,会变成持久状态
package com.it.hibernate.test;

import com.it.hibernate.domain.User;
import com.it.hibernate.uitls.HibernateUtils;
import org.hibernate.Session;
import org.junit.Test;

public class Demo3 {
    
    
	/*
		瞬时 -> 持久
	*/
	@Test
	public void test3(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//新创建一个对象就是瞬时状态
		//此时:该对象没有id,数据库里没有记录,session没有缓存
		User user = new User("shu","123");
		session.save(user);//通过save或saveOrUpdate保存就会变成持久状态
		//saveOrUpdate方法会判断user里有没有id,有id,更新数据,没有id,插入数据

		//经过保存后,这个对象就是持久状态,
		//这时:id有值,数据库也有值,session有缓存
		session.getTransaction().commit();
		session.close();
	}

}

在这里插入图片描述

  • session有缓存的理解:
	@Test
	public void test4(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		User user = new User("shu02","123");
		session.save(user);//持久化保存
		System.out.println(user);
		//由于上面save保存过了,session中有缓存,有id和对象,
		//查阅数据库可以知道下一个数据的id为2
		//本来使用get方法会想执行select查询一下,现在没有执行select,
		//是因为session缓存中有数据,就不用查询数据库了,直接从session中取数据
		User user1 = (User) session.get(User.class,2);
		System.out.println(user1);
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述

1.3 持久 转 托管

  • load、get返回的对象是持久状态的,当session关闭或者清除后,对象变成托管状态
	@Test
	public void test5(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//通过get方法可以获取一个持久状态的对象
		User user1 = (User) session.get(User.class,2);
		System.out.println(user1);
		User user2 = (User) session.get(User.class,2);
		System.out.println(user2);
		session.getTransaction().commit();
		session.close();
	}
  • 发现2个get只执行了一次select,是因为有session缓存
    在这里插入图片描述
  • 清除session缓存就能使持久转托管
	/*
	 	持久 -> 托管 (清除session缓存)
	 */

	@Test
	public void test6(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//通过get方法可以获取一个持久状态的对象
		User user1 = (User) session.get(User.class,2);
		System.out.println(user1);

		//清除session缓存
		session.clear();

		User user2 = (User) session.get(User.class,2);
		System.out.println(user2);
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述

1.4 总结状态的转换过程

  • 查询操作:get、load、createQuery、createCriteria 等操作,获得都是持久态

  • 瞬时状态执行:save、update、saveOrUpdate之后,变成持久状态

  • 持久态 转换 托管态

    • session.close (); 关闭
    • session.clear(); 清除所有
    • session.evict(obj); 清除指定的PO对象

一些概念:
PO:persistent object ,用于与数据库交互数据。–dao层 (JavaBean + hbm )
BO:Business object 业务数据对象。–service层
VO:Value Object 值对象。–web层

第二节 一级缓存

2.1 概念

  • 一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

2.2 证明一级缓存

	/*
		证明一级缓存
	*/
	@Test
	public void test4(){
    
    
		Session session = HibernateUtils.openSession();
		System.out.println(session.getClass());
		User user1 = (User) session.get(User.class, 1);
		System.out.println(user1);
		User user2 = (User) session.get(User.class, 1);
		System.out.println(user2);
		System.out.println(user1==user2);
		session.close();
	}

  • 2次get,1次select,是由于session缓存,第一次从数据库中取所以有select查询,第二次从session缓存中取,不会有sql执行,默认情况下没有重写equals方法,比较的是2个对象的地址,发现地址相同(获取一个用户放入session,会用map存储起来)
    在这里插入图片描述

2.3 移除缓存

	//移除缓存
	@Test
	public void test5(){
    
    
		Session session = HibernateUtils.openSession();
		User user1 = (User) session.get(User.class, 1);
		System.out.println(user1);
		//session.clear();//清除所有缓存
		session.evict(user1);//清除指定对象缓存,使之成为托管/游离状态
		//这时就又要从数据库中加载数据,会有2个select语句输出
		User user2 = (User) session.get(User.class, 1);
		System.out.println(user2);
		System.out.println(user1==user2);
		session.close();
	}
  • 此时地址当然不同
    在这里插入图片描述

2.4 一级缓存快照

  • 快照与一级缓存存放位置是一样的,是对一级缓存数据备份。保证数据库的数据与一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。

  • 修改了属性,快照和一级缓存比较确实修改了,commit后会update更新到数据库,否则不会update
    在这里插入图片描述

  • 查看数据库
    在这里插入图片描述

	//一级缓存快照
	@Test
	public void test5(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		User user = (User) session.get(User.class, 1);
		//查看数据库shu 123
		//与快照比较,如果修改的属性没有变化,不会执行update语句,
		//如果发生变化,在commit提交时会自动刷新一级缓存执行update,将一级缓存的数据更新到数据库
		user.setUsername("zhangsan");
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述

  • 再执行一次,虽然设置了属性,但是没有属性变化,不会有update
    在这里插入图片描述

2.5 快照演示(一级缓存刷新)

  • 查看数据库
    在这里插入图片描述

  • 以下程序执行后会有几个update执行?

	//一级缓存刷新
	@Test
	public void test6(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		User user = (User) session.get(User.class, 2);
		//查看数据库:id为2的数据 shu02 123
		user.setUsername("lisi");
		user.setUsername("wangwu");
		session.getTransaction().commit();
		session.close();
	}
  • 1个update执行,因为第二个会覆盖掉第一个,即最后姓名为wangwu
    在这里插入图片描述
    在这里插入图片描述

手动刷新,保持一级缓存与数据库一致

	//一级缓存刷新
	@Test
	public void test7(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		User user = (User) session.get(User.class, 2);
		//查看数据库:id为2的数据 wangwu 123
		user.setUsername("lisi");
		//手动刷新,保持一级缓存与数据库一致
		session.flush();//第一个update:执行完这条语句,有update,但是事务没有提交,数据库不会刷新
		//此时会有2个update语句
		user.setUsername("zhaoliu");
		session.getTransaction().commit();//第二个update:执行完commit,数据才会更新到数据库
		session.close();
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6 一级缓存的细节

HQL的结果会进行一级缓存

	//HQL的结果有一级缓存,也就是有session缓存
	@Test
	public void test8(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		Query query = session.createQuery("from User ");
		query.list();//有select
		User user = (User) session.get(User.class, 2);//发现没有对应select
		System.out.println(user);
		session.getTransaction().commit();
		session.close();
	}
  • 有session缓存(也就是一级缓存),就不用再查询数据库执行sql
    在这里插入图片描述

SQL的结果不会添加到一级缓存

	@Test
	public void test9(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
		sqlQuery.list();//有select
		User user = (User) session.get(User.class, 2);//发现有对应select
		System.out.println(user);
		session.getTransaction().commit();
		session.close();
	}
  • 没有session缓存,还要再查询数据库执行sql,找到id为2的user
    在这里插入图片描述

Criteria也会对数据进行一级缓存

	@Test
	public void test10(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		Criteria criteria = session.createCriteria(User.class);
		criteria.list();//有select
		User user = (User) session.get(User.class, 2);//没有对应select
		System.out.println(user);
		session.getTransaction().commit();
		session.close();
	}
  • 有session缓存,不用再查询数据库执行sql,找id为2的user
    在这里插入图片描述

第三节 其它API的细节详解

3.1 save和persist方法的细节与区别

save方法:瞬时态 转换 持久态 ,会初始化OID

  1. 执行save方法,立即触发insert语句,从数据库获得主键的值(OID值)。
  2. 执行save方法前,设置OID将忽略。
	@Test
	public void test6(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		User user = new User("shu03", "1234");
		user.setUid(30);
		System.out.println("保存前:"+user);
		session.save(user);//持久态
		System.out.println("save保存后:"+user);
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述
3. 如果执行查询,session缓存移除了,再执行save方法,将再次执行insert。

	@Test
	public void test7(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		User user = new User("shu03", "1234");
		user.setUid(30);
		System.out.println("保存前:"+user);
		session.save(user);//持久态,save直接insert
		System.out.println("save保存后:"+user);
		session.clear();//移除session,托管态
		user.setUid(31);
		session.save(user);//再执行save将再insert,共2个insert
		System.out.println(user);
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述

persist方法:瞬时态 转换 持久态

  • save和persist都是持久化对象的作用
  1. persist保存的对象,在保存前,不能设置id,否则会报错

detached 游离态:session没有缓存,数据库有记录,oid有值

在这里插入图片描述

  • 注释后,成功运行
    在这里插入图片描述
    在这里插入图片描述
  1. save 因为需要返回一个主键值,因此会立即执行 insert 语句,而 persist 在事务外部调用时则不会立即执行 insert 语句,在事务内调用还是会立即执行 insert 语句的。
  • 注释事务就在事务外了,persist不会执行sql,也不会保存数据到数据库,在事务内会立即执行insert
    在这里插入图片描述
  • save方法不加事务也可以立即执行insert,把数据保存到数据库
    在这里插入图片描述

一般save和persist都在事务内完成,这里了解一下即可。

猜你喜欢

转载自blog.csdn.net/qq_43414199/article/details/108264611
今日推荐