分为六个部分学习:
(1)Hibernate的持久化类的编写规则
- 无参数构造
- 属性私有
- 属性尽量使用包装类
- 提供一个唯一OID与主键对应
- 不要使用final修饰
(2)Hibernate的主键生成策略
- 主键分类
- 自然主键
- 代理主键
- 主键生成策略
- increment
- identity
- sequence
- uuid
- native
- assigned
- foreign
(3)Hibernate的持久化类的三种状态
- 瞬时态:没有唯一标识OID,没有被session管理
- 持久态:有唯一标识OID,已经被session管理(session使用了save等方法之后的对象)
- 脱管态:有唯一标识OID,没有被session管理
- 状态转换:(了解)
(4)Hibernate的一级缓存
- 一级缓存:Hibernate优化手段,称为是session级别缓存。
- 一级缓存:快照区
(5)Hibernate的事务管理
- 事务的回顾
- 事务的概念
- 事务的特性
- 引发安全性问题
- 安全性问题解决
- Hibernate解决读问题
- 配置设置隔离级别
- Hibernate解决Service事务
- 采用的是线程绑定的方式:
(6)Hibernate的其他的API
- Query :HQL 面向对象方式的查询。
- Criteria :QBC 完成面向对象化。
- SQLQuery :SQL查询
(7)悲观锁与乐观锁
(8)基本数据类型和包装数据类型
1.Hibernate的持久化类的编写规则
持久类:我们可以理解为是实体类与表中的列有映射关系的实体类
- 无参数构造
- 属性私有
- 属性尽量使用包装类
- 提供一个唯一OID与主键对应
- 不要使用final修饰
PO:persistent object ,用于与数据库交互数据。--dao层 (JavaBean + hbm ) BO:Business object 业务数据对象。--service层 VO:Value Object 值对象。--web层 |
实体类entity(model)的编写规则
- 我们在使用Hibernate时,书写了一个User类,这个类我们称为JavaBean
- JavaBean可以简单的理解成提供私有属性,并提供私有属性的get/set方法
- POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans
- 我们也称为模型,在Hibernate中,又称这种类为实体,因为是与表关联的
编写规则 |
|
持久化对象的唯一标识 OID(对象标识符)
- Java按地址区分同一个类的不同对象.
- 关系数据库用主键区分同一条记录
- Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系结论: 对象的OID和数据库的表的主键对应。
- 为保证OID的唯一性,应该让Hibernate来为OID赋值
2.Hibernate的主键生成策略
区分自然主键和代理主键
所谓的自然主键有点类似于我们所设定的主键,例如表中的id,但是有时候我们需要更改表中的id,为了后期维护的方便,就必须使用代理主键,代理主键相当于是我们额外增加的与表中数据无关的列。
- 主键需要具备: 不为空/不能重复/不能改变
- 自然主键: 在业务中,某个属性符合主键的三个要求.那么该属性可以作为主键列.【登录名可以是自然主键】
- 代理主键: 在业务中,不存符合以上3个条件的属性,那么就增加一个没有意义的列.作为主键.
主键生成策略
这里是xml配置文件中对于主键设置的
<id name="uid" column="id">
<!-- generator:id的生成策略
increment:也会自动增长id,但是它的这种增长是自己Hibernate实现
执行select max(id) 查询,这种会有线程并发问题
sequence:一般在oracle数据库才用
hilo:hibernate自己实现的id规则【不用,不用学】
native:【经常常用】
如果是mysql数据库,id会自动增长
如果是oracle数据库,会自动增长,sequence
uuid:【经常常用】一个长字符串,需要把模型的id改成字符串
保存的时候,不用自己设置ID,hibernate会设置id
assigned:【经常常用】要手动设置id属性
-->
<generator class="assigned"></generator>
</id>
3.Hibernate的持久化类的三种状态
- 实体Entity有三种状态,瞬时状态、持久状态、脱管状态
- 瞬时状态:transient, session没有缓存,数据库也没有记录,oid没有值
- 持久状态:persistent, session有缓存,数据库也有记录,oid有值
- 脱管状态/游离状态:detached,session没有缓存,数据库有记录,oid有值
(1)瞬时转持久
- 新创建的一个对象,经过save,或者savaOrUpdate调用后,会变成持久状态
(2)持久转脱管
- load,get返回的对象是持久状态的,当session关闭或者清除后,对象变成脱管状态
(3)总结状态的转换过程
- 查询操作:get、load、createQuery、createCriteria 等 获得都是持久态
- 瞬时状态执行save、update、saveOrUpdate之后变成持久状态
- 持久态 转换 脱管态
- session.close () 关闭
- session.clear() 清除所有
- session.evict(obj) 清除指定的PO对象
4.Hibernate的一级缓存
一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。
快照:与一级缓存存放位置是一样,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。
自己理解:session中有两个区域,一个是普通的储存区域,另一个就是快照区,我们在第一次进行操作时,会把我们所查询的东西放在缓存区而且会复制一份到快照区,下次访问时需要先与快照区中的数据做对比,如果是一样的数据,便会从缓存区直接取数据,而不会发送sql语句,但是如果不一样的话就会发送sql语句
证明一级缓存
移除缓存
快照演示(一级缓存刷新)
5.事务
一组业务操作,要么全部成功,要么全部不成功。
特性:ACID
- 原子性:整体 【原子性是指事务包含的所有操作要么全部成功,要么全部失败】
- 一致性:数据 【一个事务执行之前和执行之后都必须处于一致性状态】
- 隔离性:并发 【对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。】
- 持久性:结果 【持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的】
隔离问题
- 脏读:一个事务读到另一个事务未提交的内容【读取未提交内容】
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。
- 不可重复读:一个事务读到另一个事务已提交的内容(insert)【读取提交内容】
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
- 虚读(幻读):一个事务读到另一个事务已提交的内容(update)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。
- Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
隔离级别--解决问题
- read uncommittd,读未提交。存在3个问题。
- read committed,读已提交。解决:脏读。存在2个问题。
- repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
- serializable,序列化,单事务。没有问题。
Hibernate中设置隔离级别
- 在hibernate.cfg.xml 配置hibernate.connection.isolation 4
6.Hibernate的其他的API
- Query :HQL 面向对象方式的查询。
- Criteria :QBC 完成面向对象化。
- SQLQuery :SQL查询
Query 查询对象(重点掌握)
HQL:Hibernate Query Language的缩写,就是Hibernate的查询语言
面向对象查询语言,最终底层要转成面向数据库查询语言
SQL:直接面向数据库查询语言
Query:用于查询的对象,可以设置查询条件和分页查询
limit 0,3表示检索行1-3
(1)setFirstResult(int firstResult):设置开始检索的对象。
(2)setMaxResults(int maxResults):设置每次检索返回的最大对象数。
下面图片的setFirstResult()应该是0
Criteria(重点掌握)
QBC(query by criteria),hibernate提供纯面向对象查询语言,提供直接使用PO对象进行操作。
SQLQuery(了解)
- SQLQuery:使用原生的SQL语句查询
- 并不是所有sql都能转成hql
7.悲观锁和乐观锁
(1)悲观锁
每次都认为会发生丢失更新
悲观锁分为这面两种锁(数据库提供实现) .
读锁/共享锁 【少用】
- 读锁可被其他线程所共享,如果是读取的话大家都可以用这把锁读到数据.
- select * from table lock in share mode(读锁、共享锁)
演示步骤 A终端开事务添加读锁 B终端开事务添加读锁(这时候就会把锁从A抢过来,为B所用) A终端更新(A数据要更新,但是锁在B。所以无法更新,只能等B提交释放锁才行) B终端提交(释放锁,将锁返回给A,A更新了数据) A终端提交(释放锁) |
START TRANSACTION; #添加读锁或者共享锁,其它线程可以用这把锁 SELECT * FROM t_customer LOCK IN SHARE MODE; #如果其它线程用了这把锁,没有提交释放锁,是不能执行更新 UPDATE t_customer SET `name` = 'guoyongfeng' WHERE id=1; COMMIT; |
写锁/排他锁【用的多】
- 写锁不能共享,只要有人为数据加入了写锁,其他人就不能为数据加任何锁.
- select * from table for update (写锁、排它锁)
- 锁可以锁一张表,或者锁一行记录
演示步骤 A终端开事务添加写锁 B终端开事务读表添加写锁【读不到数据】(锁无法为B所用,必须等A释放锁才行) A终端更新(A更新了数据,B无法对数据进行任何操作) A终端提交(释放锁,这时B才能得到锁,并进行数据更新) B终端提交(释放锁) |
START TRANSACTION; #给表添加写锁,其它线程无法读取这个表,当提交后才可以读取这个表 SELECT * FROM t_customer FOR UPDATE;//锁表【不推荐使用】 UPDATE t_customer SET `name` = 'guoyongfeng' WHERE id=1; COMMIT; |
演示Hibernate添加写锁
(2)乐观锁
乐观锁就是程序员去手动操作的,一般认为不会发生丢失更新,所以用一个任意字段来表示,一般的话是version字段(在数据库中加上version这个字段)
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段来实现。
乐观锁的工作原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
乐观锁就是在表中添加一个version字段来控制数据不一致性
画图,以扣钱为例
这里以Customer为例
在PO对象(javabean)提供字段,表示版本字段。一般为Integer |
在映射文件中配置下Version
8.基本数据与包装类型
我们在创建实体类时如果使用的是基本类型,因为默认值为0,我们后期维护时可能会无法区分是默认的0还是我们自己设定的,所以我们最好使用包装类型,包装类型的默认值为null,对于我们对业务操作有很大的帮助
- 基本数据类型和包装类型对应hibernate的映射类型相同
- 基本类型无法表达null、数字类型的默认值为0。
- 包装类默认值是null。当对于默认值有业务意义的时候需要使用包装类。
SQL、Hibernate和对象类型对应
Java数据类型 | Hibernate数据类型 | 标准SQL数据类型 |
byte、java.lang.Byte | byte | TINYINT |
short、java.lang.Short | short | SMALLINT |
int、java.lang.Integer | integer | INGEGER |
long、java.lang.Long | long | BIGINT |
float、java.lang.Float | float | FLOAT |
double、java.lang.Double | double | DOUBLE |
java.math.BigDecimal | big_decimal | NUMERIC |
char、java.lang.Character | character | CHAR(1) |
boolean、java.lang.Boolean | boolean | BIT |
java.lang.String | string | VARCHAR |
boolean、java.lang.Boolean | yes_no | CHAR(1)('Y'或'N') |
boolean、java.lang.Boolean | true_false | CHAR(1)('Y'或'N') |
java.util.Date、java.sql.Date | date | DATE |
java.util.Date、java.sql.Time | time | TIME |
java.util.Date、java.sql.Timestamp | timestamp | TIMESTAMP |
java.util.Calendar | calendar | TIMESTAMP |
java.util.Calendar | calendar_date | DATE |
byte[] | binary | VARBINARY、BLOB |
java.lang.String | text | CLOB |
java.io.Serializable | serializable | VARBINARY、BLOB |
java.sql.Clob | clob | CLOB |
java.sql.Blob | blob | BLOB |
java.lang.Class | class | VARCHAR |
java.util.Locale | locale | VARCHAR |
java.util.TimeZone | timezone | VARCHAR |
java.util.Currency | currency | VARCHAR |