Hibernate知识点复习之二
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate知识点复习之二相关的知识,希望对你有一定的参考价值。
Hibernate学习之二
一 Hibernate持久化类
1 什么是持久化类?
Hibernate是持久层ORM映射框架, 专注于数据的持久化 ,所谓的持久化 就是将内存中的数据永久存储到关系型数据库的过程。
而持久化类就是 通过映射文件与数据库建立起映射关系的Java类。
2 持久化类的编写规则:
1. 需提供无参构造方法。(Hibernate底层是通过反射技术生成持久类的实例)
2. 类属性需私有化, 提供私有的getter和setter方法。(Hibernate底层将查询到数据通过get/set方法进行封装处理)
3.类属性类型使用包装类。(包装类的语义描述更清晰,如:Integer中 null 和 0 代表两种情况,而这两种情况 int 只能使用 0 来表示)
4.持久化类要有一个唯一标识(oid)与表主键对应。(Hibernate需通过此唯一标识oid来区分内存中是否是同一个持久化对象,Hibernate不允许存在同oid的两个持久化对象)
5.持久化类尽量不使用final修饰。(Hibernate的延迟加载机制 是通过字节码增强技术来产生一个代理对象,通过产生当前类的一个子类对象实现,
若final修饰,不能产生子类,也就不能产生代理对象, Hibernate的延迟加载策略(一种优化手段)就会失效。
3 Hibernate的主键生成策略
1.主键类型
#自然主键:把具业务含义的字段作为主键,如customer表中的 name作主键
#代理主键:把不具业务含义的字段作为主键,该字段一般取名为id,通常为整数类型(比字符串类型节省更多空间)
2.主键生成策略 <generator class="主键生成策略属性"></generator>
#assigned:自然主键策略,需开发人员手动录入指定主键,若id不指定generator属性,Hibernate默认使用这种主键生成策略。
#increment:代理主键策略,整型数据主键自增,增量为1,存在线程安全问题,不适用多线程操作同一张表的情况,不能在集群环境下使用
#uuid:代理主键策略,产生随机字符串作为主键,主键类型必须为String ,Hibernate采用128位的UUID算法来生成(长度为32位十六进制的)唯一的字符串标识符
#identity:代理主键策略,采用底层数据库本身提供的主键生成标识符,该生成器要求数据库中把主键定义为自增长类型
#sequence:代理主键策略,根据底层数据库序列生成标识符,该数据库需支持序列(Oracle的主键生成策略)
#native:代理主键策略,根据底层数据库 自动生成标识符能力来选中 identity,sequence,hilo(高低算法生成器) 三种生成器中的一种,适合跨数据库平台开发
4 Hibernate的持久化对象的三种状态
1 概述:Hibernate为了更好管理持久化类,特将持久化类分三种状态:瞬时态,持久态,托管态
2 瞬时态(transient):不存在持久化标识oid,尚未与Hibernate的Session关联的持久化对象。
细述:瞬时态也称临时态或自由态,瞬时态的实例由new 命令创建,在内存开辟空间的孤立对象,与数据库无关联。
3 持久态(persistent):存在持久化标识oid,加入到Session缓存中,且相关联的Session没有关闭(执行close()方法),持久化状态对象的任何变化都会自动同步到数据库中
如:存在持久态对象c,执行方法c.setCust_name("微软公司")后 不需调用Session.update(c) 即可自动把数据更新到数据库中
细述:值得一提的是,持久化对象是在事务提交前变成持久态的。Session.save()方法就是 瞬时态 转换为 持久态的 过程
4. 托管态(detached):失去Session关联,仍然存在oid,与数据库存在关联
细述:托管态也称游离态或离线态,当某个持久化状态的对象与Session关联被关闭了就会变成托管态,托管态对象发生改变时Hibernate不能检测到
Session.close()方法就是 持久态 转换为 托管态的过程
5. 三种状态相互转换
#瞬时态
*转 持久态:save()或saveOupdate
*转 托管态:为瞬时态对象设置oid
#持久态
*转 瞬时态:delete()
*转 托管态:close()
#托管态
*转 瞬时态:将对象的oid设为null
*转 持久态:update()或 saveOrUpdate()
二 Hibernate的一级缓存
@概述:持久化对象可以自动更新数据库依赖于Hibernate的一级缓存
@什么是Hibernate的一级缓存?
#缓存概念:缓存介于应用程序和永久性数据存储源(硬盘,数据库)之间,其作用是降低应用程序直接读取永久性数据存储源的频率,从而提高应用的运行性能
缓存中的数据是永久性数据存储源中数据的拷贝,它的物理介质通常是内存
#Hibernate缓存:Hibernate缓存分为一级缓存和二级缓存,Hibernate这两级缓存都位于持久层,存储的都是数据库备份数据,
其中Hibernate一级缓存为内置缓存,不能被卸载
#Hibernate一级缓存:
*概念:Hibernate的一级缓存就是指Session缓存(一块内存空间),用来存放相互管理的java对象。Session接口的实现包含了一系列的Java集合,这些java集合
构成了Session缓存,只要Session实例没有结束生命周期,存放在它缓存中的对象就不会结束生命周期,因此一级缓存也被称为是Session基本的缓存
*持久化对象自动更新数据库的原理:
在使用Hibernate查询对象时,会先使用对象的oid属性在Hibernate的一级缓存中(Session缓存)进行查找,如果找到匹配oid值的对象,
则直接从一级缓存中取出,不会再查询数据库。若没有找到匹配oid值的对象,则去数据库中查找相应的数据,从数据库中查询到的数据也会
放进一级缓存中。
*Hibernate的一级缓存作用:减少对数据库访问的次数
*Hibernate一级缓存的特点:
&当应用程序调用Session接口的save(),update(),saveOrUpdate(),如果Session缓存中没有相应的对象,Hibernate就会自动进入数据库查询,
将查询到相应的对象信息加入到一级缓存中
&当调用Session接口的load(),get()方法,以及Query接口的 list(),iterator() 方法时,先判断Session缓存中有无对象,有则返回,没有则
数据库查询,并将查询结果存进Session缓存中。
&当调用Session的close()方法,Session缓存会被清空。
*Hibernate一级缓存是否存在示例:
//第一次获取id为1的Customer对象,Session缓存中没有,会进行数据库查询操作,打印出sql查询语句,并存进Session缓存中
Customer c1 = session.get(Customer.class,1L);
System.out.println(c1);
System.out.println("---------------------------------------");
//第二次获取同一个id为1的Customer对象,此时其已存在Session缓存中,不会进行数据库查询操作,不会打印出sql查询语句
Customer c2=session.get(Customer.class,1L);
System.out.println(c2);
System.out.println(c1==c2); //返回true,获取的是同一个对象
*Hibernate一级缓存的快照:
&概述:一级缓存之所以可以更新数据库,依赖于一级缓存中的快照区域
&底层原理:Hibernate向一级缓存存数据时,同时会拷贝一份到一级缓存中的快照区里。当执行事务提交commit()方法时,Hibernate会
比对一级缓存中的数据和内部的快照区数据是否一致,若不一致则底层自动update操作,把新数据同步更新到数据库中,
若一致则不更新。
&作用:确保一级缓存中 的数据与数据库的一致
*Hibernate一级缓存和一级缓存中的快照区职能总结:
&一级缓存主要应用于 Session进行CRUD中查取操作时,减少对数据库的访问 (查取操作)
&快照区则为了保证 一级缓存区的数据与数据库一致,而对数据库进行数据更新 (更新操作)
三 Hibernate的事务控制
1. 什么是事务?
#概述:在数据库操作中,一项事务是由一条或多条操作数据库的sql语句组成的不可分割的工作单元。当事务中所有sql操作都正常时,整个事务才可被提交到数据库中,
若有一项操作没有完成,则整个事务会被回滚。
#归纳理解:事务可以理解为逻辑上的一组操作,组成这组操作的各个单元,要么一起成功,要么一起失败
#事务的ACID特性:
&原子性(Atomic):将事务中所有操作捆绑成一个不可分割的单元,不可分割在于,事务的所有操作要么全部执行要么全部不执行
&一致性(Consistency):事务完成后,所有数据都保持一致状态
&隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
&持久性(Durablility):也称永久性,指一个事务一旦提交,它对数据库中数据的改变是永久性的,完成提交后,产生的其他操作或故障不会对其影响。
2. 事务中的并发问题:
#概述:实际开发中,数据库是要被多个用户访问的。多个事务访问同一个数据库时会发生并发问题。
#事务并发产生的问题:
& 脏读(Dirty Read):一个事务读取到另一个事务中未提交的数据
如:事务T1修改了一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,
之后事务T1因为某种原因Rollback了,那么事务T2读取的数据就是脏的
& 不可重复读(None-Repeatable Read):一个事务中两次读同一行数据,可两次读到的数据内容不一致
如:事务T1读取某一数据,事务T2读取并修改(update)了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
& 虚读/幻读:事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据
如:事务T1在查询某数据库的数据时,T2在该数据库插入(insert)或删除(delete)了一条记录,事务T1查完第一次,想确认查询第二遍时发现前后数据数量不一致。
& 更新丢失(Update Lost):两个事务都同时更新(update)一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。
这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
#为解决事务并发问题而定义的4个事务隔离级别:
&读未提交(Read Uncommitted,1级):
一个事务可以访问另一个事务未成功提交的修改(update)和插入(insert)数据,但当一个事务在写数据时,另一个事务不可也进行写操作,只允许读此行数据。
读事务不阻塞其他读事务和写事务,未提交的写事务阻塞其他写事务但不阻塞读事务。
此隔离级别可以防止更新丢失,但不能防止脏读、不可重复读、幻读。
& 读已提交(Read Committed,2级,oracle默认的):
一个事务可以访问另一个事务成功提交的修改(update)和插入(insert)数据,但未提交的写事务,禁止其他事务访问该行。
读事务允许其他读事务和写事务,未提交的正在进行的写事务禁止其他读事务和写事务
此隔离级别可以防止更新丢失、脏读,但不能防止不可重复读、幻读。
& 可重复读(Repeatable Read,4级,mysql默认的):
以操作同一行数据为前提,读事务禁止其他写事务但不阻塞读事务,未提交的写事务禁止其他读事务和写事务。
此隔离级别可以防止更新丢失、脏读、不可重复读,但不能防止幻读
& 序列化/串行化(Serializable,8级):
提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。(相当于锁表,性能差没人用)
此隔离级别可以防止更新丢失、脏读、不可重复读、幻读。
& 总结:隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,
可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免更新丢失、脏读,而且具有较好的并发性能。
尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
& 配置文件对事务中隔离级别进行配置:
隔离级别为:
0001 1 第一级别(读未提交)
0010 2 第二级别(读已提交)
0100 4 第三级别(读可重复)(mysql数据库为4)
1000 8 第四级别(串行化)
<property name="hibernate.connection.isolation">4</property>
四 Hibernate的事务管理
1. 如何保证在Service层开启事务时使用的Session对象,与在dao层进行多个操作时使用的是同一个Session对象?
# 在业务层获取到Session后,将Session作为参数传递给DAO
# 使用ThreadLocal将业务层获取到Session绑定到当前线程中,从而,dao层获取的Session都是从当前线程。具体实现Hibernate帮助我们完成,只需进行配置
& Hibernate提供三种管理Session对象的方法:
1 Session对象的生命周期与本地线程绑定(这是调用sessionFactory.getCurrentSession()方法,获取与当前线程绑定Session必备的配置)
<property name="hibernate.current_session_context_class">thread</property>
2 Session对象的生命周期与JTA事务绑定
<property name="hibernate.current_session_context_class">jta</property>
3 Hibernate委托程序来管理Session对象的生命周期
<property name="hibernate.current_session_context_class">managed</property>
五 Hibernate的Query对象
1 Query对象(代表面向对象的一个Hibernate查询操作)
概述:在Hibernate中,通常使用session.createQuery()方法接收一个hql语句,获取一个Query对象,然后调用Query对象的list()或uniqueResult()方法进行查询,
hql(Hibernate Query Language)语句,语法很像sql,但它是全面向对象的,如果HQL语句含有参数,则可调用Query的setXxx()设置参数
2 Query对象的常用API查询示例:
# 基本查询:
String hql="from Customer"; //书写hql语句
Query query = session.createQuery(hql); //创建查询对象
List<Customer> list = query.list(); //执行查询
#条件查询:
String hql="from Customer where cust_id = 1L"; //书写hql语句
Query query = session.createQuery(hql); //创建查询对象
Customer uniqueResult = (Customer) query.uniqueResult();//执行查询
#占位符?的使用
String hql="from Customer where cust_id = ?"; //书写hql语句
Query query = session.createQuery(hql); //创建查询对象
//query.setLong(0, 1L);
query.setParameter(0,1L);//此方法会自动帮你转化处于占位符中的数据类型
Customer uniqueResult = (Customer) query.uniqueResult();//执行查询
#命名占位符 =: 的使用
String hql="from Customer where cust_id=:myId"; //书写hql语句--设置命名占位符
Query query = session.createQuery(hql); //创建Query对象
query.setParameter("myId",1L); //为命名占位符设置参数
Customer uniqueResult = (Customer) query.uniqueResult(); //执行查询
# 分页查询:
String hql="from Customer";
Query query = session.createQuery(hql);
//limit 0,5
query.setFirstResult(0); //从0条记录开始查询
query.setMaxResults(5); //查询到第5条
List<Customer> list = query.list();
3 Query的其他API
# setter方法(如:setParameter("","");setLong("",""))
提供一系列的setter方法针对不同数据类型设置 查询参数
#iterator()
该方法用于查询语句,迭代读取时按照顺序读取,把使用到数据转换到java对象
# executeUpdate()
Hbernate3 新特性,它支持hql的更新和删除操作
六 Hibernate的Criteria对象
1 概述:
Criteria是一个完全不需考虑数据库底层实现,sql的编写,完全面向对象,可扩展的条件查询API.它是Hibernate框架的核心查询对象。
Criteria查询 也称QBC(Query By Criteria )查询,它是Hibernate的一种对象检索方式。
2 org.hibernate.criterion接口:
Criterion是Hibernate一个提供面向对象查询条件的 接口,Cirterion接口的一个实例就是一个单独的查询条件,而Criterion实例是由工厂类Restrictions的静态方法完成。
Criteria对象需通过add()方法添加Criterion实例查询条件,从而创建查询。
3 Criteria条件查询sql操作符对应的方法:
* > gt()
* >= ge()
* < lt()
* <= le()
* == eq()
* != ne()
* in in()
* between and between()
* like like()
* is not null isNotNull()
* is null isNull()
* or or()
* and and()
4 Criteria的QBC查询API:
#基本查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//执行QBC查询
List<Customer> list = criteria.list();
#条件查询
//创建查询
Criteria criteria = session.createCriteria(Customer.class);
//设置查询条件
criteria.add(Restrictions.eq("cust_id",1L));
//执行查询
Customer c = (Customer) criteria.uniqueResult();
#分页查询
//创建查询对象
Criteria criteria = s.createCriteria(Customer.class);
//设置查询条件
criteria.setFirstResult(0);
criteria.setMaxResults(5);
//执行查询
List<Customer> list = criteria.list();
#查询总记录
//创建查询对象
Criteria criteria = s.createCriteria(Customer.class);
//设置查询条件
criteria.setProjection(Projections.rowCount());
//执行查询
Long uniqueResult = (Long) criteria.uniqueResult();
七 Hibernate的SQLCriteria对象
1 概述:SQLCriteria接口用于接收sql原生语句进行查询,然后调用list()或 uniqueResult()方法获取结果
但sql语句不会手动封装到实体,需要我们手动调用 addEnity()进行封装
2 SQLCirteria查询示例:
//书写原生sql语句
String sql="select * from cst_customer";
//创建查询对象
SQLQuery sqlQuery = s.createSQLQuery(sql);
//3 获取查询结果
//没使用手动封装实体的方式:
/*List<Object[]> list = sqlQuery.list();
for(Object[]objs:list) {
System.out.println(Arrays.toString(objs));
}*/
//使用手动封装实体方式:
sqlQuery.addEntity(Customer.class);
//执行查询
List<Customer> list = sqlQuery.list();
八 Hibernate的Query,Criteria,SQlCriteria三大API对象总结:
Query对象:书写的是hql语句,然后放进 session.createQuery(hql)方法创建查询对象,由setter方法设置查询条件,list()和uniqueResult()执行查询获取结果
Criteria对象:不需书写hql语句,使用 session.createCriteria(xxx.class)关联对应实体,由QBC查询方法设置查询条件,list()和uniqueResult()执行查询获取结果
SQLQuery对象:书写的是原生sql语句,使用 session.createSQLQuery(sql)创建查询对象,addEnity(xxx.class)封装实体,list()和uniqueResult()执行查询获取结果
以上是关于Hibernate知识点复习之二的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate——实例总结Hibernate对象的状态和ThreadLoacl封闭的session