Hibernate学习笔记
Hibernate
1、概述
1.1 EE三层结构
1.2 各层对应技术
2、使用
2.1 导入jar包
- 数据库驱动包
- Hibernate开发必需的jar包(required)
- Hibernate引入日志记录包(log4j)
2.2 创建映射
- 数据库表属性与实体类映射体现在
hibernate
中是配置文件; - 配置文件的命名原则上是随意的,但一般遵循一定的规则,即
类名.hbm.xml
; - 文件在类所在目录下。
2.3 创建核心配置文件
- 核心配置文件的名称遵循的规则是
hibernate.cfg.xml
; - 配置文件在项目中的位置与映射文件不同,在根目录下。
2.4 使用
-
加载
Hibernate
的核心配置文件; -
创建一个 SessionFactory 对象(类似于
JDBC
中的连接池); -
通过 SessionFactory 对象获取到 Session 对象(类似于
JDBC
中的 Connection ); -
手动开启 事务;
-
编写处理代码(操作数据库代码);
-
事务提交;
-
资源释放。
3、Hibernate的常用配置
3.1 XML提示的配置
- 配置本地
dtd
文件,在开发计算机未连接网络时仍然有提示功能。
3.2 Hibernate映射的配置
3.2.1 class标签的配置
- 该标签用来建立类与数据库表的映射关系;
- 属性
- name : 类的全路径
- table : 表名 (类名与表名一致,可省略)
- catlog : 数据库名 (一般省略)
3.2.2 id标签的配置
- 该标签用来建立类中的属性和表中主键之间的关系;
- 属性
- name : 类中的属性名
- colume : 表中的字段名 (类名与字段名一致,可省略)
- length : 长度 (自动建表时使用)
- type : 数据类型 (自动建表时使用)
3.2.3 property标签的配置
- 该标签用来建立类中的属性和表中普通属性之间的关系;
- 属性
- name :
- colume :
- length :
- type :
- not-null : 设置非空
- unique : 设置唯一
3.3 Hibernate核心的配置
3.3.1 必须的配置
- 连接数据库的基本参数
- 驱动类
- hibernate.connection.driver_class
- com.mysql.cj.jdbc.Driver
- 数据库名称(URL路径)
- hibernate.connection.url
- jdbc:mysql:///test
- 用户名
- 密码
- 驱动类
- 方言
- hibernate.dialect
- org.hibernate.dialect.MySQLDialect
3.3.2 可选的配置
- 显示(输出)SQL语句
- hibernate.show_sql
- true
- 格式化SQL语句
- hibernate.format_sql
- true
- 自动建表
- hibernate.hbm2ddl.auto
- 取值
- none 不使用hibernate的自动建表
- create 有表则删除后创建,无表直接创建(用于测试)
- create-drop 有表则删除并创建,执行操作后删除表;无表则创建,执行完后删除(用于测试)
- update 有表则使用表(可能会更新表结构),无表则创建表
- validate 只会使用现有的表(没有不会创建),但会校验映射和表结构。
3.3.3 映射文件的引入
- mapping标签
- resource
- hibernate/Customer.hbm.xml(文件路径)
- resource
4、Hibernate的核心API
4.1 Configuration
Hibernate
的配置对象,并启动之,随后创建SessionFactory
对象;- 作用
- 加载核心配置文件(hibernate.cfg.xml)
- 手动加载映射文件(当核心配置文件中未加载时)
4.2 SessionFactory
- 初始化
Hibernate
,并创建Session
对象; - 内部维护了
Hibernate
的连接池和二级缓存(可以自己配置C3P0连接池<在核心配置文件中>); - 是线程安全的;
- 一个项目创建一个对象即可(非轻量级的);
- 可以抽取一个工具类
4.3 Session
-
类似
JDBC
中的Connection
对象; -
非线程安全的(定义时不能定义为全局变量,在使用到的方法中定义);
-
API
-
保存方法
-
// 返回保存在数据库中的id Serializable save(Object obj);
-
-
查询方法
-
// 1. 采用 立即 加载 // 2. 返回的是 真实对象 本身 // 3. 查询找不到的对象时返回 null T get(Class c, Serializable id);
-
// 1. 采用 延迟 加载(lazy加载) // 2. 返回的是 代理对象(利用javassist技术产生的代理) // 3. 查询找不到的对象时 报错(ObjectNotFoundException) T load(Class c, Serializable id);
-
-
修改方法
-
// 建议 先查询,后修改 void update(Object obj);
-
-
删除方法
-
// 建议 先查询,后删除(级联删除) void delete(Object obj);
-
-
保存或修改
-
// 操作对象表中没有,则保存;否则修改 void saveOrUpdate(Object obj);
-
-
查询所有
-
使用
HQL
-
Hibernate Query Language
-
Query query = session.createQuery("from Customer"); List<Customer> list = query.list(); for (Customer customer : list) { System.out.println(customer); }
-
-
-
使用
SQL
-
SQLQuery query = session.createSQLQuery("select * from cst_customer"); List<Object[]> list = query.list(); for(Object[] objects : list) { System.out.println(Arrays.toString(objects)); }
-
-
4.4 Transaction
Hibernate
中管理事务的对象;- 方法
- commit();
- rollback();
5、持久化类的编写规则
5.1 持久化类
- 一个Java类拥有一个与它形成映射的数据库表,这样的类在Hibernate中被称为持久化类。
- 持久化类 = Java类 + 映射文件
5.2 持久化类的编写规则
-
对持久化类提供一个无参数的构造方法
Hibernate
底层需要使用反射来生成实例对象(如查询方法get
的第一个参数)。
-
属性需要私有,并给这些属性
get
和set
方法Hibernate
中需要获取这些属性值,并设置对象的值。
-
持久化类需要一个唯一标识
OID
与数据库表主键对应Java
中通过对象的地址区分是否是同一个对象,数据库中通过表中的主键区分是否是同一条记录,Hibernate
中通过持久化类的OID
属性区分是否是一个对象。
-
持久化类中的属性尽量使用包装类类型
- 基本数据类型的默认值是0, 0有很多的歧义。
-
持久化类不能使用
final
关键字进行修饰- 在
Hibernate
中有延迟加载技术,具体的是调用某方法会返回一个代理对象(对没有实现接口的类),这里又用到了底层技术——字节码增强技术,即使用了继承机制,被final
关键字修饰的类是不能被继承的。
- 在
6、主键生成策略
6.1 主键的分类
- 自然主键
- 主键本身就是表中应有的一个字段(是实体中的一个必须属性)
- 如创建一个人员表,使用人的身份证号作为主键。
- 主键本身就是表中应有的一个字段(是实体中的一个必须属性)
- 代理主键
- 主键本身不是表中的一个必须字段(不是实体中的某个具体属性)
- 如创建一个人员表,不使用身份证号,而用了一个与此表不相关的字段ID(PNO)。
- 主键本身不是表中的一个必须字段(不是实体中的某个具体属性)
- 一般使用代理主键
- 避免后期修改源代码
OCP
原则:对程序的扩展是open的,对源代码的修改是close的。
- 避免后期修改源代码
6.2 主键生成策略
increment
- 适用于
short
,int
,long
类型的主键; - 是
Hibernate
中的自动增长机制; - 一般在单线程程序中使用。
- 在使用前会发送一条语句 select max(id) from table,之后让id加一,故存在线程安全问题。
- 适用于
identity
- 适用于
short
,int
,long
类型的主键; - 使用数据库底层增长机制,故需要带有自增机制的数据库支持(如MySQL)。
- 适用于
sequence
- 适用于
short
,int
,long
类型的主键; - 使用序列方法,需要支持序列方式的数据库(如Oracle)。
- 适用于
native
- 本地策略,可以在
identity
和sequence
之间自动切换。
- 本地策略,可以在
uuid
- 适用于字符串类型的主键。
7、持久化类的三种状态
-
为了更好的管理持久化类,将持久化类分成三个状态。
-
// 三种状态的区分 Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); // 瞬时态对象 Customer customer = new Customer(); customer.setCust_name("zhangsan"); // 持久态对象 Serializable id = session.save(customer); transaction.commit(); session.close(); // 脱管态对象 System.out.println(customer);
7.1 瞬时态(transient)
- 亦称 临时态 或 自由态;
- 可以由
new
关键字创建; - 没有唯一标识
OID
,不受Session
管理。
7.2 持久态(persistent)
- 有唯一标识
OID
,被Session
管理; - 会自动更新数据库;
- 底层原理
Hibernate
提供的一级缓存 和 快照 技术。
- 底层原理
- 可以通过 get(),load(),find(),iterate() 获得。
7.3 脱管态(detached)
- 亦称离线态或游离态;
- 有唯一标识
OID
,没有被Session
管理; - 可以由 持久态 对象通过 close(), clear(), evict() 转换得到。
8、Hibernate的一级缓存
Hibernate
中提供了一些优化手段,如 缓存 技术和 快照 技术;Hibernate
的 一级缓存 又称为 Session缓存,其生命周期与Session
一致;- 一级缓存 由一系列
Java
集合构成; Hibernate
快照区的存在是为了使一级缓存和数据库表中的数据保持一致。Hibernate
向一级缓存中存入数据时,会拷贝一份到快照区;- 并在执行commit()方法时
- 会清理一级缓存中的数据(执行flush())操作;
- 同时会检测一级缓存中的数据是否和快照区中的数据一致。
- 不一致时会执行update()方法,将一级缓存中的数据存入到数据库中,并更新快照区(这也是持久态对象会自动更新数据库的原因)。
9、Hibernate的事务管理
- 事务
- 逻辑上的一组操作,并且组成这组操作的各个逻辑单元,必须同时成功或失败。
9.1 事务特性
- 原子性
- 代表事务不可分割
- 一致性
- 代表事务执行的前后,数据的完整性保持一致
- 隔离性
- 代表一个事务执行过程中,不应受到其他事务的干扰;
- 不考虑隔离性,会引发一系列安全问题
- 读问题
- 脏读
- 一个事务读到另一个事务未提交的数据。
- 不可重复读
- 一个事务读到另一个事务已经提交的
update
数据,导致查询不一致。
- 一个事务读到另一个事务已经提交的
- 虚读
- 一个事务读到另一个事务已经提交的
insert
数据,导致查询不一致。
- 一个事务读到另一个事务已经提交的
- 脏读
- 写问题
- 引发两类丢失更新
- 读问题
- 持久性
- 代表事务执行完成后,数据持久到数据库中
9.2 读问题的解决
可以通过设置事务隔离级别解决。
- Read uncommitted
- 三类读问题都会发生。
- Read committed
- 解决脏读;
- Oracle支持。
- Repeatable read
- 解决脏读和不可重复读;
- MySQL支持。
- Serializable
- 解决所有。
9.3 Hibernate
中事务隔离级别的设置
<!--在核心配置文件中设置事务的隔离级别-->
<!--
1--Read uncommitted
2--Read committed
4--Repeatable read
8--Serializable
-->
<property name='hibernate.connection.isolation'>4</property>