持久化与持久化类
持久化:将内存中的一个对象持久化到数据库中过程。Hibernate框架就是用来进行持久化的框架。
持久化类:一个Java对象与数据库的表建立了映射关系,那么这个类在Hibernate中称为是持久化类。
持久化类 = Java类 + 映射文件
持久化类的编写规则
对持久化类提供一个无参数的构造方法:Hibernate底层需要使用反射生成实例
属性需要私有,对私有属性提供public的get和set方法:Hibernate中获取,设置对象的值
对持久化类提供一个唯一标识OID与数据库主键对应:Java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID的属性区分是否是同一个对象
持久化类中的属性尽量使用包装类类型:因为基本数据类型默认值是0,会产生歧义。而包装类类型(Integer、Double、Long)的默认值是null,就很好区分。
持久化类不要使用final进行修饰:延迟加载本身是hibernate一个优化手段。返回的是一个代理对象(javaassist,可以对没有实现接口的类产生代理–使用非常底层的字节码增强技术,继承这个类进行代理)。如果不能被继承,就不能产生代理对象,延迟加载就失败,结果与get方法一致。
这里如果大家想要知道javaassist是如何实现底层的动态代理,可以看我以前写的一篇文章:简单!5分钟帮你搞懂自定义注解及动态代理
主键的分类
自然主键:主键的本身就是表中的一个字段(实体中的一个具体属性)
创建一个人员表,使用身份证作为主键(唯一),这种主键就是自然主键
代理主键:主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)
创建一个人员表,没有使用人员中的身份证,而是用了一个与表不相关的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:外部的,一对一的一种关系映射情况下使用
持久化类的三种状态
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()); //脱管态对象
瞬时态对象
获得
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、深入理解每个状态对象,不用图和文字就能知道怎么转换
什么是缓存?
缓存:一种优化的方式,将数据存入内存中,使用的时候直接从缓存中获取,不用通过存储源
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();
}
快照区概念作用
在Hibernate中,一级缓存和快照都是利用Map进行存储。select语句向数据库查找,数据库找到后将结果返回给Session,这时Session会对数据进行组装成实体对象,并且一式两份,即一份在缓存中,一份是快照,然后Session会将缓存中的那一份返回给程序
首先,一级缓存区对象的内容会实时改变,和持久态对象保持一致性,但是将持久态对象的属性更改一级缓存对象的属性也改了,那怎么判断是否改变了?是否与数据库中的数据一致呢?这里就用到了快照区
快照区就是一级缓存对象的快照,相当于一张照片,内容不会实时更改,所以在一级缓存发生更改时执行提交操作,会先与快照区进行对比,这时的快照区保存了数据库的原有内容,如果不一致则自动执行更新保存。
快照区:entityEntryContext->entityEntry->loadedState
一级缓存:persistenceContext->entitiesByKey->table
事务及其特性
事务:指逻辑上的一组操作,组成这组操作的各个逻辑单元要么都成功,要么都失败
特性: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>
web事务逻辑图
必须保证连接对象是同一个
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 对象的生命周期与本地线程绑定
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
小结
1、SQLQuery一般用于处理复杂的情况,比多很多个表关联时
2、一般用其他两个就可以
3、criteria与query效率都较低,criteria效率略高与query,因为query还要解析hql语句