一级缓存
一级缓存
在EntityManager中存在一个一级缓存区域,称之为一级缓存;
每次查询时会把查询结果保存到一级缓存区中,下次再次查询时会根据主键ID判断是否缓存中有值,有则直接取出。
EntityManager 是线程不安全的,每次请求都会是一个全新的对象,一级缓存只存在于这个EntityManager中,缓存是有限的
缓存清空
在EntityManager提交事务或关闭之后,一级缓存会被清空。可以手动清空某一个缓存或所有缓存信息。
- detach 清除一级缓存中指定的对象
- clear 清除一级缓存中所有的对象
案例 多次查询时查看打印SQL
/**
* 保存实体到数据库表
*/
@Before
public void save(){
User u=new User();
u.setName("jake");
u.setBirthday(new Date());
EntityManager entityManager = JpaUtil.getEntityManager();
//开启事务
entityManager.getTransaction().begin();
//执行
entityManager.persist(u);
//关闭事务
entityManager.getTransaction().commit();
//归还连接
entityManager.close();
}
@Test
public void queryFromCache(){
EntityManager entityManager = JpaUtil.getEntityManager();
//1: 多次查询查看SQL打印只有1个,说明第二次查询直接从一级缓存获取数据
User user = entityManager.find(User.class, 1L);
User user1 = entityManager.find(User.class, 1L);
System.out.println("user.getName() = " + user.getName());
System.out.println("user.getName() = " + user1.getName());
//2: 从另外一个EntityManager查询,也会打印SQL,一级缓存只存在于EntityManager中
EntityManager otherEntity = JpaUtil.getEntityManager();
User user2 = otherEntity.find(User.class, 1L);
System.out.println("user.getName() = " + user2.getName());
otherEntity.close();
//3:手动清除缓存
//清除缓存中某一个对象
// entityManager.detach(user);
//清除缓存中所有对象
entityManager.clear();
User user3 = entityManager.find(User.class, 1L);
System.out.println("user.getName() = " + user3.getName());
entityManager.close();
}
延迟加载 懒加载
find()查询时,会立即执行SQL语句,不管后续是否用到这个查询结果了。
getReference
查询时,不会立即执行SQL语句,只有真正使用到这个对象时,才会打印SQL语句到数据库查询。
懒加载的实现原理:JPA对查询结果对象进行了动态代理,重写所有Getter方法,当调用对象的Getter方法时,会触发SQL的执行
@Test
public void queryLazy(){
EntityManager entityManager = JpaUtil.getEntityManager();
//find 查询会立即执行SQL,不管这个user对象后续是否使用都会和数据库有一次查询
// User user = entityManager.find(User.class, 1L);
//getReference是个懒加载,后续如果没有地方使用,是不会去数据库查询的
User reference = entityManager.getReference(User.class, 1L);
System.out.println("reference = " + reference.getName());
entityManager.close();
}
对象状态
JPA对象状态分为4种,对对象的保存、修改、删除等操作都是对象状态的改变,不同的改变执行不同的SQL。从而把状态改变的操作转换成数据库的CRUD 操作。
- 瞬时状态 Transisent 使用new 创建新对象,没有OID,一级缓存不存在
- 持久状态 Persistent 调用persist方法后,对象保存到数据库,一级缓存中同样有
- 游离状态 Detached 对象存在数据库中,但不在一级缓存中
- 删除状态 Removed 事务已提交,就会删除,持久状态和被删除之间的临界状态
| 状态 | 是否在一级缓存中 | 是否有OID
| 瞬时状态 | 否 | 否
| 持久状态 | 是 | 是
| 游离状态 | 否 | 是
| 删除状态 | 是 | 是
对象状态改变
- persist 将创建的对象或删除的对象变为Persistent状态,数据存到数据库和1级缓存中
- remove 删除持久状态对象
- merge 将游离实体转变为Persistent 状态,存到数据库
- 如果使用了事务管理,则事务在 commit/rollback 也会改变对象状态。对象状态变化或对象依赖的对象状态变化,都会导致对象的更新操作
案例分析 控制台为什么会有update 语句?
@Test
public void changeState(){
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//查询对象后,只是修改属性,没有调用merge方法,name属性怎么会更新到数据库,有update SQL打印出来
User user = entityManager.find(User.class, 1L);
user.setName("haha");
entityManager.getTransaction().commit();
entityManager.close();
}
为什么会出现update语句?
Hibernate: select user0_.id as id1_0_0_, user0_.birthday as birthday2_0_0_, user0_.name as name3_0_0_ from User user0_ where user0_.id=?
Hibernate: update User set birthday=?, name=? where id=?
分析:
将数据从数据库查询出来后,EntityManager的一级缓存存储一份,EntityManager的另一个快照区也存储了一份;在提交事务的时候,会清理一级缓存,此时会对2份数据对比是否一致,如果不一致会把缓存中的数据(脏数据)更新到数据库中,变成持久化状态。user.setName("haha");
这个语句会把对象状态从持久状态变成游离状态,状态发生改变,需要把游离状态再次更新到数据库(持久状态)。这也就是为什么会出现Update语句的原因