目录
Hibernate
一、Hibernate是什么,它有什么作用?
Hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成操作,就是与数据库的交互操作。它是在dao层去使用的。
Hibernate框架基于ORM设计思想,它将关系型数据库中的表与我们java中的类进行映射,一个对象就对应着表中的一条记录,而表中的字段对应着类中的属性。
对象关系映射(英语:Object Relation Mapping,简称ORM)
简说,我们使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作。
配置文件将类与表进行映射
使用hinernate提供API来实现操作对象完成操作表的过程
二、Hibernate快速入门
导入hibernate框架相关依赖jar包
1导入lib/required下所有的jar
2导入数据库的驱动jar包
3日志相关jar包
4 将hibernate/project/etc/log4j.properties日志文件导入到工程src下
Hibernate的相关配置文件
1.xxx.hbm.xml 它主要是用于描述类与数据库中的表的映射关系。映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- name属性它是实体类的全名 table 表的名称 catalog 数据库名称 -->
<class name="www.huihex.dao.User" table="t_user" catalog="test">
<!-- id它是用于描述主键 --> <!-- java数据类型 -->
<id name="id" column="id" type="int">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<!-- 使用property来描述属性与字段的对应关系 -->
<!-- hibernate数据类型 -->
<property name="name" column="name" length="20" type="string"></property>
<property name="age" column="age" length="20"></property>
<property name="address">
<!-- sql数据类型 -->
<column name="address" length="50" sql-type="varchar(50)"></column>
</property>
</class>
</hibernate-mapping>
2.hibernate.cfg.xml 它是hibernate框架核心配置文件 。可以改变hibernate.cfg.xml的名字 一般不修改
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项 driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///test</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 设置连接提供者 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- c3p0连接池的配置 -->
<property name="hibernate.c3p0.max_size">20</property> <!-- 最大连接池 -->
<property name="hibernate.c3p0.min_size">5</property> <!-- 最小连接数 -->
<property name="hibernate.c3p0.timeout">120</property> <!-- 超时 -->
<property name="hibernate.c3p0.idle_test_period">3000</property> <!-- 空闲连接 -->
<!-- 可以将向数据库发送的sql显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 用于设置事务提交方式 默认不自动提交 -->
<property name="hibernate.connection.autocommit">false</property>
<!-- 配置hibernate的映射文件所在位置 -->
<mapping resource="www/huihex/dao/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
Hibernate代码测试
package www.huihex.controller;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import www.huihex.dao.User;
public class UserController {
// 保存一个User
@Test
public void saveUser() {
// 创建一个Customer
User user = new User();
user.setName("赵四");
user.setAge(50);
user.setAddress("上海市");
// 使用hibernate的api来完成将customer信息保存到mysql中操作
// 加载hibernate.cfg.xml
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
// 相当于得到一个Connection。
Session session = sessionFactory.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
// 操作
session.save(user);
// 事务提交
transaction.commit();
session.close();
sessionFactory.close();
}
}
Hibernate执行原理总结
hibernate工作原理:
1、通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
2、由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取解析映射信息。
3、通过config.buildSessionFactory();//得到sessionFactory。
4、sessionFactory.openSession();//得到session。
5、session.beginTransaction();//开启事务。
6、persistent operate; //执行操作
7、session.getTransaction().commit();//提交事务
8、关闭session;
9、关闭sessionFactory;
三、Hibernate常用API
Configuration 、SessionFactory、Session、Transaction、Query(HQL、SQL)、Criteria。
update、saveorupdate、delete
四、Hibernate持久化类与主键生成策略
1.Hibernate持久化类
什么是持久化类?
Persistent Object (PO)
PO=POJO+hbm映射配置。
对于hibernate中的PO编写规则:
1.必须提供一个无参数的public构造方法。
2.所有属性要private ,对外提供public 的get/set方法。
3.在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID。
4.PO类中的属性尽量使用基本数据类型的包装类。
Int-Integer double--Double float-Float
5.PO类它不能使用final修饰符。
OID作用:
OID指的是与数据库中表的主键对应的属性。
Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate认为它们是同一个对象。
为什么PO类属性它要使用包装类型?
使用基本数据类型是没有办法去描述不存在概念,如果使用包装类型,它就是一个对象,对于对象它的默认值是null。
PO类不可以使用final修饰?(hibernate中的get/load方法的区别)
Get/load方法它们都是根据id去查询对象。
get直接得到了一个持久化类型对象,它就是立即查询操作。
load它得到的是持久化类开的代理类型对象(子类对象)。它采用了一种延迟策略来查询数据。
get方法在查询时,如果不存在返回null。
load方法在查询时,如果 不存在,会产生异常 ObjectNotFoundException。
2.Hibernate主键生成策略
Hibernate中定义的主键类型包括:自然主键和代理主键:
自然主键:具有业务含义 字段 作为主键,比如:学号、身份证号。
代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:mysql自增主键,oracle序列生成的主键、uuid()方法生成的唯一序列串。
建议:企业开发中使用代理主键!
主键生成器 increment identity sequence native uuid assigned
五、Hibernate持久化对象状态
有三种:
- 瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录。它使用完成后,会被jvm直接回收掉,它只是用于信息携带。
简单说:无OID 与数据库中的信息无关联,不在session管理范围内。
2.持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久 态,当它发生改变时,hibernate是可以检测到的。
简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。
3.托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数 据库中有可能存在,也有可能不存在。
对于托管态对象,它发生改变时hibernet不能检测到。
测试
2.持久化类三种状态切换
判断持久化类对象三种状态:
1.是否有OID。
2.判断是否与session关联。
图示:
1.瞬时态(new 出来的)
瞬时------持久 save saveOrUpdate
瞬时-----脱管(游离) 手动设置oid
2.持久态 它是由session管理
持久-----瞬时 delete() 被删除后持久化对象不在建议使用
持久-----脱管 注意:session它的缓存就是所说的一级缓存
evict(清除一级缓存 中指定的一个对象)
clear(清空一级缓存)
close(关闭,清空一级缓存)
3.脱管态 (它是无法直接获取)
脱管-----瞬时 直接将oid删除
脱管-----持久 update saveOrUpdate lock(过时)
六、Hibernate一、二级缓存
Hibernate的一级缓存就是指session缓存。在session中定义了一系列的集合来存储数据,它们构成session 缓存。只要session没有关闭,它就会一直存在。
actionQueue它是一个行列队列,它主要记录crud操作的相关信息。
persistenceContext它是持久化上下文,它其实是真正缓存。
当我们通过hibernate中的session提供的一些API例如 save get update等进行操作时,就会将持久化对象 保存到session中,当下一次在去查询缓存中具有的对象(OID值来判断),就不会去从数据库查询,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库访问。
在hibernate中还有一个二级缓存,它是SessionFactory级别缓存。
持久化对象具有自动更新数据库能力(快照)
执行代码 发现控制台打印 查询和修改的SQL语句 数据库数据也修改了。
为什么持久化对象具有自动更新数据库能力?
当事物提交, session关闭, 向数据库发送请求时,会判断一级缓存的数据是否与快照区一致,如果不一致,就会发送 update语句。
七、Hibernate关联映射--数据对象三种关系
1.一对一 唯一外键对应 或 主键对应
2.一对多(多对一) 在多添加外键 一个集合(一方)
图一:双向关联
图二:单向关联 希望保存订单时也保存客户 会报错
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.itheima.oneToMany.Customer
…………..
这个异常代表提一个持久化对象关联了一个瞬时对象。
解决办法:级联操作
我们现在要做的是保存订单时保存客户,需要在订单的hbm配置文件中修改 在设置《many-to-one cascade="save-update"》
设置cascade=save-update 那么在保存订单时就可以自动将客户保存。
如果我们要完成保存客户时,保存订单,也使用cascade=save-update。
在实际开发中
我们在开发中要配置双向关联配置。---------可以通过任意一方来操作对方
在操作代码,尽量来要进行单向关联。------可以尽量资源浪费。
我们可以使用inverse属性来设置,双向关联时由哪一方来维护表与表之间的关系。
关于inverse的取值:
外键在哪一个表中,我们就让哪一方来维护外键。
级联删除 cascade=delete
使用cascade可以完成级联操作
它可常用取值:
none这是一个默认值
save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.
delete 级联删除
delete-ophan 删除与当前对象解除关系的对象。
all 它包含了save-update delete操作
all-delete-orphan 它包信了delete-orphan与all操作
3.多对多 中间表 两集合
八、Hibernate注解开发
PO类注解配置
我们最终需要在hibernate.cfg.xml文件中将我们类中的注解配置引用生效 《mapping class》
问题:1.如果我们主键生成策略想使用UUID类型?
问题2:如果设定类的属性不在表中映射?
一对多(多对一)
Customer类
Order类
测试保存客户时保存订单,但订单中没有关联客户的id,为什么?因为mappedBy 我们必须代码中手动关联
多对多
Teacher类中
Student类中
九、Hibernate检索方式概述
1导航对象图检索方式,根据已加载的对象导航到其它对象
2.OID检索方式,按照对象的OID来检索对象
3.HQL检索方式,使用面向对象的HQL查询语言
4.QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
5.本地SQL检索方式,使用本地数据库的SQL查询语句
Hql多表操作分类:
- 交叉连接
- 内连接
- 显示内连接
- 隐式内连接
- 迫切内连接 迫切内连接得到的结果是直接封装到PO类中,而内连接是Object[]数组,数组中封装是PO类对象。
- 外连接
左外连接
右外连接
迫切左外连接 注意:在hibernate中有迫切连接的概念,而sql中没有。
十、Hibernate事务管理
1.什么是事物
事务就是逻辑上的一组操作,组成这组操作的各个单元要么全部成功,要么全都失败。
2.事物的特性(ACID)
原子性:不可分割
一致性:事务在执行前后,要保证数据的一致。
隔离性:一个事务在执行的过程中,不应该受到其它事务的干扰。
持久性:事务一旦结束,数据持久化到数据库。
3.问题:不考虑事务的隔离性,会产生什么问题?
脏读:一个事务读取到另一个事务的未提交数据。
不可重复读:一个事务读取到另一个事务提交的数据(主要是指update),会导致两次读取的结果不一致。
虚读(幻读): 一个事务读取到另一个事务提交的数据(主要是指insert),会导致两次读取结果不一致。
4.问题:对于上述问题如何解决? 设置隔离级别来解决
READ_UNCOMMITED 读取未提交,它引发所有的隔离问题。
READ_COMMITTED 读已提交,阻止脏读,可能发生不可重复读与虚读。
REPEATABLE_READ 重复读 阻止脏读,不可重复读 可能发生虚读。
SERIALIZABLE 串行化 解决所有问题 不允许两个事务,同时操作一个目标数据。(效率低下)
ORACLE 默认的是事务隔离级别 READ_COMMITTED。
MYSQL 默认的事务隔离级别 REPEATABLE_READ。
5.Hibernate中设置事务隔离级别
在hibernate.cfg.xml文件中配置
hibernate.connection.isolation可取的值有 1 2 4 8
1代表的事务隔离级别为READ UNCOMMITTED
2代表的事务隔离级别为READ COMMITTED
4代表的事务隔离级别为 REPEATABLE READ
8代表的事务隔离级别为 SERIALIZABLE
6. Hibernate提供了三种管理session的方式
1.Session对象的生命周期与本地线程绑定(ThreadLocal)
2.Session对象的生命周期与JTA事务绑定(分布式事务管理)
3.Hibernate委托程序来管理Session的生命周期
主要介绍关于本地线程绑定Session。
步骤1:需要在hibernate.cfg.xml文件配置
步骤2: 在获取session时不要在使用openSession而是使用getCurrentSession()方法。
十一、Hibernate优化方案
HQL优化
1.使用参数绑定
使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用用生成好的执行计划,这样做节省CPU时间和内存。 避免SQL注入
2.尽量少使用NOT 如果where子句中包含not关键字,那么执行时该字段的索引失效。
3.尽量使用where来替换having
Having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销
4.减少对表的查询
在含有子查询的HQL中,尽量减少对表的查询,降低开销
5.使用表的别名
当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。
6.实体的更新与删除
在hibernate3以后支持hql的update与delete操作
一级缓存优化 手动清除一级缓存
检索策略(抓取策略)
延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
load方法采用的策略延迟加载.
get方法采用的策略立即加载。
set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。
Fetch可取值有:
SELECT 多条简单的sql (默认值)
JOIN 采用迫切左外连接
SUBSELECT 将生成子查询的SQL
lazy可取值有:
TURE 延迟检索 (默认值)
FALSE 立即检索
EXTRA 加强延迟检索(及其懒惰)