1.1什么是hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。简单说,hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成的操作,就是与数据库的交互操作.它是在dao层去使用的.
1.2什么是orm
对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.
ORM模型的简单性简化了数据库查询的过程.使用ORM查询工具,用户可以访问期望数据,而不必理解数据库的底层结构.
简单说,我么使用orm可以将我们的对象和我们的类去进行映射,是的我们可以去操作类/对象就可以完成对表的操作.
1.3为什么使用hibernate
Hibernate对jdbc访问数据库的代码进行了封装,大大简化了数据访问层繁琐的重复性代码.
Hibernate是一个基于jdbc的主流持久化框架,是一个优秀的orm实现,它很大程度的简化了dao层的编码工作
Hibernate是企业级开发中主流框架.映射的灵活性非常出色.它支持很多关系型数据库.
2.1hibernate下载的文件结构
Documentation目录:存放的就是hibernate相关的文件与api
Lib目录:存放的是hibernate编译和运行所依赖的jar包,其中required子目录下包含了运行hibernate项目必须的jar包
Project目录:存放的是hibernate各种相关的源代码与资源
在lib/required目录中,包含hibernate运行必须的jar包
2.2创建数据库与表
DROP DATABASE IF EXISTS hibernate;
CREATE DATABASE hibernate;
USE hibernate;
CREATE TABLE t_customer(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
address VARCHAR(50)
)
2.3创建实体类
2.4导入hibernate依赖jar包
导入lib/required下所有的9个jar包
导入mysql数据库的驱动jar包
日志相关jar包
2.5hibernate相关配置文件
有两种:
- xxx.hbm.xml 它主要是用来描述类与数据库中的表的映射关系
- hibernate.cfg.xml 它是hibernate框架的核心配置文件
2.5.1映射配置文件
位置:它要与我们的实体类在同一个包下
名称:类名.hbm.xml
约束:
约束位置:
;
3.5.2核心配置文件
它主要是hibernate框架所使用的,它主要包含了链接数据库的相关信息,hibernate的相关配置等.
位置:在src目录下创建一个hibernate.cfg.xml
约束:
约束文件位置所在:hibernate核心jar包下的org.hibernate包下
可以参考\hibernate-release-5.0.7.Final\project\etc\hibernate.properties文件,来进行配置
2.6.快速入门开发测试
2.7hibernate工作原理总结
1、通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
2、由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取解析映射信息。
3、通过config.buildSessionFactory();//得到sessionFactory。
4、sessionFactory.openSession();//得到session。
5、session.beginTransaction();//开启事务。
6、persistent operate;
7、session.getTransaction().commit();//提交事务
8、关闭session;
9、关闭sessionFactory;
3.hibernate配置详解
3.1核心配置文件的配置
对于hibernate的核心配置文件它有两种方式
- Hibernate.cfg.xml
- Hibernate.properties
我们在开发中使用比较多的是hibernate.cfg.xml这种方式,原因是它的配置能力更强,易于修改
我们主要讲解的是hibernate.cfg.xml配置
- 可以加载数据库相关信息
- 可以加载hibernate的相关配置
- 加载映射配置文件
对于hibernate.cfg.xml配置文件中的内容,可以参考/hibernate/project/etc/hibernate.properties的配置
配置这个属性后,我们可以进行表的自动创建
Create-drop 每次都会创建一个新的表,执行完成之后会删除,一般在测试中使用
Create 每次都会创建一个新的表,一般也是在测试中使用.
Update 如果数据库中有表,不创建,如果没有表就会创建,如果映射不匹配,会自动更新表结构(只能够添加)
Validate 只会使用存在的表,并且会对映射关系进行校验
3.2映射文件配置
映射配置文件它的名称是,类名.hbm.xml,它一般放置在实体类所在的包下.
这个配置文件的主要作用是简历表与类的映射关系.
1.可以统一声明包名,这样在<class>中就不需要写类的全路径类名
- 关于<class>标签的配置
name属性:类的全路径类名
table属性:表的名称,可以省略,这时表的名称和类名一致
catelog属性:数据库的名称,可以省略,如果省略,参考核心配置文件中url路径中的库名称
- 关于<id>标签
首先它必须存在.<id>是用于建立类中的属性与表中的主键映射的.
name 类中的属性名称
column 表中的主键名称 column它也可以省略,这是列名和类中属性名称一致
length 表示字段长度
type属性 指定类型
<generator>它是主要描述主键生成策略
- <property>标签
它主要是描述类中其他属性与表中非主键的映射关系
关于hibernate的映射文件中类型的问题
对于type属性的取值,可以有三种
- java中的数据类型
- hibernate中的数据类型
- Sql语句中的数据类型
默认是hibernate中数据类型
4.hibernate常用API
Hibernate的API一共有6个,分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration.这6个核心类和接口在任何开发中都会用到.
4.1configuration
它的主要作用是用于加载hibernate配置
Configuration configure = new Configuration().configure();主要加载src下的hibernate.cfg.xml核心配置文件
Configuration configure = new Configuation(); 主要是加载src下的hibernate.properties
Configuration configure = new Configuration().configure(核心配置文件名称);可以通过configure方法的参数形式传递核心配置文件名称,加载制定配置文件
问题:我们是在hibernate.cfg.xml文件中有xxx.hbm.xml映射文件配置.如果我们使用的是hibernate.properties这种核心配置文件,它如何加载映射配置?
4.2sessionFactory
首先sessionFactory它的获取是通过configuration得到.
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
通过sessionFactory可以得到session.
它其实是从连接池中获取一个连接
获取一个与线程绑定的session
sessionFactory它不是轻量级的,不要频繁的创建关闭它.在一个项目中有一个sessionFactory就可以,通过sessionFactory来获取session进行操作.
问题:怎样可以保证在一个项目中所使用的sessionFactory是同一个了?
sessionFactory内部维护了一个数据库连接池,如果我们要想使用c3p0连接池,应该怎么处理?
- 我们要导入c3p0的相关jar包
在hibernate/lib/optional下有关于c3p0连接池jar包
- 在hibernate.cfg.xml文件中配置c3p0连接
可以查看etc/hibernate.properties中关于c3p0的配置
4.3session
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。
问题:我们如何解决session的安全问题?
我们只需要在方法内部来使用session就可以.
问题:session如何获取到?
sessionFactory.openSession();相当于直接通过sessionFactory创建一个新的session对象,使用完成后要手动调用close来关闭.
sessionFactory.getCurrentSession();获取一个与线程绑定的session,当我们提交或事物回滚之后,session对象会自动关闭.
Session对象中常用的方法:
save: 保存对象
update: 修改操作
delete: 删除操作
get/load: 根据id进行查询
saveOrUpdate: 执行save操作或者update操作
createQuery: 获取一个query对象(hql)
createSQLQuery: 获取一个可以操作sql的SQLQuery对象
createCriteria: 获取一个criteria对象,它可以完成条件查询
4.4transaction
transaction接口主要用于事物管理,它是hibernate的事物接口,对底层的事物进行了封装.使用它可以进行事物的操作.
Commit 事物的提交
Rollback 事物的回滚
问题:如何获取一个transaction对象?
通过session.beginTransaction()得到
问题:如果我们在程序中没有开启事物,是否存在事物了?
有事物的,session的每一个操作就会开启一个事物.
默认情况下,事物是不会自动提交的
默认不自动提交
设置事物自动提交
4.5query
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。
通过query对象主要完成查询操作
Query query = Session.createQuery(hql);
下面sqlQuery 可以执行sql语句
SQLQuery sqlQuery = session.creteSQLQuery(sql);
sqlQuery是query的子类
4.5.1查询所有操作---使用hql
4.5.2分页查询---使用hql
4.5.3查询指定列信息---使用hql
Select name,address from customer; 得到的是list<obj[]>结果
要想得到list<customer>结果
1.在customer类中生成以name,address为参数的构造.注意:无参构造也要有
2.select new customer(name,address) form customer;
4.5.4条件查询---使用hql
可以使用where关键字
无名称参数 from customer where name = ?
对其进行赋值 query.setParameter(0,”张三”);
有名称参数 from customer where name = :myname;
对其进行赋值 query.setParameter(“myname”,”李四”);
如果能够保证查询结果是唯一的,我们可以使用query.uniqueResult()来得到一个单独对象.
4.5.5执行本地sql
要想执行本地的sql
SQLQuery SQLQuery = Session.createSQLQuery(String sql);
使用addEntity方法来将结果封装到指定的对象中,如果不封装,得到的是list<object[]>
如果sql有参数,我们使用setParameter方法来完成参数的赋值
如果结果只有一个,我们可以使用uniqueResult方法来的到一个单独对象
4.6criteria
Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。值得注意的是Criteria接口也是轻量级的,它不能在Session之外使用。
首先我们想要使用criteria,必须得到criteria
Criteria criteria = Session.createCriteria();
查询所有操作
Session.createCriteria(实体类.class)得到一个criteria对象,调用list方法查询所有
分页方法与query的方法一致 setFirstResult()起始位置 setMaxResults()返回结果条数
查询指定列信息
通过projections(映射)对象来完成 如果不指定别名和结果转换条件,返回list<obj[]>
要想返回list<customer>,需要通过指定alias别名,加上调用setResultTransfomer方法指定结果转换类,setResultTransfomer方法的参数为transformers对象的aliasToBean(target.class)方法的返回值
条件查询
criteria.add(Restrictions.eq(“name”,”xxxx”));
criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))
我们使用Criteria可以更加面向对象去操作,它非常适合进行多条件组合查询。
5.1hibernate持久化类
什么是持久化类了?Persistent object PO
PO = POJO + hbm映射配置
对于hibernate中的PO类的编写规则:
- 必须提供一个无参数的public构造方法
- 所有属性必须为private,对外提供public的get/set方法
- 在PO类中必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫做OID.
- PO类中的属性尽量使用基本数据类型的包装类
- PO类它不能使用final修饰
OID作用:OID指的是与数据库中表的主键对应的属性
Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate会认为他们是同一个对象
为什么要使用包装类型?
使用基本数据类型它没有办法去描述不存在的概念,如果使用包装类型,它就是一个对象,对于对象它的默认值为null。
PO类不能使用final修饰?(get/load方法的区别)
Get/load方法都是根据id,去数据库查询对象
- get直接得到了一个持久化类型的对象,它就是立即进行查询操作
load它得到的是持久化类型对象的代理对象(子类对象),它采用了一种延迟策略来查询数据.
- get方法查询的时候,如果不存在返回null
load方法在查询的时候,如果不存在,会产生异常ObjectNotFoundException
5.2Hibernate主键生成策略
Hibernate中定义的主键类型包括:自然主键和代理主键:
自然主键:具有业务含义 字段 作为主键,比如:学号、身份证号
代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:mysql自增主键,oracle序列生成的主键、uuid()方法生成的唯一序列串
建议:企业开发中使用代理主键!
在hbm.xml中可以设置的主键生成策略如下:
主键生成器 |
描述 |
increment |
代理主键。由hibernate维护一个变量,每次生成主键时自动以递增。 问题:如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突。建议不采用。 优点:可以方便跨平台 缺点:不适合高并发访问 |
identity |
代理主键。由底层数据库生成标识符。条件是数据库支持自动增长数据类型。比如:mysql的自增主键,oracle不支持主键自动生成。 如果数据库支持自增建议采用。 优点:由底层数据库维护,和hibernate无关 缺点:只能对支持自动增长的数据库有效,例如mysql |
sequence |
代理主键。Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。比如oracle的序列。 如果数据库支持序列建议采用。 优点:由底层数据库维护,和hibernate无关 缺点:数据库必须支持sequence方案 例如: oracle |
native |
代理主键。根据底层数据库对自动来选择identity、sequence、hilo 由于生成主键策略的控制权由hibernate控制,所以不建议采用。 优点:在项目中如果存在多个数据库时使用方便 缺点:效率比较低 |
uuid |
代理主键。Hibernate采用128(二级制)位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符。 此策略可以保证生成主键的唯一性,并且提供了最好的数据库插入性能和数据库平台的无关性。建议采用。 优点:与数据库无关,方便数据库移植,效率高,不访问数据库就可以直接生成主键值,并且它能保证唯一性. 缺点:uuid长度大(32位),占用的空间较大,对应数据库类型char varchar |
assigned |
自然主键。由java程序负责生成标识符。 不建议采用。 尽量在操作中避免手动对主键操作. |
6.1持久化对象的三种状态
有三种:
- 瞬时态: 也叫做临时态或自由态,它一般指我们new出来的对象,它不存在oid,与hibernate session无关联,在数据库中无记录.它使用完成后,会被jvm直接回收掉,它只是用于信息携带.
简单说: 无oid, 与数据库中的信息无关联, 不在session的管理范围内
- 持久态: 在hibernate session管理范围内,它具有持久化标识oid 在事物未提交之前一直是持久态,当它发生改变的时候,hibernate是可以检测到的.
简单说: 有oid 由session管理 在数据库中可能有,也有可能没有
- 托管态: 也叫做游离态或者离线态,它是指持久态对象失去了与session的关联,托管对象它存在oid,在数据库中有可能存在,也有可能不存在
对于托管态独享,它发生改变时,hibernate不能检测到.
6.2持久化对象三种状态切换
判断持久化类对象的三种状态:
- 是有oid
- 判断是否与session有关联
- 瞬时态(new出来的对象)
瞬时---------------->持久 save saveOrUpdate
瞬时---------------->托管(游离) 手动设置oid
- 持久态 它是由session管理
持久---------------->瞬时 delete() 被删除后持久化对象不再建议使用
持久---------------->托管 注意:session它是有缓存的,叫做一级缓存
evict(清除一级缓存中指定的一个对象)
clear(清空一级缓存)
close(关闭,清空一级缓存)
- 托管态 (它是无法直接获取)
托管---------------->瞬时 直接将oid删除
托管---------------->持久 update saveOrUpdate lock(过时)
7Hibernate的一级缓存
Hibernate的一级缓存就是指session缓存
actionQueue它是一个行动队列,它主要记录的是crud操作的相关信息
PersistenceContext它是持久化上下文,它其实是真正的缓存
在session中它定义了一系列的集合来存储数据,它们构成了session缓存
只要我们的session没有关闭,它就会一直存在.
当我们通过hibernate中的session提供的一些api进行操作的时候,例如save get update等进行操作,就会将持久化对象保存到session中,当下一次再去查询缓存中具有的对象(oid来判断),就不会再去数据库中查询,而是直接从缓存中获取.
Hibernate的一级缓存存在的目的就是为了减少数据库的访问.
在hibernate中还有一个二级缓存,它是sessionFactory级别的缓存.
7.1示例-------------->演示一级缓存的存在
7.2持久化对象具有自动更新数据库的能力
为什么持久化对象具有自动更新数据库的能力?
8.3一级缓存常用api
一级缓存的特点:
- 当我们通过session的save,update,saveOrUpdate进行操作的时候,如果一级缓存中没有对象,会将这些对象从数据库中查询得到,存储到一级缓存.
- 当我们通过session的load,get,query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询到的数据存储到一级缓存中.
- 当调用session的close方法时,session缓存会清空.
Clear会清空一级缓存
Evict会从一级缓存中删除一个指定的对象
Refresh会重新查询数据库,用数据库中信息来更新一级缓存与快照
8.4hibernate的二级缓存
二级缓存是一种名称的定义,指在已有缓存的基础上,再增设一种缓存,二级缓存只为一级缓存服务,它实现了让同一个sessionFactory造成的session之间数据实现共享sessionFactory,它弥补了一级缓存单词存储数据量小、生命周期短、不同Session间数据无法共享的 缺陷,二级缓存可以实现多次请求操作间的数据共享,可以有效减少访问永久介质的次数。
hibernate本身不支持二级缓存,只是提供了二级缓存的接口,由不同的缓存供应商提供缓存,目前hibernate支持的二级缓存主要有四种:
*ehcache
*OpenSymphony
*SwarmCache
*JbossCache
2.二级缓存中对数据的要求
1) 适合加入二级缓存的数据
*很少被修改的数据(因为存在数据更新不及时或更新失败的问题)
*不是很重要的数据
2) 不适合加入二级缓存的数据
*经常被修改的数据
*财务数据,绝对不允许出现并发
3. 二级缓存的内部结构供程序使用的共4个部分(内部结构还有其他的部分)
二级缓存执行原理
先将数据加载进入一级缓存,再加载进入二级缓存,再由一级缓存返回给客户端,拿数据会先找一级缓存,有则返回,没有则接着找二级缓存,找到则返回给一级缓存,由一级缓存返回给客户端,如果一级、二级缓存中都没有数据查找数据库,二级缓存不直接与客户端打交道,只跟一级缓存打交道,如果是HQL语句则直接查找数据库
8.5hibernate的一级缓存跟二级缓存
二级缓存因为是sessionFatory级别,所以可以解决不同的session不会共享缓存数据的缺点。
一级缓存的list和iterator的区别?
list会 一次性把所有的记录都查询出来了;会放入缓存,不会从缓存中取数据;
iterate(N+1次查询) N表示所有的记录总数,即会发送一条语句查询所有的记录的主键,这是第一条查询语句,再根据每一个主键取数据库查询,这是根据第一次查询的条数进行N次查询操作;会放入缓存,也会从缓存中取出数据。
这个时候就可以用使用二级缓存, 二级缓存的应用将不怕1+N 问题,因为即使第一次查询很慢(未命中),以后查询直接缓存命中也是很快的。刚好又利用了1+N 。但是此问题只存在于第一次查询时,在后面执行相同查询时性能会得到极大的改善。此方法适合于查询数据量较大的业务数据。
但是注意:当数据量特别大时(比如流水线数据等)需要针对此持久化对象配置其具体的缓存策略,比如设置其存在于缓存中的最大记录数、缓存存在的时间等参数,以避免系统将大量的数据同时装载入内存中引起内存资源的迅速耗尽,反而降低系统的性能!!!
8.6hibernate常用api-session补充
Update
Update操作它主要是针对于托管对象,持久对象具有自动更新能力.
问题1:如果我们直接操作的对象是一个托管对象,执行update会出现什么情况?
Update操作时,如果对象是一个托管对象,可以操作,它会将托管对象转换成一个持久化对象再操作
如果session中出现相同oid的两个对象,会产生异常
问题2:托管对象的oid如果再数据库表中不存在,会不会报异常?
所以,在实际开发操作中,建议我们通过持久化对象来直接修改其操作.
SaveOrUpdate
如果对象是一个瞬时对象---------->执行save操作
如果对象是一个托管对象---------->执行update操作
如果对象是一个持久对象---------->直接返回
Delete
删除一个托管对象,先与session关联,然后再进行删除.
注意:如果执行delete操作,先删除一级缓存,再删除数据库中的数据.