Hibernate5一级缓存Session和对象的状态

标签: Hibernate5  Session

一、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);	//[email protected]
			}
		});
	}

 

 

 

 

 

原文链接:加载失败,请重新获取