Hibernate框架(二):主键生成策略&一级缓存&事务管理

文章目录

        • 1、持久化类的编写的规则
        • 2、主键生成策略-生成策略概述
        • 3、持久化类的三种状态-认识三种状态
        • 4、持久化类的三种状态-三种状态的转换
        • 5、Hibernate的一级缓存-一级缓存概述
        • 6、Hibernate的一级缓存-一级缓存的快照区
        • 7、Hibernate的事务管理-事务的回顾
        • 8、Hibernate的事务管理-线程绑定的Session
        • 9、Hibernate的其他API

1、持久化类的编写的规则

持久化与持久化类
 持久化:将内存中的一个对象持久化到数据库中过程。Hibernate框架就是用来进行持久化的框架。
 持久化类:一个Java对象与数据库的表建立了映射关系,那么这个类在Hibernate中称为是持久化类。
   持久化类 = Java类 + 映射文件
持久化类的编写规则
 对持久化类提供一个无参数的构造方法:Hibernate底层需要使用反射生成实例
 属性需要私有,对私有属性提供public的get和set方法:Hibernate中获取,设置对象的值
 对持久化类提供一个唯一标识OID与数据库主键对应:Java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID的属性区分是否是同一个对象
 持久化类中的属性尽量使用包装类类型:因为基本数据类型默认值是0,会产生歧义。而包装类类型(Integer、Double、Long)的默认值是null,就很好区分。
 持久化类不要使用final进行修饰:延迟加载本身是hibernate一个优化手段。返回的是一个代理对象(javaassist,可以对没有实现接口的类产生代理–使用非常底层的字节码增强技术,继承这个类进行代理)。如果不能被继承,就不能产生代理对象,延迟加载就失败,结果与get方法一致。
 这里如果大家想要知道javaassist是如何实现底层的动态代理,可以看我以前写的一篇文章:简单!5分钟帮你搞懂自定义注解及动态代理

2、主键生成策略-生成策略概述

主键的分类
 自然主键:主键的本身就是表中的一个字段(实体中的一个具体属性)
   创建一个人员表,使用身份证作为主键(唯一),这种主键就是自然主键
 代理主键:主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)
   创建一个人员表,没有使用人员中的身份证,而是用了一个与表不相关的PID,这种主键为代理主键
 实际开发中,尽量使用代理主键
   一旦自然主键参与到业务逻辑中,后期有可能修改源代码,会很麻烦
   好的程序设计满足开闭原则(Open Closed Principle),通过扩展实现变化而不是源码,也就是说不用修改主键
主键生成策略
在实际开发中,一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序设置。在Hibernate中为了减少程序编写,提供了很多种主键的生成策略(在配置文件中设置
 increment:hibernate中提供的自增机制,适用short、int、long类型的主键,在单线程中使用(非底层实现,线程不安全)
   利用select max(id) from 表,再id+1来作为下一个主键
 identity:适用short、int、long类型的主键,使用的是数据库底层的自增机制,适用于有自增机制的数据库(Mysql、MSSQL),但是Oracle没有自增机制
 sequence:适用short、int、long类型的主键,采用序列方式(Oracle)
 uuid:适用字符串类型主键,使用Hibernate中随机方式生成字符串主键
 native:本地策略,可以在identity和sequence间切换
 assigned:hibernate放弃外键管理,需要通过手动编写程序或自己设置(手动设置id)
 foreign:外部的,一对一的一种关系映射情况下使用

3、持久化类的三种状态-认识三种状态

持久化类的三种状态
Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理持久化类,将持久化类分为三种状态。持久化类 = Java类 + 映射
瞬时态
这种对象没有唯一的标识OID,没有被session管理,被称为瞬时态对象
持久态
这种对象有唯一的标识OID,被session管理,被称为持久态对象
脱管态
这种对象有唯一的标识OID,没有被session管理,被称为脱管态对象
代码演示

Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer();	 //瞬时态对象
customer.setCust_name("赖晓聪");
Serializable id = session.save(customer);	//持久态对象
transaction.commit();
session.close();
System.out.println(customer.getCust_name());  //脱管态对象

4、持久化类的三种状态-三种状态的转换

Hibernate框架(二):主键生成策略&一级缓存&事务管理_第1张图片
瞬时态对象
 获得
   Customer customer = new Customer();
 状态转换:
   瞬时 -> 持久
     save(obj)、saveOrUpdate(obj)
   瞬时 -> 脱管
     customer.setCust_id(Int id)
持久态对象
 获得
   get()、load()、iterate()
   Customer customer = session.get(Class,id)
 状态转换:
   持久 -> 瞬时
     delete()
   持久 -> 脱管
     close()、clear()、evict(obj)
脱管态对象
 获得
   Customer customer = new Customer()
   customer.setCust_id(Int id)
 状态转换:
   脱管 -> 瞬时
     customer.setCust_id(null)
   脱管 -> 持久
     update()、saveOrUpdate()
持久态对象特性
  持久态对象拥有自动更新的能力,不用update语句也能更新,前提是持久态对象
小结
  1、将方法从对象转换的角度来看,如saveOrUpdate()
  2、深入理解每个状态对象,不用图和文字就能知道怎么转换

5、Hibernate的一级缓存-一级缓存概述

什么是缓存?
  缓存:一种优化的方式,将数据存入内存中,使用的时候直接从缓存中获取,不用通过存储源
Hibernate的一级缓存
  Hibernate中提供了优化手段,缓存、抓取策略,Hibernate中提供了两种缓存机制,一级缓存和二级缓存
  Hibernate的一级缓存:称为Session级别的缓存,一级缓存生命周期与Session一致(一级缓存是由Session中的一系列的Java集合构成)。一级缓存是自带的不可卸载的(Hibernate的二级缓存是SessionFactory级别的缓存,需要配置的缓存)
  一般开发的情况下,不用二级缓存,二级缓存默认不开启,一级缓存绝对开启,二级缓存可以被Redis替代
证明Hibernate一级缓存存在
1、证明思想:
  当我们使用Hibernate保存或更新了一个对象时,Hibernate会自动将其存储到一级缓存中,当再次查询时,如果与该对象一样,那么就直接提取,不用二次发送sql语句查询。
2、代码实现:

@Test
//证明Hibernate的一级缓存是否
public void demo06(){
	Session session = HibernateUtils.openSession();
	Transaction transaction = session.beginTransaction();
	Customer customer = session.get(Customer.class, 4l);
	System.out.println(customer);
	Customer customer2 = session.get(Customer.class, 4l);
	System.out.println(customer==customer2);	//这里显示为true,地址相等
	transaction.commit();
	session.close();
}

6、Hibernate的一级缓存-一级缓存的快照区

快照区概念作用
  在Hibernate中,一级缓存和快照都是利用Map进行存储。select语句向数据库查找,数据库找到后将结果返回给Session,这时Session会对数据进行组装成实体对象,并且一式两份,即一份在缓存中,一份是快照,然后Session会将缓存中的那一份返回给程序
  首先,一级缓存区对象的内容会实时改变,和持久态对象保持一致性,但是将持久态对象的属性更改一级缓存对象的属性也改了,那怎么判断是否改变了?是否与数据库中的数据一致呢?这里就用到了快照区
  快照区就是一级缓存对象的快照,相当于一张照片,内容不会实时更改,所以在一级缓存发生更改时执行提交操作,会先与快照区进行对比,这时的快照区保存了数据库的原有内容,如果不一致则自动执行更新保存。
  快照区:entityEntryContext->entityEntry->loadedState
  一级缓存:persistenceContext->entitiesByKey->table

7、Hibernate的事务管理-事务的回顾

事务及其特性
  事务:指逻辑上的一组操作,组成这组操作的各个逻辑单元要么都成功,要么都失败
  特性:1、原子性 2、一致性 3、隔离性 4、持久性
事务引发的安全性问题
 读问题
   脏读:一个事务读到另一个事务未提交的数据
   不可重复读:一个事务读到另一个事务已提交的update数据,导致前后查询结果不一致
   虚读:一个事务读到另一个事务已经提交的insert数据,导致前后查询结果不一致
 写问题
   引发两类丢失更新
事务安全性问题的解决
 设置事务隔离级别
   Read uncommitted:以上问题都会发生
   Read committed:只解决脏读
   Repeatble read:解决脏读和不可重复读
   Serializable:解决所有问题
 在核心配置文件中进行配置

<!--
	设置事务隔离级别:hibernate.connection.isolation
	1  Read Uncommited 可读未提交
	2  Read Commited 可读已提交
	4  Repeatable Read 可重复读
	8  Serializable 串行化
 -->
<property name="hibernate.connection.isolation">4</property>

8、Hibernate的事务管理-线程绑定的Session

web事务逻辑图
Hibernate框架(二):主键生成策略&一级缓存&事务管理_第2张图片
 必须保证连接对象是同一个
   1、向下传递 DBUtils
   2、使用ThreadLocal对象
     将这个对象绑定到当前线程中
     在Dao的方法中,通过当前的线程获得到连接对象
 Hibernate框架内部都已经绑定好了ThreadLocal
   在SessionFactory中提供了一个方法getCurrentSession();
   但ThreadLocal不能使用,必须要通过配置
配置核心文件与扩展Utils

//核心配置文件中
<!-- 
	配置当前线程绑定的session
	在 Hibernate 的配置文件中,hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括
    ① thread: Session 对象的生命周期与本地线程绑定
    ② jta*: Session 对象的生命周期与 JTA 事务绑定
    ③ managed: Hibernate 委托程序来管理 Session 对象的生命周期
-->
<property name="hibernate.current_session_context_class">thread</property>
//工具类中
public static Session getCurrentSession(){
	return sf.getCurrentSession();	//前提是要配置
}

使用线程绑定的Session时,会自动将Session关闭,因为线程在使用完之后会自动关闭,顺带Session也会关闭,不用手动关闭。
Hibernate的本地线程绑定详文:Session 对象的生命周期与本地线程绑定

9、Hibernate的其他API

Query
 Query接口用于接收HQL,查询对象
   HQL:Hibernate Query Language,与SQL语法类似,面向对象
 Query的使用:

public void demo08(){
	Session session = HibernateUtils.getCurrentSession();
	Transaction transaction = session.beginTransaction();
	String hql = null;	//hql语句
	//普通查询
	hql = "from Customer";//里面写HQL,默认查询所有
	//条件查询 - 这里的语句是写对象属性名不是列名
	//hql = "from Customer where cust_name like ?";
	//设置条件
	//query.setParameter(0, "赖%");setParameter与setObject类似,index从0开始
	//分页查询
	Query query = session.createQuery(hql);
	query.setFirstResult(0);//代表limit第一个参数
	query.setMaxResults(3);//代表limist第二个参数
	List<Customer> list = query.list();//获取查询结果
	for (Customer customer : list) {
		System.out.println(customer);
	}
	transaction.commit();
}

Criteria
 Criteria:QBC(Query By Criteria)
   更加面向对象的一种查询方式

public void demo08(){
	Session session = HibernateUtils.getCurrentSession();
	Transaction transaction = session.beginTransaction();
	Criteria criteria = session.createCriteria(Customer.class);
	//条件查询
	criteria.add(Restrictions.like("cust_name", "聪",MatchMode.END));//也可以直接"%聪"
	//分页查询
	criteria.setFirstResult(0);
	criteria.setMaxResults(3);
	List<Customer> list = criteria.list();
	for (Customer customer : list) {
		System.out.println(customer);
	}
	transaction.commit();
}

SQLQuery
Hibernate框架(二):主键生成策略&一级缓存&事务管理_第3张图片
小结
  1、SQLQuery一般用于处理复杂的情况,比多很多个表关联时
  2、一般用其他两个就可以
  3、criteria与query效率都较低,criteria效率略高与query,因为query还要解析hql语句

你可能感兴趣的:(Hibernate)