SSH框架之Hibernate进阶持久化对象及事务管理(二)
第一节: 持久化类的编写规则
1.1 什么是持久化类
- 持久化:将内存中的一个对象持久化到数据库中的过程,Hibernate是持久层的ORM映射框架。
- 持久化类:一个java对象与数据库表建立了映射关系,则该类被称为持久化类。
1.2 持久化类的编写规则
- ①对持久化类提供一个无参数的构造方法 :Hibernate底层需要使用反射生成实例;
- ②属性需要私有,对私有属性提供public的get和set方法:Hibernate中获取对象值;
- ③对持久化类提供一个唯一标识OID与数据库主键对应:Hibernate中通过持久化类的OID的属性来区分是否是同一个对象。
- ④持久化类中的属性尽量使用包装类型类来定义:基本数据类型的默认值为0,易引起歧义。
- ⑤持久化类不要使用final进行修饰:延迟加载本身是Hibernate一个优化的手段,返回的是一个代理对象(对没有实现接口的类产生代理,javassist使用了非常底层的字节码增强技术,集成这个类进行代理)如果该类不能被继承,则不能产生代理对象,则延迟加载也就失效了。
1.3 Hibernate中主键生成策略
-
1.3.1 主键的分类
自然主键:主键的本身就是表中的一个字段(实体中的一个具体属性);
代理主键:主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)。
在实际的开发中,尽量使用代理主键,满足OCP原则(对程序的扩展是open的,对源代码的修改是close) -
1.3.2 主键的生成策略
在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库。在Hibernate中提供了多种主键的生成策略。如下:
<id name="cust_id" column="cust_id">
<!--主键生成策略-->
<!--
* increment:Hibernate中提供的自动增长的机制,适用于long,short或int类型的主键。在单线程中使用,
在集群下不要使用(存在线程安全的问题);
* identity:适用于short,int,long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增
长机制的数据库(MySql,MSSQL),Oracle不适用。
* sequence:适用于short,int,long类型的主键,采用的是序列的方式。Oracle支持。
* uuid:适用于字符串类型的主键,使用hibernate中的随机方式生成字符串主键。
* native:本地策略,可以在identity和sequence之间自动切换。
* assigned:hibernate放弃主键的管理,需要用户自行设定
* foreigen:使用另外一个相关联的对象的标识符。它通常和 <one-to-one> 联合起来使用。
-->
<generator class="native"/>
</id>
第二节:持久化类的三种状态
Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理之间化类,将持久化类分成三种状态。
- 瞬时态(transient)
瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建,开辟内存空间的对象,不存在持久化标识OID(相对于主键值),尚未与Hibernate session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是gu孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。 - 持久态(presistent)
持久态的对象拥有持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。(持久态的对象可以自动更新数据库) - 脱管态(detached)
脱管态也称离线态或者游离态,当某个持久化状态的实例与session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
2.1 如何区分这三种状态?
瞬时态对象,没有唯一标识OID,没有被session管理。
持久态对象,拥有唯一标识OID且被session管理。
脱管态对象,拥有唯一标识OID,但没有被session管理。
2.2 持久化对象的三种状态转换
image.png第三节 Hibernate的一级缓存
缓存是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提供应用的运行性能。缓存汇总的数据是数据存储源中数据的拷贝。缓存的物理介子通常是内存。
Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据库数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。
3.1 什么是Hibernate的一级缓存
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的Java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配的OID值的对象,就直接将该对象从一级缓存中取出使用。不会再查询数据库。如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为Session基本的缓存。
3.2 Hibernate的一级缓存特点:
①当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动把从数据库中查询到的相应对象信息加入到一级缓存中去。
②当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
③当调用session的close()方法时,Session缓存会被清空。
3.3 Hibernate一级缓存的特殊区域(快照区)
image.png第四节 Hibernate的事务管理
4.1 数据库事务的回顾
-
4.1.1 什么是事务:
事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元执行时要么全部成功,要么全部失败; -
4.1.2 事务的特性:
原子性:代表事务不可分割
一致性:代表事务执行前后,数据完整性保持一致
隔离性:代表一个事务的执行过程中,不应该受到其他事务的干扰
持久性:代表事务执行完成后,数据就持久化保持中数据库中 -
4.1.3 如果不考虑事务的隔离性,会引发那些问题:
①读问题- 脏读:一个事务读到另一个事务未提交的数据
- 不可重复读:一个事务读到另一个事务已经提交update的数据,导致在前一事务中多次查询结果不一致
- 虚读:一个事务读到另一个事务已经插入insert数据,导致在前一个事务多次查询结果不一致
②写问题(了解)
- 引发两类丢失更新问题。
-
4.1.4 读问题的解决方法
设置事务的隔离级别:- Read uncommitted:以上读问题都会发生
- Read committed:解决脏读,不可重复读和虚读有可能发生
- Repeatable read:解决脏读和不可重复读,虚读有可能发生
- Serializable:解决所有读的问题
4.2 Hibernate中事务隔离级别的设置
在hibernate配置文件中配置如下属性:
<!--设置事务的隔离级别-->
<!--
隔离级别取值对应关系:
1 : Read uncommitted isolation
2 : Read committed isolation
4 : Repeatable read isolation
8 : Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
4.3 Service层事务管理
Service业务层的复杂业务对DAO层的调用必须保证连接对象是同一个,通常的解决方案有:
①:向下传递
②:使用ThreadLocal对象进行绑定,将连接对象绑定到当前线程中,在DAO的方法调用中,通过当前线程来获取连接对象。
4.5 Hibernate中针对Service层事务的管理方法
Hibernate框架内部已经绑定了ThreadLocal,在SessionFactory中提供了一个方法getCurrentSession()来获取连接对象。但这个方法的调用前提需修改Hibernate的属性,如下
<!--配置Hibernate对Session事务的管理方式-->
<!--
用于指定session管理方式
* thread : Session对象的生命周期与本地线程绑定。
* jta:Session对象的生命周期与JTA事务绑定
* managed: Hibernate委托程序来管理Session对象的生命周期
-->
<property name="hibernate.current_session_context_class">thread</property>
则session连接对象的获取方式:该连接对象在执行完成后无需手动关闭。
/**
* currentSission对象的获取
* @return
*/
public static Session getCurrentSission(){
return sessionFactory.getCurrentSession();
}
第五节:Hibernate中的查询对象简介
5.1 Query
Query代表面向对象的一个Hibernate查询操作。在Hibernate中,通常使用session.createQuery()方法接受一个HQL语句,然后调用Query的list()或uniqueResult()方法执行查询。所谓的HQL是Hibernate Query Language缩写,其语法很像SQL语法,但它是完全面向对象的。
在Hibernate中使用Query对象的步骤:
- 获得Hibernate的Session对象
- 编写HQL语句
- 调用session.createQuery()创建查询对象
- 如果HQL语句包含参数,则调用Query的setXxx设置参数
- 调用Query对象的list()或uniqueResult()方法执行查询
@Test
public void findByQuery(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//查询所有人员数据
// String hql = "from Customer";
//条件查询(使用命名参数的方式)
// String hql = "SELECT c from Customer c where c.cust_name like :name ";
//分页查询
String hql = "from Customer";
Query query = currentSission.createQuery(hql);
//插入检索条件
// query.setParameter("name","小%");
//插入分页查询条件
query.setFirstResult(0);//从第i条开始
query.setMaxResults(1);//每页展示的数据为0条
List<Customer> list = query.list();
for (Customer customer: list) {
System.out.println(customer.toString());
}
transaction.commit();
}
5.2 criteria
Query by Criteria 是更加面向对象的一种查询方式。
@Test
public void findByCriteria(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//通过criteria进行查询
// Criteria criteria = currentSission.createCriteria(Customer.class);已过时
//1.import javax.persistence.criteria.CriteriaQuery;获取Criteria对象
CriteriaQuery<Customer> query = currentSission.getCriteriaBuilder().createQuery(Customer.class);
//2.指定根条件
query.from(Customer.class);
//3.执行查询
// List<Customer> resultList = currentSission.createQuery(query).getResultList();
//3.1 执行分页查询
List<Customer> resultList = currentSission.createQuery(query)
.setFirstResult(0)//设置开始位置
.setMaxResults(1)//设置每页条数
.getResultList();
for (Customer customer: resultList) {
System.out.println(customer.toString());
}
transaction.commit();
}
5.3 SQL Query
SQLQuery用于结束sql,特别复杂的情况下使用SQL。