Hibernate 学习笔记
hibernate(持久化)
一. Hibernate 的理解
Hibernate 是数据访问层(Dao层),就是把数据存入到数据库中,称为持久化。
Hibernate 对 JDBC 进行了封装,针对数据访问层提出面向对象的思想,操作对象间接的操作数据库中的表,自动生成 SQL 语句,可以简化数据访问层的代码,提高开发效率。
二. hibernate的优缺点
-
优点:
-
使用 JDBC 遇到的问题:
-
代码中存在大量的 SQL 语句
-
查询结果需要手动封装到 Model
-
SQL 语句中存储大量的
?
,需要手动赋值 -
SQL 语句根据不同的数据库,有不同的函数,如果更换数据库,需要大量更改 SQL 语句,针对数据库移植性差
-
-
使用 Hibernate 可以解决以上问题:
-
Hibernate 操作对象自动生成 SQL 语句
-
查询结果自动赋值给 Model 类
-
自动赋值
-
Hibernate 使用的是 HQL 语句,根据不同的方言,生成不同数据库的 SQL 语句,到达跨数据库平台
注:数据迁移是软性项目中的大事情,特别难做,做项目时对数据库的选型尤为重要
-
-
-
缺点:
- SQL语句自动生成,人工无法控制,使得 SQL 语句执行效率慢
- Hibernate 执行效率低
- Hibernate 特别耗内存,有一系列缓存机制
三. 什么是ORM(☆)
ORM 是一种编程思想(开发模式),全称:Object Relation Mapper(对象关系映射)
是为了解决面向对象与面向关系型数据库不匹配现象,通过一个配置文件把面向对象与面向关系型数据库关联起来
- 类 --- 表
- 属性 --- 字段
- 对象 --- 记录
优点:使得数据访问层更面向对象,不用考虑关系型数据库,只要会面向对象即可,开发程序变简单了(Hibernate、MyBatis、Spring、JDBC)
四、搭建 Hibernate 环境
- 导包:在
hibernate-release-4.3.11.Final/lib/required
下所有 Jar 文件与数据库驱动包复制到项目中 - 引入 Hibernate 主配置文件,
Hibernate.cfg.xml
文件复制到项目中的 src 目录 - 创建一个 Model 类实现 Serializable 接口,对应一个表,并且在映射文件
xxx.hbm.xml
中配置 - 编写 Hibernate API 进行测试
五. Hibernate 的体系结构(Hibernate由哪几部分组成)(☆)
结构 | 描述 |
---|---|
hibernate.cfg.xml | 是 Hibernate 的主配置文件,用来配置数据库连接信息与 Hibernate 相关参数 |
XXX.hbm.xml | 类与表之间的映射文件,一个类对应一个表,一个属性对应一个字段 |
实体类 | 用于封装数据库的表,一定要实现 Serializable 接口 |
Hibernate API | 用于读取并解析配置文件,然后根据映射关系生成 SQL 语句,间接操作数据库 |
六. Hibernate 工作原理(☆)
- Configuration 类读取并解析配置文件(主配置文件、映射文件)
- 创建 SessionFactory(一个 SessionFactory 对应一个数据库)
- 打开 Session(Session 代表程序与数据库之间的一次会话,用来做表的增删改查)
- 创建事务(Transaction 代表数据库事务)
- 持久化操作(增删改查)
- 提交事务
- 关闭 Session
- 当应用程序停止,关闭 SessionFactory
注:SessionFactory 是一个重量级对象,创建销毁特别耗资源,应用程序创建一次,关闭一次
七. Hibernate 代码工作原理
-
获取 Configuration 类的对象(cfg)
-
调用
cfg.configure()
方法默认读取 src 根目录下的hibernate.cfg.xml
-
调用
cfg.buildSessionFactory()
方法创建 Session 工厂(sf) -
调用
sf.openSession()
方法获取 Session -
若为添加、删除、修改操作,则开启事务
Transaction ts = session.getTransaction() ts.begin();
-
进行持久化操作
添加:e(obj); 删除:delete(obj); 修改:update(obj); 查询:get(类名.class, 1); 查询:load(类名.class, 1); 添加和修改都支持:saveOrUpdate(obj);
-
提交事务,关闭 Session,关闭 SessionFactory
ts.commit(); session.close(); sf.close();
八. Hibernate 的映射类型
Hibernate 映射文件xxx.hbm.xml
中,Java 数据类型与数据库数据类型相互转换,通过 type 属性进行指定,类型如下:
-
整数
Java 类型 type 取值 byte byte short short int Integer long long -
小数
Java 类型 type 取值 float float double double -
字符串
Java 类型 type 取值 String String -
日期
Java 类型 type 取值 java.util.Date date java.sql.Date date -
时间
Java 类型 type 取值 java.sql.Timestamp timestamp
注:建议手动创建表,不要让 Hibernate 生成表格,
hibernate.hbm2ddl.auto
配置成为 update
九. Hibernate 核心开发接口(常用的类与接口)(☆)
接口 | 描述 |
---|---|
Configuration | 读取并解析配置文件,然后创建 SessionFactory(是一个类) |
SessionFactory | 代表一个数据库,一个 SessionFactory 对应一个数据库,用于创建 Session(是一个接口) |
Session | 程序与数据库的一次会话,用于数据库表的增删改查(是一个接口) |
Transaction | 事务管理(是一个接口) |
Query 与 Criteria | 用于数据查询(是一个接口) |
十. Hibernate 有多少种查询
- 主键查询:
get()
与load()
- HQL 查询
- SQL 查询
- QBC 查询
查询效率由高到低排列:主键查询 > SQL 查询 > HQL 查询 > QBC 查询
十一. 查询方法延迟加载(☆)
-
什么是延迟加载?
Hibernate 中存在一些方法,在查询的时候并没有马上发送 SQL 语句去数据库查询,而是返回一个空值对象,空值对象不是 null,而是新 new 出来的对象,除主键以外其他属性值为空,当程序真正使用到此对象时,才会发送 SQL 语句去数据库中查询,并且将结果赋值给此对象,这种查询称为延迟加载
-
为什么要用延迟加载?
- 在延迟的这段时间内,并没有访问数据库,可以节约内存开销,减少数据库的访问,提高使用效率
- 可以防止查询对象,但并没有真正的使用,这种情况下可以节约内存,减少数据库访问
-
如何使用延迟加载?
-
Hibernate 中一些方法自带延迟加载机制,只要调用这些方法,就可以使用延迟加载
-
具有延迟加载机制的访问如下
延迟加载 立即加载 session.load() session.get() query.iterate() query.list()
-
-
get() 与 load() 区别(☆)
get():立即加载,立即发送 SQL 语句查询,如果没有查询到结果返回 null
load():延迟加载,不会立即发送 SQL 语句查询,返回一个空值对象,真正使用到此对象,才会发送 SQL 语句。如果没有查询到结果,会抛出
ObjectNotFoundException
异常 -
list() 与 iterate() 区别
list():立即加载,立即发送 SQL 语句查询,返回一个对象集合,如果没有查询到数据,返回空集合
iterate():延迟加载,首先会发送一条 SQL 语句把表中所有主键查询出来,在遍历的时候,根据主键发送 SQL 语句单个查询,有多少条记录就会发送多少条 SQL 语句查询
注:建议使用立即加载
十二. 一级缓存(☆)
-
什么是一级缓存?
Hibernate 在创建 Session 的时候,会给每个 Session 另外分配一片内存空间,用于缓存 Session 操作过的对象,这块儿内存称为一级缓存,一级缓存是
Session 管理并且使用的,所以也称为:Session 缓存。
一级缓存的生命周期与 Session 一致,Session 被创建时,一级缓存空间被分配,
session.close()
时,一级缓存被回收 -
一级缓存的作用?
一级缓存用来缓存 Session 操作过的对象,相同数据不用每次都去查询数据库,直接从一级缓存中获取,提高查询效率
-
一级缓存的步骤?
Session 优先查询一级缓存,首先去一级缓存中查询,查询不到才会发送 SQL 语句查询数据库,查询到结果之后会在一级缓存中存放一份,再次查询时,无需发送 SQL 语句查询数据库,直接从一级缓存中获取(在 Hibernate 中一级缓存优先于数据库,一级缓存与数据库数据不同步时,以一级缓存为主)
-
如何使用一级缓存?
-
一级缓存是默认开启,自动使用
-
一级缓存的特征:
- 一级缓存是 Session 独享的,Session 与 Session 之间不能共享数据
- Session 查询一组数据时,会将一组数据拆开存入一级缓存中,一级缓存中存储的是单个对象
- 执行增删改时会同步一级缓存,当
delete(Object obj)
时,在一级缓存中会标记此对象可能被删除,再次查询时不会发送 SQL 语句查询,返回一个null 对象
-
管理缓存
- clear():清空一级缓存
- evict():清空一级缓存单个对象
- flush():手动同步一级缓存与数据库,数据不一样,以一级缓存为主
-
十三. 二级缓存
SessionFactory 级别的缓存(需要配置)
Hibernate 二级缓存需要配置,在同一 SessionFactory 范围内,查询同一个对象多次,只会发送一条 SQL 语句(只会去数据库中查询一次),后面每次查询都是从二级缓存中去取数据
配置信息:
- 导入二级缓存的 Jar 包 ehcache oscache
- 把对应配置文件放入到 src 根目录下
- 在
Hibernate.cfg.xml
中开启二级缓存 - 声明哪些对象需要放入到二级缓存中
十四. 对象的持久性
什么是对象的持久性?
Hibernate 操作对象时,可以把对象看成三种状态::瞬时态,持久台,游离态/托管态
三种状态的规则?
-
瞬时态(transient)
- 定义:对象刚刚被 new 出来,称为瞬时态
- 规则:瞬时态可以被垃圾回收机制回收,一级缓存中没有,数据库中没有
-
持久态(persistent)
- 定义:一级缓存中有,数据库中有,称为持久态
- 规则:通过 save(),update(),saveOrUpdate(),get(),load(),HQL,SQL,QBC 方式操作过的对象,称为持久态对象
-
游离态(detached)
- 定义:一级缓存中没有,数据库中有,称为游离态
- 规则:通过 clear,evict,close 方式操作过的对象, 称为游离态对象
十五. 主键的生成策略
在映射文件(xxx.hbm.xml)中需要配置主键字段,并且需要配置主键的生成策略,通过 generator 标签指定
主键生成策略:
-
sequence
代码如下:
<generator class="sequence"> <!-- 指定序列的名称 --> <param name="sequence">t_person_seq</param> </generator>
对应数据库:Oracle,DB2
-
identity
代码如下:
<generator class="identity"></generator>
对应数据库:MySQL,SQL Server
-
native
代码如下:
<generator class="native"></generator>
含义:native 是让 hibernate 自动选择一种主键方式,根据配置文件中的方言,从 sequence 与 identity 中选一个,方言配置的是 Oracle ,自动选择sequence,方言配置的是MySQL,自动选择 identity
-
assigned
代码如下:
<generator class="assigned"></generator>
含义:程序员手动分配主键,Hibernate 不会自动生成
-
uuid
代码如下:
<generator class="uuid"></generator>
含义:采用 UUID 算法生成一个32位十六进制的字符串作为主键
-
increment
代码如下:
<generator class="increment"></generator>
含义:查询表中最大 id 值, 把最大 id + 1 生成主键
优点:适用与任何数据库
缺点:并发量大时,会产生相同的 id,线程不安全,不推荐使用
十六. 关系映射
什么是关系映射?
如果两张表之间有关系,Hibernate 允许我们将关系提取出来,并且映射的配置文件中,在对一个表的增删改查操作,Hibernate 通过这个映射关系,间接的操作另一张表的增删改查,这两个表的关系配置称为关系映射
关系映射类型?
- 多对一
- 一对多
- 一对一
- 多对多
十七. 多对一(重点)与一对多
-
设计
两张表之间的关系为多对一或一对多,会在多的一端增加一个字段指向一的那端的主键
-
案例
学生与班级:多个学生属于一个班级,一个班级有多个学生
一对多添加会产生 N+1 条,尽量少用一对多,用多对一代替
一对多配置:
<!-- set标签:指定是 set 集合 name属性:类中的属性名 --> <set name="students"> <!-- key标签:指定两个表之间关联字段(外键字段名称) column属性:外键字段的字段名 --> <key column="f_classId"></key> <!-- one-to-many标签:表示与哪个类发生了一对多的关系 class属性:多对那端类的名称 --> <one-to-many class="Student" /> </set>
多对一配置:
<!-- many-to-one 标签:多对一关系 name 属性:类中属性的名称 column 属性:表中的字段名(外键字段名) class 属性:外键属性的类(完整路径) --> <many-to-one name="cla" column="f_classId" class="com.zt.model.Classes"></many-to-one>
十八. 关联操作
关联查询(查询)
-
延迟加载(对于关联属性,Hibernate 默认采用的是延迟加载,查询一端数据,不会关联出另一端数据)
lazy="proxy/true" 默认方式,采用懒加载 lazy="false" 立即加载,关联的表数据会同时查询出来
-
连接查询
fetch="select" 默认方式,使用多条 SQL 语句查询 fetch="join" 使用连接查询,一条 SQL 语句完成查询,使用此属性懒加载失效
-
join 查询
String hql = "from Student s left join fetch s.cla"; 左外连接 String hql = "from Student s inner join fetch s.cla"; 内连接
级联操作(增删改)
-
什么是级联操作?
在对一张表做增删改操作时,关联的另一张表也做增删改操作,称为级联操作
-
如何设置级联操作?
在映射文件中,关联映射配置 cascade 属性,用这个属性定义级联操作
-
cascade取值如下:
取值方式 | 描述 |
---|---|
none | 默认方式,不支持级联 |
all | 支持增删改 |
save-update | 支持增,改 |
delete | 支持删 |
十九. 一对一(了解)
一对一的类型:
主键一对一:包装两个标段主键相同
外键一对一:在任意一端增加一个字段(外键字段)指向另一端的主键,并且这个字段有唯一约束(不能重复)
案例:
学生与档案一对一,在学生表中增加一个字段指向档案表的主键,外键保证唯一性
二十. 多对多
什么是多对多?
如果两张表的关系是多对多,必然产生一张中间表,中间表只有两个字段,分别为两张表的外键字段,这两个外键字段组合成复合主键
案例:
一个班级由多个老师教学,一个老师可以教多个班级
多对多配置:
<!--
set标签:指定是 set 集合
name属性:类中的属性名
table:中间表的表名
-->
<set name="teachers" table="t_cla_tea">
<!--
column属性:当前表的主键的对应中间表的外键字段
-->
<key column="f_classId"></key>
<!--
many-to-many标签:多对多的关系映射
class属性:当前 set 集合元素对应的类名
column属性:对方表的主键对应中间表的外键字段名
-->
<many-to-many class="Teacher" column="f_teacherId"/>
</set>
二十一. Hibernate 查询语句
- 主键查询:load,get
- HQL 查询(Hibernate Query Language) :标准的SQL + 面向对象语言
- QBC 查询(Query By Criteria):完全的面向对象
- SQL 查询(Structured Query Language):结构化查询语言
本文来自博客园,作者:Schieber,转载请注明原文链接:https://www.cnblogs.com/xiqingbo/p/java-24.html