第一节 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
- 执行save方法,立即触发insert语句,从数据库获得主键的值(OID值)。
- 执行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都是持久化对象的作用
- persist保存的对象,在保存前,不能设置id,否则会报错
detached 游离态:session没有缓存,数据库有记录,oid有值
- 注释后,成功运行
- save 因为需要返回一个主键值,因此会立即执行 insert 语句,而 persist 在事务外部调用时则不会立即执行 insert 语句,在事务内调用还是会立即执行 insert 语句的。
- 注释事务就在事务外了,persist不会执行sql,也不会保存数据到数据库,在事务内会立即执行insert
- save方法不加事务也可以立即执行insert,把数据保存到数据库
一般save和persist都在事务内完成,这里了解一下即可。