※※※ Hibernate不适合的场景:不适合批量的查询和更新 ※※※
a.不适合OLAP(On-Line Analytical Processiong 联机分析处理),以查询分析数据为主的系统
适合OLTP(On-Line transaction processing 联机事务处理)
b. 对于些关系模型设计不合理的老系统,也不能发挥hibernate优势
c 数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求,批量操作数据效率也不高
Configuration:用于解析hibernate.cfg.xml文件和XXXXX.hbm.xml文件,并创建SessionFactory对象。
Session:是非线程安全的,生命周期较短,代表一个和数据库的连接,在B/S系统中一般不会超过一个请求;内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接。
SessionFactory:持久化管理器,对象级数据库操作;是线程安全的,一个数据库对应一个SessionFactory,生命周期长,一般在整个系统生命周期内有效;SessionFactory保存着和数据库连接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到的一些信息。
Query:对对象作持久化操作或查询操作
Transaction :用于管理操作事务。
实体对象的生命周期
了解session的CURD操作
了解session.get()和session.load()方法的区别
* get不支持lazy,load支持lazy
* 采用get加载数据,如果数据库中不存在相应的数据,那么返回null
* 采用load加载数据,如果数据库中不存在相应的数据,那么抛出ObjectNotFoundException
Transient(瞬时)状态
* 不存在 session缓存中,不和任何session实例有关联
* 在数据库中没有相应的记录
Persistent(持久)状态
* 位于session缓存中,Persistent状态的对象总是和session实例相关联
* Persistent状态的对象和数据库表中的记录有对应
* Persistent状态的对象发生改变会自动和数据库同步
Detached(分离)状态
* 不处于session缓存中,不和任何session实例有关联
* Detached状态的对象在数据库中有与之对应的记录
Hibernate内置的主键生成策略
1,序列sequence 只适用于Oracle
<id name="id" column="id">
<generator class="sequence">
<param name="sequence">person_seq</param><!--指定sequence名-->
</generator>
</id>
2,自增列,适用于SQLServer、MySql
<id name="id" column="id">
<generator class="identity"/>
</id>
3,取最大值加一
<id name="id" column="id" type="integer">
<generator class="increment"/>
</id>
4,根据底层数据库指定生成方法
<id name="id" column="id">
<generator class="native"/>
</id>
使用缺省策略
针对Oracle数据库的生成方式还是sequence,只不过需要一个特定名字的sequence,"hibernate_sequence"。
5,高低位算法
<id name="id" column="id">
<generator class="
<param name="table">high_value</param>
<!--设置高位值取值的表-->
<param name="column">next_value</param>
<!--设置高位值取值的字段-->
<param name="max_lo">50</param>
<!--指定低位最大值,当取道最大值是会再取一个高位值再运算-->
</generator>
</id>
以上是hilo算法的普通形式,不适合用于squenece
其它。assigned(手工指定),foreign(外部引用)
uuid (使用了IP地址+JVM的启动时间(精确到1/4秒)+系统时间+一个计数器值(在JVM中唯一))
Hibernate基本映射
实体类à数据库表
普通属性à表字段
通过<class>标签映射成数据库表, 其name属性表示这个类的全路径
通过<property>标签将普通属性映射成数据库表的字段
★ 所谓普通属性指不包含自定义类,集合,数组等等
实体类中的String会映射成数据库中的varchar类型
实体类的设计原则
* 实现一个无参的构造方法
* 提供一个标识属性 (可选)
* 使用非final类(可选)(如果使用的话,lazy属性就会失效)
* 为持久化字段声明访问器(get,set方法)
主键生成策略
* uuid (字符串作为主键)
* native (数字作为主键,效率低,并发不太好)
* assigned (手动分配)
注意:类的名称或类中属性名称,如果和SQL中的关键字重复,必须用table或column属性重命名
Hibernate【多对一】关联映射
将关联关系映射到关系表,关联关系在对象模型中体现为一个或多个引用
<many-to-one>会在“多”的一端添加一个外键,指向“一”的一端。
这个外键是由<many-to-one>中的column属性定义的,如果忽略这个属性,默认外键与实体类的属性一致
注意: 如果实体类的名称和属性与sql关键字重复,必须重新命名
<many-to-one>标签示例:
* <many-to-one name="Group" column="groupid"/>
理解级联的含义
对象之间的连锁操作(只对【增*删*改】起作用)
<many-to-one name="group" column="groupid" cascade="save-update"/>
Persistent状态的对象不能引用Transient状态的对象
Hibernate一对一【主键】关联映射
(单向关联Person--->IdCard)
一对一主键关联映射:让两个实体对象的id保持相同,这样可以避免多余的字段被创建
具体映射方式:
<class name="com.bin.hibernate.Person" table="t_person">
<id name="id">
<!-- Person主键来源于IdCard,共享IdCard的主键 -->
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<property name="name"/>
<!-- one-to-one标签只是hibernate如何加载其引用对象,默认根据主键加载,
constrained="true":表明当前主键上存在一个约束,Person的主键作为外键参照了IdCard的 -->
<one-to-one name="idCard" constrained="true"/>
</class>
在一对一关联映射中,存储person对象,其关联的IdCard也同时被存储
而不会抛出TransientObjectException异常,因为它默认了级联属性
one-to-one标签不在数据库表里加外键的字段
(双向关联Person <---> IdCard)
需要在IdCard中加入<one-to-one>标签指向person,指示hibernate如何加载Person,默认根据主键加载
hibernate一对一【唯一外键】关联映射
(单向关联Person--->IdCard)
一对一唯一外键关联映射实际上就是多对一关联映射的特例
可以采用<many-to-one>标签,指定“多”的一端的unique=true,即限制了“多”的一端的多重性为“一”。
通过这种手段来映射一对一唯一外键关联
(双向关联Person<--->IdCard)
一对一唯一外键双向关联映射,需要在另一端(idCard)添加<one-to-one>标签,指示hibernate如何加载其引用对象。
默认情况下根据主键加载Person,因为外键关联映射中两个实体的关系是通过person中的idCard维护的,所以不能根据person的主键来加载,
而是根据person的外键来加载person对象,如:
<one-to-one name="person" property-ref="idCard"/>
session.flush测试
session.flush主要做两件事:
* 清理缓存(脏数据对比)
* 执行SQL(不代表提交事务)
hibernate中SQL的执行顺序
hibernate按照save(insert)、update、delete顺序提交相关操作
(1)、如果user的主键生成策略是uuid,所以调用完save后,只是将user纳入了session管理,不会发出SQL,但userid已经生成,session中的existsDatabase状态为false
(2)、调用flush,hibernate会清理缓存,执行SQL
如果将数据库的隔离级别设置为“未提交读”,此时可以看到flush过的数据,并且session中的existsDatabase状态为true
(3)、默认情况下commit会首先执行flush来清理缓存,所以不用显式的调用flush
commit后无法回滚事务
hibernate【一对多】关联映射(单向)
这个映射的本质:采用多对一映射原理
多对一关联映射:在“多”的一端加入一个外键,指向“一”的一端,它维护的是多到一的关系
一对多关联映射:在“多”的一端加入一个外键,指向“一”的一端,它维护的是一到多的关系
<set name="students">
<!-加外键,在“多”的一端加一个外键,名字叫classesid->
<key column="classesid"/>
<one-to-many class="com.bin.hibernate.Student"/>
</set>
也就是说一对多和多对一映射策略是一致的,只是站的角度不同
一对多关联映射让hibernate在“一”的一端维护关系存在缺点:
* 会发出多余的update语句,建立班级和学生之间的关系,影响性能
* 如果student表中的classesid设置为非空,将无法保存数据
Hibernate【一对多】关联映射(双向)
一对多双向关联映射:
* 在“一”的一端的集合上使用<key>标签,在对方表中加入一个外键指向“一”的一端
<set name="students" inverse="true">
<key column="classesid"/>
<one-to-many class="com.bin.hibernate.Student"/>
</set>
* 在“多”的一端采用<many-to-one>:
<many-to-one name="classes" column="classesid"/>
注意:<key>标签所指定的外键字段名需要与<many-to-one>标签定义的外键字段名一致,否则便会造成引用数据的丢失!
-----------------------------------------------------------------------
如果从一端来维护一对多双向关联的关系,hibernate会发出多余的update语句,所以
一般地情况下,我们便会从多一端来维护其关联关系!
------------------------------------------
Hibernate【多对多】关联映射(双向)
<set name="roles" table="t_user_role">
<key column="userid"/>
<many-to-many class="com.bin.hibernate.Role" column="roleid"/>
</set>
table属性必须和单向关联中table的名称相同
<key>中的column属性值必须等于单向关联中<many-to-many>标签指向的column的属性值
<many-to-many>中column属性值必须等于单向关联中<key>中column的属性值