hibernate_day02
1 对象状态与一级缓存
1.1 状态介绍
- hibernate 规定三种状态:瞬时态、持久态、脱管态
- 状态
- 瞬时态:transient,session没有缓存对象,数据库也没有对应记录。
- OID特点:没有值
- 持久态:persistent,session缓存对象,数据库最终会有记录。(事务没有提交)
- OID特点:有值
- 脱管态:detached,session没有缓存对象,数据库有记录。
- OID特点:有值
- 瞬时态:transient,session没有缓存对象,数据库也没有对应记录。
1.1.1 持久化类的定义
- 持久化类的定义
- 持久化:把容易丢失的数据,长期保存的过程,持久化。
- 参与这个持久化操作的类,就是持久化类。
- 持久化类的特点:一个类与数据库的表建立了映射关系。
- 持久化类= java类 + 映射文件(类名.hbm.xml文件)
- 持久化类的编写规则
- 有无参的构造方法。
- hibernate通过反射创建持久化对象
- 属性私有化,对属性提供get/set方法。
- java的规则
- 属性尽量使用包装类。
- int与Integer. 默认值不同。int:0, Integer:null。 数据库里面,0值和null值不同。
- 持久化类要有一个唯一标识OID与表的主键对应。
- 判断是否同一个java对象,使用的地址;判断是否同一个持久化对象,使用的是oid的值。(缓存的时候,体现)
- 持久化类,不要使用final修饰符。
- final修饰持久化类,导致不能被继承。load方法,使用的延迟加载机制,依赖于通过继承产生的代理对象。如果,不能被继承,延迟加载失效。
- 有无参的构造方法。
1.1.2 数据库主键的分类
- 数据库主键的分类
- 自然主键:对象本身的属性,作为表的主键
- 代理主键:不使用对象本身的属性,而使用没有任何意义的一个字段,作为主键。(推荐)
- 主键的生成方式
- 自然主键:需用户手动输入
- 代理主键:由系统自动生成。
- hibernate提供的主键生成策略
- increment:适合代理主键,适合int,long等类型,由hibernate来生成。缺陷:在多线程或者集群环境,不能使用。原因:hibnernate会进行一个查询:select max(id) from user, +1,作为新的记录的候选主键。
- identity:适合代理主键,适合int,long等类型,由底层数据库支持,要求底层数据库支持自增长类型。
- sequence:适合代理主键,适合int,long等类型,由底层数据库支持,要求底层数据库支持序列类型。
- native:适合代理主键,适合int,long等类型,根据底层数据库的特色,自动匹配。如果,底层数据库支持identity,native等价于identity; 如果,底层数据库支持sequence,native等价于sequence。(推荐)
- uuid:适合代理主键,适合字符串类型。
- assigned:适合自然主键,需要手动制定。(默认值)
1.2 转换
1.2.1 瞬时态/临时态
瞬时态称为临时态或自由态,不存在持久化表示OID(相当于主键值),与Hibernate Session 不关联.
获得:一般都只直接创建(new)
瞬时态 转换 持久态
一般操作:save方法、saveOrUpdate
瞬时态 转换 脱管态
一般操作:通过setId方法设置数据
例如:
User user = new User(); //瞬时态
user.setUid(1); //脱管态
1.2.2 持久态
- 获得:查询操作:get、loat、createQuery、createCriteria 等 获得都是持久态【】
执行save之后持久态
执行update之后持久态
- 持久态 转换 瞬时态
- 官方规定执行delete() --民间:删除态
- 持久态 转换 脱管态
- session没有记录
- session.close () 关闭
- session.clear() 清除所有
- session.evict(obj) 清除指定的PO对象
- session没有记录
1.2.3 脱管态/游离态
- 获得:创建、并设置OID
- 通过api获得
- 脱管态 转换 瞬时态
- 手动去除OID,设置成默认值
- 脱管态 转换 持久态
- 一般操作:update()、saveOrUpdate
代码:案例
@Test
public void demo01(){
User user = new User(); //瞬时态
user.setUsername("jack");
user.setPassword("1234"); //瞬时态(与oid没有关系)
Session session = factory.openSession();
session.beginTransaction();
session.save(user); //持久态
//---- 持久态就应该有持久态的行为(特性)
//user.setUsername("rose");
//持久态对象 被修改后,hibernate将自动生成update语句
//session.flush();
session.getTransaction().commit();
session.close();
System.out.println(user); //脱管态
}
2 一级缓存
2.1 介绍
- 缓存 : 作用降低应用程序直接读写永久性数据存储的频率,提高运行性能
- 物理介质 : 内存
- 存储的是数据库数据备份
- 一级缓存 : session级别的缓存.
- 当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。
2.2 一级缓存操作
2.2.1 证明一级缓存
@Test
public void demo02(){
//证明一级缓存
Session session = factory.openSession();
session.beginTransaction();
//1 查询 id = 1
User user = (User) session.get(User.class, 1);
System.out.println(user);
//2 再查询 -- 不执行select语句,将从一级缓存获得
User user2 = (User) session.get(User.class, 1);
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
2.2.2 移除
@Test
public void demo03(){
//清除缓存
Session session = factory.openSession();
session.beginTransaction();
User user = (User) session.get(User.class, 1); //--select
System.out.println(user);
//清除
//session.clear();
session.evict(user);
// 一级缓存没有缓存对象,从数据库直接查询
User user2 = (User) session.get(User.class, 1); //--select
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
2.2.3 一级缓存快照--内部结构【掌握】
- 快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。
- 如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。
2.2.4 refresh 刷新
- refresh 保证 一级缓存的数据 与 数据库的数据 保持一致。将执行select语句查询数据库,将一级缓存中的数据覆盖掉。只要执行refresh都将执行select语句。
代码:
@Test
public void demo04(){
//刷新
Session session = factory.openSession();
session.beginTransaction();
User user = (User) session.get(User.class, 1); //--select
System.out.println(user);
session.refresh(user);
session.getTransaction().commit();
session.close();
}
2.3 PO对象操作
2.3.1 save & persist
-
save方法:瞬时态 转换 持久态 ,会初始化OID
1.执行save方法,立即触发insert语句,从数据库获得主键的值(OID值) 2.执行save方法前,设置OID将忽略。 3.如果执行查询,session缓存移除了,在执行save方法,将执行insert
2.3.2 update
-
update:脱管态 转换 持久态
如果OID在数据存放的,将执行update语句 如果OID不存在将抛异常
案例:
// update: 两种方式:a.直接更新 b.先查询后更新(推荐)
@Test
public void test_update1() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 自身的业务逻辑 ---a.直接更新
User user = new User();
user.setId(5);
user.setName("刘德华");
session.update(user);
transaction.commit();
session.close();
}
@Test
public void test_update2() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 自身的业务逻辑 ----b.先查询后更新(推荐)
User user = session.get(User.class, 6);
user.setName("郭富城");
session.update(user);
transaction.commit();
session.close();
}
2.3.3 saveOrUpdate
- 代理主键:
- 判断是否有OID
- 如果没有OID,将执行insert语句
- 如果有OID,将执行update语句。
- 判断是否有OID
实例:
@Test
public void demo02(){
// 代理 native
User user = new User();
// user.setUid(2);
user.setUsername("jack2");
user.setPassword("12345");
Session session = factory.openSession();
session.beginTransaction();
session.saveOrUpdate(user);
session.getTransaction().commit();
session.close();
}
- 自然主键:
- 先执行select语句,查询是否存放
- 如果不存在,将执行insert
- 如果存在,将执行update
- 先执行select语句,查询是否存放
实例:
@Test
public void demo02(){
// 自然 assigned
User user = new User();
user.setUid(2);
user.setUsername("jack2333");
user.setPassword("12345333");
Session session = factory.openSession();
session.beginTransaction();
session.saveOrUpdate(user);
session.getTransaction().commit();
session.close();
}
2.3.4 delete
@Test
// 删除---直接指定id对象
public void test_delete(){
Session session =HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 自身的业务逻辑
User user = new User();
user.setId(5);
session.delete(user);
transaction.commit();
session.close();
}
@Test
// 删除---先通过id查询该对象,再删除
public void test_delete2(){
Session session =HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 自身的业务逻辑
User user = session.get(User.class, 6);
session.delete(user);
transaction.commit();
session.close();
}
3 事务
3.1 概念
在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的SQL 语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被回滚。
3.1.1 事务的四个特性
- ACID
- Atomic(原子性) : 事务内的所有操作不可分割,视作一个整体
- Consistency(一致性) : 事务状态保持一致
- Isolation(隔离性) : 事务与事务间是独立的,互不相干
- Durability(持久性) : 事务提交后,对数据做的操作是持久性的,在数据库中已经保存
3.2 事务的并发问题
- 脏读:一个事务读取到另一个事务未提交的数据
- 不可重复读:一个事务读取到了另一个事务已经提交的update的数据,导致在同一个事务中多次查询结果不一致
- 虚读/幻读:一个事务读取到了另一个事务已经提交的insert的数据,导致在同一个事务中多次查询结果不一致
【总结】 在多个事务同时使用相同的数据的时候,可能会出现并发问题。
3.3 事务的隔离级别
为了平衡事务并发问题的解决与数据库的效率,设计了多种隔离级别:
-
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(重复读)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 脏读 不可重复读 虚读(幻读) Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 备注: √: 可能出现 ×: 不会出现
-
每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通过数据库配置设置,也可在应用程序中设置。例如Hibernate:在hibernate.cfg.xml中设置隔离级别:
1—Read uncommitted isolation:读未提交 2—Read committed isolation:读提交 4—Repeatable read isolation:重复读 8—Serializable isolation:序列化
2 -
hibernate管理session的三种方式
通过上面的分析,肯定使用ThreadLocal将业务层的session绑定当前线程是最佳方案。其实,hibernate已经给我们准备好了。我们只需要进行一段配置即可。
- Hibernate 5 自身提供了三种管理 Session 对象的方法
- Session对象的生命周期与本地线程绑定
- Session对象的生命周期与JTA事务绑定
- Session对象的生命周期由Hibernate委托程序管理
- Hibernate 5 自身提供了三种管理 Session 对象的方法