一、Hibernate简介
Hibernate是一个持久层的ORM框架
Hibernate用于简化DAO层的操作,封装了JDBC的操作。
ORM 对象关系映射
将java的对象与关系数据库进行映射
类名映射为表名 类中属性映射为表的字段 对象映射为表中记录
使用Hibernate进行数据库操作时,只需要操作对象即可,无需考虑SQL语句,很好的体现面向对象的思想。
二、Hibernate框架的使用
简单概述为三大准备、七大步骤
2.1 三大准备
1、准备Hibernate必须要的jar包,在Hibernate下载资料,lib文件夹下的必须目录中。
2、准备配置文件 hibernate.cfg.xml
配置文件中主要配置 数据库方言,表示使用什么数据库 ,数据库的连接信息,javabena的映射文件
3、准备映射文件
po类与表的映射关系文件,就是类与表的映射 类中属性与表字段的映射
2.2 七大步骤
1、加载配置文件
Configuration config = new Configuration().configure();
2、获取会话工厂
SessionFactory sessionFactory = config.buildSessionFactory();
3、获取会话
Session session = sessionFactory.openSession();
4、开启事务
Transaction transaction = session.beginTransaction();
5、执行操作
session.sava() get() update() delete()等
6、提交或回滚事务
transaction.commit();transaction.rollback();
7、关闭连接
session.close();
三、Hibernate的API
3.1 Configuration
作用是用于加载Hibernate的配置文件,configure()默认加载的是hibernate.cfg.xml文件
configure("文件名");可以记在指定名称的配置文件
3.2 SessionFactory
由Configuration加载配置文件创建该对象
SessionFactory对象中保存了当前的数据库配置信息和预定义SQL语句,同时SessionFactory
负责维护Hibernate的二级缓存
一个SessionFactory对应一个数据库实例。
SessionFactory 是线程安全的,意味着一个实例可以被并发访问。
3.3 Session
Session是会话实例,类似于连接,不是线程安全的,并发访问会出现安全问题。
Session维护着Hibernate的一级缓存
对象与数据库的操作都是通过Session对象实现的
Session session = sessionFactory.openSession();
Session session = sessionFactory.getCurrentSession();
openSession与getCurrentSession()的区别
openSession是每次都获取一个新的session对象
currentSession是获取与当前线程绑定的session对象
currentSession获取到的session对象,当事务提交时,自动关闭。
3.4 Transaction
事务
Transaction transaction = session.beginTransaction();
提交事务transaction.commit();回滚事务transaction.rollback();
四、Hibernate的对象状态以及一级缓存
4.1 对象的三种状态
瞬时态:新建的对象,和session没有进行关联,是瞬时态
持久态:对象与session进行关联,变成持久态,例如使用sav() update()方法
用get/load方法获取的对象直接是持久态
游离态:持久态的对象在session关闭之后,处于游离态或者叫托管态。
游离态和瞬时态的区别在于游离态的对象可能在数据库中有响应的对应。
持久态的对象的更新操作,不需要调用update方法就可以自动更新到数据库,产生原因是一级缓存的存在
4.2 一级缓存及快照区
1、持久态对象自动更新
一级缓存由Session对象进行维护。每一个持久化对象都会在一级缓存的快照区中保存一个副本,一级缓存中保存对象,
当事务提交时,会将快照区的对象与一级缓存中的对象进行比较,当两个对象不一致时,会执行更新操作。
4.3 一级缓存的好处
减少访问数据库的频率,当一级缓存中有需要的数据时,直接从一级缓存中取数据,提高了执行效率。
可以保证数据库中的相关记录与缓存中的对象保持同步。
4.4 一级缓存的常用方法
clear()清空一级缓存
evict(obj)清空一级缓存中的指定实体对象。
flush()将缓存中的数据同步到数据库,也即将sql语句同步到数据库,并执行sql语句,但事务未提交时,
数据不会保存到数据库。
refresh() 重新刷新缓存,将数据库中的数据填充一级缓存。
五、Hibernate数据的检索方式
5.1 get/load方法
get/load方法只能通过主键进行查询。
get/load的区别
1、get是立即加载,立即执行sql语句
load是延迟加载,当需要使用对象时才执行sql语句
2、get是返回的实体类对象,load是返回的实体类的代理对象
3、get如果查询到数据库中没有的数据,会返回null
load如果查询到数据库中没有的数据,会报对象未找到异常。
5.2 QBC查询
1、获取Obc查询对象
Criteria criteria = session.createCriteria(实体类.class)
//添加条件进行查询 gt 大于 ge大于等于 le小于等于 lt小于 In and or like
criteria.add(Restrictions.gt(列名,条件值));
//分页查询
pegeSize 每页个数 pageNumber 页码
criteria.setMaxResults(pageSize);
criteria.setFirstResults(pageSize*(pageNumber-1));
//排序 criteria.addOrder(Order.desc("custId"));
//查询所有
List<实体类> list = criteria.list();
5.3 HQL查询
相比于SQL查询,不需要关注表,只需要关注类和属性即可。
[Select 属性名] from 类名 where条件(属性=值1)
//1获取HQL对象
Query query = session.createQuery(HQL语句);
List<> list = query.list();
5.3.1HQL语句加强
1、String hql = "from Customer" 语句正确 返回的是所有Customer对象
2、String hql="from Customer where custId=3" 语句正确查询为id=3的Customer对象
3、String hql ="select * from Customer" 语句错误,因为类中没有*对应的属性
4、String hql = "select custName from Customer" 语句正确 返回的结果是 custName String类型
5、String hql = "select custId,custName from Customer" 语句正确,返回的结果是数组,第一个元素为custID,第二个为custName
6、String hql = "select new Customer() from Customer" 语句错误
7、String hql = "select new Customer(custId) from Customer" 错误,必须要有对应的构造函数
8、String hql = " from Customer as c order by c.custId";正确,返回的是Customer对象并排序
9、String hql = "from java.lang.Object" 多态查询,查询所有的持久化对象
10、String hql = " from Customer c inner join c.linkmans" 语句正确、显示外连接
11、String hql = "from Customer c,LinkMan l where c.custId=L.customer.custId"语句正确、隐式外连接
12、String hql = " from Customer c inner join LinkMan l on c.custId=L.customer.custId" 语句错误
进行内联接操作时,虽然查询了linkman,但并没有将linkman赋值到customer中,使用连接查询,返回的是数组,两个元素分别为对应的对象
13、配合fetch迫切 使用 String hql = " from Customer c inner join fetch c.linkmans"
将查询到的linkman封装到对应的customer中,返回的结果是customer对象
14、 String hql = " from Customer c left join fetch c.linkMans";使用左外连接进行查询的时候如果一个客户有多个联系人,这个客户就会
查询多次,使用distinct去重就可以了 String hql = "select distinct c from Customer c left join fetch c.linkMans ";
5.4 SQL查询
//1 h获取SQL对象,同时将获取到的列绑定到对应的类的属性上
SQLquery sql = session.createSQLQuery(sql).addEntity(类名.class);
5.5 对象导航
对象导航是多表关联的情况,通过配置映射文件的多表关系,在查询一个Po类的对象时,
会把和它相关联的po类的对象查询出来,具体操作,参照Hibernate的映射关系配置。
6、Hibernate的多表映射
6.1 一对多关系映射
1、 一方配置方式
<set name="linkMans" cascade="all" inverse="true"> <!-- 类中集合 name为实体类中集合的名称-->
<!-- cascade表示级联关系,inverse=true表示让出关系维护权 -->
<key column="lkm_cust_id"></key> <!-- 外键名称 -->
<one-to-many class="cn.lx.domain.LinkMan"/> <!-- 外键所属表映射的实体类 -->
</set>
一方实体类中有个集合对象用于存储对应的多方对象
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
2、多方配置方式
<many-to-one name="customer" class="cn.lx.domain.Customer">
<!-- column为对应的外键字段 -->
<column name="lkm_cust_id"></column>
</many-to-one>
多方中有个一方对象的属性
private Customer customer ;
3、级联
级联的作用就是在操作一个对象的同时可以操作另外一个对象。
级联cascade取值的介绍
save-update: 级联保存(load以后如果子对象发生了更新,也会级联更新). 但它不会级联删除
delete: 级联删除, 但不具备级联保存和更新
all-delete-orphan: 在解除父子关系时,自动删除不属于父对象的子对象, 也支持级联删除和级联保存更新.
all: 级联删除, 级联更新,但解除父子关系时不会自动删除子对象.
delete-orphan:删除所有和当前对象解除关联关系的对象
4、控制权inverse
inverse 表示控制权转换,默认为false,不让出控制权,一般情况下一对多关系由多方进行维护双方关系
6.2 多对多关系映射
多对多关系的配置
name表示该方集合的名称
table表示中间表名称
key-column 表示该方在中间表中对应的外键字段
many-to-many表示配置多对多关系
class表示集合中存储的对象的所属的类,也即多对多的另一方
column表示另一方在中间表中的外键字段
<set name="user" table="USER_ROLE" inverse="true" >
<!-- role_id 为role实体类对应的中间表的外键名称 -->
<key>
<column name="ROLE_ID" />
</key>
<!-- many-to-many是配置多对多关系,class为集合中所对应的实体类
user_id为该实体类在表中对应的外键字段
-->
<many-to-many class="cn.lx.domain.User" column="USER_ID" />
</set>
多对多关系中必须有一方让出控制权。多对多关系一般不设置级联,由程序员自己维护
6.3 一对一关系映射
一对一关系,一般不需要配置,由程序员自己维护两表关系
一个表中的主键采用identity生成,另外一个表采用assigned程序员自己指定。
7、Hibernate的数据抓取策略
####1.no-session 问题
7.1 no-session问题的产生
异常名称:org.hibernate.LazyInitializationException: could not initialize proxy - no Session
1、no-session问题产生的原因是获取数据使用延迟加载数据的方式,当使用数据前关闭session的,再使用数据时,数据不会加载,进而报错
7、1.2 解决no-session问题
1、在session关闭之前,直接使用数据,这时数据会加载完成,此后再使用数据就是完整数据
7.2 数据的抓取策略
提高执行效率以及解决no-session的问题
常用的抓取策略
抓取策略可以配置在类上,表示类的抓取策略,也可以配置到set或者one-to-many等上表示对方的抓取策略
lazy属性在one-to-many中有三个属性 false、proxy
many-to-many 有三个属性 false,true,extra
1、set节点中配置抓取策略
fetch:"join" 抓取时使用左外连接 使用join可以提高查询效率,因为执行的sql语句相对较少
fetch:"select" 抓取时使用多条SQL语句 相比于join效率低一些
使用HQL/SQL查询 fetch="join"失效,lazy属性有效,因为HQL和SQL已经指定了语句,按指定的语句操作
使用get/load或者QBC 查询时,使用hibernate底层的语句查询 lazy属性无效,fetch属性有效
lazy="false"是否懒加载,延迟加载 false表示不延迟加载 true表示延迟加载 extra 极其懒惰,除查询属性外,查询其他的内容,是不查询属性。
lazy="proxy" 代表代理,就是由对方来执行抓取策略,也即由对方的抓去策略来执行
fetch决定的是sql语句的格式 ,lazy决定的是sql语句出现的时机
2、many-to-one 节点中配置抓取策略
抓取策略是当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略。
抓取策略包括:
查询时机:延迟加载、立即加载
查询方式:单表查询、多表查询
抓取策略可以在hbm映射文件中声明,也可以通过在HQL 语句定义。
QBC、get查询方式使用hbm映射文件中定义的抓取策略进行。
Hql不能使用hbm文件中定义的抓取策略,它通过迫切内连接、迫切外连接方式实现抓取策略。
7.2.1 类抓取策略
设置类级别抓取策略 ,修改 hbm文件 <class>元素 lazy属性,默认为true延迟加载,修改为false 立即加载。
如果修改为false则get方法和load方法一样都是立即加载。
如果采用默认true,get立即检索,load延迟检索。
7.2.3 关联抓取策略
当通过对象导航方式查询关联对象时可以设置数据抓取策略。比如:调用 customer.getCstLinkmans()获取cstLinkmans信息时通过抓取策略修改数据的获取方式。
7.2.3.1 一对多、多对多
<set> 元素提供 lazy属性 和 fetch 用于设置 抓取策略
Fetch(默认select) Lazy(默认true) 策略
join True 采用迫切左外连接
join false 采用迫切左外连接
select true 延迟检索
select false 立即检索
fetch=join
fetch="join"实现批量抓取。
如果fetch设置为join,lazy不起作用。
get、QBC、HQL都支持fetch方式。
get和QBC方式会采用左外连接SQL方式,fetch=join时会忽略lazy。
测试:查询客户关联查询联系人,设置fetch=join
fetch=select (默认值 )
生成多条SQL语句
lazy=true 延迟加载 (默认值 )
lazy=false 立即加载
测试:查询客户关联查询联系人,设置fetch=select lazy=true、fetch=select lazy=false
7.2.3.2 多对一
多对一, hbm采用 <many-to-one>元素,提供lazy属性和fetch属性,用来配置抓取策略
Fetch(默认select) Lazy(默认proxy) 策略
join True 采用迫切左外连接
join false 采用迫切左外连接
select false 立即检索
select proxy 按照class中设置lazy执行
Class中设置为 true则为延迟加载
Class中设置为false则为立即加载
fetch=join 采用迫切左外连接查询
测试:查询客户关联查询客户来源,设置fetch=join
fetch=select (默认值)
产生多条SQL语句
Lazy=false表示立即加载
lazy=proxy表示:
按照class (被关联对象的class) 中设置lazy执行
Class中设置为 true则为延迟加载
Class中设置为false则为立即加载
测试:查询客户关联查询客户来源,设置fetch=select lazy=proxy、fetch=select lazy=false
7.3 批量策略
N+1问题:
比如说查询customer表时,查询所有cutomer,会输出一条sql语句,同时查询其对应的linkman时,会每条customer都查询一次sql语句,会产生N+1条sql语句,
效率相对较慢,可以使用批量增加策略
解/决方案在 set节点上配置batch-sezi="10",这样表示一次查询10个客户的联系人,减少了SQL语句,大大提高执行了效率
补充知识
1、Hibernate主键的生成策略
配置映射文件时,id标签表示字段,里面有generator属性表示主键的生成策略
有以下取值:
increment 表示每次自增1,由数据库进行维护
identitity 表示自增 ,由Hibernate进行维护
native 根据底层情况,Hibernate自动维护
assigned表示由程序员进行维护
uuid 表示根据算法自动生成、
2、使用Hibernate时,一般需要设置一个工具类HibernateUtils
在工具类的静态代码块中创建SessionFactory对象
里面有两个方法,返回openSession对象和返回getCurrentSession对象
public class HibernateUtils {
private static SessionFactory sessionFactory = null;
static{
Configuration config = new Configuration().configure();
sessionFactory = config.buildSessionFactory();
}
public static Session getSession(){
return sessionFactory.openSession();
}
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}
3、Hibernate事务
事务的隔离性,如果没有考虑,可能产生一些情况:
事务的四个特性:原子性、一致性、隔离性、持久性。
脏读
不可重复读
虚读
就可以设置一些隔离级别,
READ UNCOMMITED 可以产生脏读 1
READ COMMITED 可以防止脏读 (Oracle) 2
REPEATABLE READ 可以防止脏读,不可重复读,虚读依然可以产生 (Mysql) 4
SERIALIZABLE 什么都防 8
配置文件中进行配置
<property name="hibernate.connection.islation">4</property>
丢失更新问题:解决方案如下:
锁机制(增删改本身带锁)
查询命令带锁:select * from customer for update;(悲观锁)
Hibernate要解决这个问题,也可以使用乐观锁
在映射文件中,加入一个version版本字段,hibernate就会自动维护
同时在配置文件中进行配置
<version name="version"></version>
当实体类中有version属性时,进行删除和修改操作时,需要先查询表中内容,再进行修改,要不然会报空指针异常
4、二级缓存