一. Hibernate基本概念
1. Hibernate框架的作用和特点
Hibernate主要负责对数据库进行访问,它是对JDBC的封装。
2. Hibernate框架基本原理
ORM--ObjectRelation Mapping 对象关系映射
主要思想:将Java对象与关系表进行自动映射,这样可以直接将对象更新到数据库;查询时可以自动将数据库表记录封装成Java对象。
Hibernate框架基于ORM原理实现,Hibernate是目前主流的ORM工具,此外还有MyBatista,JPA等。
3. Hibernate主要的体系结构
实体类(Xx):
可以有N个,与数据库中的表对应,用于封装数据库表的一行记录。
XML映射文件(Xx.hbm.xml):
可以用N个,主要描述实体类与数据库表之间的对应关系;类属性对应表字段。
主要配置文件(hibernate.cfg.xml):
可以有1个,用于指定连接数据库的参数,框架参数等。
4. Hibernate核心API
a:Configuration :用于加载主配置文件和映射文件
b:SessionFactory :用于创建Session对象
c:Session :原Connection对象的封装,代表Hibernate与数据库之间的一次连接,负责执行增删改查操作。
d:Transaction :用于进行事务管理,由于自动提交功能,默认被Hibernate关闭,因此使用时必须显式commit操作。
e:Query :负责执行各种查询。
5. Hibernate生成主键的方式
常用的生成主键的方式有如下几种:
identity:用于自动生成主键方式,除了Oracle不支持,其他数据库一般都支持。
<generatorclass=”identity”></generator>
sequence:Oracle中使用,用序列(sequence)生成ID主键
<generatorclass=”sequence”>
<paramname=”sequence”>指定列名</param>
</generator>
native:生成主键的方式如果是native则表示由Hibernate决定,Hibernate会根据配置文件hibernate.cfg.xml中方言<propertyname="dialect">是什么,如果方言是Mysql,相当于identity,如果方言是Oracle,相当于sequence。
<generatorclass=”native”></generator>
<generatorclass=”native”>
<paramname=”sequence”>指定列名</param>
</generator>
increment:取出现有表中的主键的最大值,家1后作为新的主键值,不常用;
不安全,容易产生并发冲突(若并发操作几率较高时不建议使用)
assigned:手动生成id,也就是需要程序中定义一个主键,不常用。
其他:UUID,hilo(高低位算法)
6. 数据映射类型
7. Hibernate对象状态:
暂时态:Transient
使用new操作符初始化的对象的状态是瞬时的,如果没有任何跟数据库表相关联的行为,只要应用程序不再引用这些对象,它们的状态将会丢失,并由垃圾回收机制回收,这种状态被称为暂时态。
持久态:persistent
如果内存中的对象和数据库的记录有对应关系,即和session对象相关,则此对象处于persistent状态,当事务提交时它们的状态和数据库进行同步。
游离态:Detached
Session关闭之后,持久化对象就变为detached对象,表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。
另外当调用了session.evic(Objectobj)方法,对象和session解除了关系,也将处于游离态。
二. 缓存:
8. 一级缓存(session级别缓存):
(1) 如果通过session查询某对象,session先到缓存中查找是否有被查询的对象,找到则直接取出,否则才查询数据库。
(2) Session需要负责实时维护在缓存中的数据,保证缓存中的数据与数据库中数据的一致性,一旦用户对缓存中的数据作了修改,session负责将数据更新到数据库中。
缓存的好处:减少对数据库的访问次数。
提示:
A:同一个session访问一个对象多次,只能对数据库访问一次。
B:每个session只能访问自己的缓存区,不能交叉访问。
C:同一个session访问不同的对象,查询数据库多次。
缓存的生命周期:
Session创建,缓存创建;
Session关闭,缓存释放。
循环多次不及时释放缓存会发生内存溢出异常。
Eg:
for(Stringid : ids){
Foofoo = (Foo)session.load(Foo.class,id);
session.evict(foo);// 应及时释放缓存
}
Session.clear(); //清除缓存中的所有对象
Session.close();//关闭缓存
9. 延迟加载
A:延迟加载:在使用Hibernate方法查询数据库对象时,Hibernate返回的对象没有立刻加载数据库中的数据,而是使用对象的getter方法触发SQL查询语句,去数据库端执行返回数据库内容。
B: 什么操作具有延迟加载:
(1) session.load()
(2) query.iterator()
(3)关联属性加载
C:注意:采用延迟加载机制要避免session关闭过早,如果session关闭过早会发生下面异常:
Org.hibernate.LazyInitialization:cloudnot initialize proxy-no session.
遇到异常,解决方法:
(1) 采用非延迟加载机制的操作:
eg: session.get() query.list()等
(2) 将session关闭放到POJO对象使用之后(推荐使用)
(3) 在项目中常使用OpenSessionInView模式控制session关闭。
C:延迟加载的好处:
可以提升内存资源的使用率
可以降低对数据库的并发访问
D:OpenSessionInView实现方法:
Servlet—过滤器
Struts2—拦截器
Spring—AOP
E:OpenSessionInView和ThreadLocal
使用OpenSessionInView必须满足Session的线程单例:一个线程分配一个session,在该线程的方法中可以获得该session,具体事宜ThreadLocal—其实是一个线程为key的map,
Hibernate支持的session线程单例
配置文件中:
<propertyname=”current_session_context_class”>thread</property>
然后调用:
sessionFactory.getCurrentSession()
自动实现线程单例。
三. 关联映射
1. Hibernate关联映射作用
如果数据表之间有关联字段,都可以采用Hibernate关联映射在POJO之间建立关系,这样在Java代码里可以通过POJO之间的关系进行数据的增删改查等级联操作。
2. 如何建立关联映射
--前提:表之间有关联字段
--然后:判断表之间的关联属性
--then:在POJO实体类中添加关联属性
--end:在hbm.xml中描述属性的映射信息。
3. 一对多关联(one-to-many)
1) 关联条件(根据E-R和表说明文档)
accout_id(主键,1) service.accout_id(外键,n)
2) 关系类型(根据需求)
accout-àservice是一对多关系
3)追加POJO属性
要根据Accout对象,找出相关service信息。因此需要建立accout——>service的一对多关联映射。在Accout中添加关联属性,将通过该属性的getter方法获取相关的service。
4) 在hbm.xml中描述相关属性
<setname=”service”>
<keycolumn=”account_id”></key> // 用于指定关联条件,写关联条件的外键字段
<ont-to-manyclass=”POJO.Service” /> //用于指定采用哪种关系,加载哪方数据
</set>
4. 多对一(many-to-one)
多对一映射描述:
<mang-to-one
name=”account”
column=”account_id”
class=”POJO.Account”>
</many-to-one>
5. 关联操作
1) 关联查询
默认:关联属性数据采用延迟属性加载,单独发送一个SQL语句查询关联数据;
如果需要当前对象和关联属性一起采用一个SQL语句,可以采用下列方法:
A:修改hbm.xml
Lazy属性:默认是true(延迟加载),false表示关闭延迟加载
Fetch属性: join表示采用表连接方法对主对象一起查询
Select(默认)表示单独发送一个sql关联数据
B:编写HQL,使用join fetch关键字
Fromaccount a join fetch a.service where a.id=?
上述HQL在调用Account对象时,将service关联属性数据采用表连接方法并查。
2) 级联添加
当执行session.save(account)如果将service关联属性设置了级联操作,那么除了将Account写入数据库Account之外,也会将Service中的service对象写入数据库Service表中。
使用注意事项:
A:需要在关联属性hbm.xml描述中添加cascade属性,指定级联操作类型
None:默认,不支持级联操作
Save-update:支持级联操作的添加和更新
Delete:支持级联删除
All:支持级联添加,更新,删除
B:将关联对象放到关联属性中,指定关联关系
3) inverse属性
默认情况下Account和Service对象之间的关系由双方负责维护工作:就是对Account和Service对象做级联操作时需要执行update语句。
将关联字段设置相同id,如果需要取消某一方的关系维护工作,可以在关联属性映射部分添加inverse=“true”设置,这样可以避免update语句执行。
提示:inverse=“true”通常加在<ont-to-many>映射部分,这样在对象级联操作时可以避免大量update。
4)级联删除
Session.delete(obj)obj需要持久对象,不能new,需要load/get取出。
批量删除方法:
级联删除采用n+1个delete语句清除主表和外键表的关联数据。如果是批量删除,不推荐级联删除,建议使用HQL编写delete语句。
例如:
Delete fromService where account.id=?
Delete fromAccount where id=?
6. 多对多关系
Admin-info admin_role role
1) 关系建立
A)在POJO中添加set集合属性
B)在hbm.xml中添加属性的映射描述
<set name=”roles” table=”admin_role”> //roles 属性名 admin_role 中间关系表id
<key column=”admin_id”></key> //关系表与当前class关联字段
<many-to-many class=”POJO.Role” column=”role_id”>
</many-to-many>
</set>
四. Hibernate查询
1. Hibernate提供的数据库访问方式:
1) HQL(Hibernate Query Language)
属于面向对象查询语句,针对Hibernate映射过来的POJO进行查询,实现对数据库的查询。
2) HQL与SQL相比
A)相似点:
1) HQL支持SQL中的select ,where ,from ,group by , having ,order by 等字子句。
2) 支持SQL中的> , <, >= , <= ,in , not in, between ,and ,like等查询条件子句。
B)不同点:
1) HQL区分大小写(关键字不区分)
2) HQL语句使用的是POJO类型名和属性名,不能使用表名和字段名
3) HQL不支持select * 写法,但支持 select count(*) 写法
From user / select u from user u
4) HQL不支持join on 中的on子句
5) HQL不支持数据库函数
Eg:字符函数,日期函数 to_date to_char
6)
2. Hibernate高级特性
1) 二级缓存(默认是关闭的)
二级缓存是SessionFactory级别的,由SessionFactory对象管理。
特点:
——可以跨越session,不同session对象可以可以访问同一二级缓存空间。
适用环境:
——对象数据频繁访问(共享),对象数据更新频率低
使用方法:
——引入ehcache.jar,在src下添加ehcache.xml缓存配置文件
——在Hibernate.cfg.xml中开启二级缓存,指定采用二级缓存组件的驱动
——在要缓存的POJO的hbm.xml中添加<cache>元素
<cache usage=”read-write”region=”simplecache”>
2) 查询缓存(默认关闭)
一二级缓存只能换存单个对象,如果遇到某个字符串结果,数组结果或者List集合,可以使用查询缓存。
使用方法:
——缓存结果:对象类型,首先要启用二级缓存
——在Hibernate.cfg.xml中开启查询缓存
——在执行query.list()之前设置query.setcacheable(true)
提示:查询缓存是以二级缓存为基础,需要首先开启二级缓存。
使用环境:
需要频繁执行相同的查询语句
查询结果集内容改变频率小
结果集数量不要太多
提示:相同SQL,第一次去数据库查,后续从缓存中查,缓存的是SQL+结果集
一级缓存:单用户
二级缓存:多用户
查询缓存:多用户的结果集
3)事务并发处理
两种处理事务并发的方法:悲观锁和乐观锁
1) 悲观锁
在查询数据时枷锁,其他线程不能对该数据作任何操作,当前事务结束后其他线程才可以访问。
Hibernate本身没有提供悲观锁实现,而是采用数据库锁机制实现的。
Session.load(Service.class,LockMode.UPGTADE);
在发生sql语句时,添加forupdate关键字,这样数据库就会锁住当前记录。
特点:安全性高,因为是排他处理,需要依次排队处理,因为处理效率低。
2) 乐观锁
在用户并发量很高的情况下,可以提高处理效率。当多个用户同时对某个对象做更新操作时,第一个更新操作提交的用户会成功,后需提交的用户失败。
该机制借助一个版本字段实现,当第一个用户更新提交后,Hibernate会将该字段更新+1,这样后续用户提交的对象版本字段比数据库中的小,会抛出异常。
乐观锁的使用方法:
——为数据库中添加一个版本字段t_version
——在POJO对象中添加一个version属性。
——在hbm.xml文件中,利用<version>定义版本控制字段。
悲观锁和乐观锁的选择:
如果并发用户排他处理,一个成功,其他失败选择乐观锁。
如果对并发用户依次处理,都完成具体操作,可以选择悲观锁。