1`hibernate的定义
- 是一个持久层框架
- 是一个ORM框架
- 对jdbc的封装(记忆)
- 用面向对象的思想,操作关系型数据库。(体会)
2`hibernate的配置文件:
映射文件
class标签
处理类与表的映射关系 name: 类的全路径名称 table:对应的表的名称(如果与类名形同,可以省略) catalog: 指定表所在的数据库
【实战】name,table
id标签
处理主键字段的映射关系 name:主键对应的类中属性的名字 column:表中主键的名字 type:类型,指定字段的类型,表达方式有两种:java方式,hibernate方式 举例:类型是整型: java: java.lang.Integer hibernate: integer length: 长度,指定字段的长度。(在自动建表的时候,才会体现)
【实战】name,column
property
处理普通字段的映射关系 name:类中属性的名字
column:表中字段的名字 type:类型,指定字段的类型,表达方式有两种:java方式,hibernate方式 举例:类型是整型: java: java.lang.Integer hibernate: integer length: 长度,指定字段的长度。(在自动建表的时候,才会体现)
not-null: 非空约束(自动建表的时候,体现) unique: 唯一约束(自动建表的时候,体现)
【实战】name,column
3`核心配置文件(hibernate.cfg.xml)
- 连接数据库的参数配置
- 名称的来由(连接数据库的那些配置从哪找):hibernate-release-5.0.7.Final\project\etc\hibernate.properties
自身的参数
必选:方言配置
指定底层的数据库。针对性的生成对应的SQL,从而实现跨数据库特性。
- 跨数据库:用mysql, 切换成oracle.
- 不同的数据库,SQL语句存在不同(部分不同,大部分相同的)
- 最典型的例子:分页:select * from user limit 5,5;
oracle: 特别复杂。select * from (select rowid as num , a.* from user a where rowid <10) b where num >5;
可选:
显示sql语句
自动建表
工作中,一般使用none, validate。
<!-- 可选:是否自动建表 none:默认值 create:启动时,自动建表(测试) create-drop:启动时,自动建表,关闭时,自动销毁表(测试) update:没有表的时候,自动建表。有表,使用原来的表。如果表结构不匹配,更新表结构(测试) validate:不会自动建表。如果有表,检查表的结构,如果,不匹配,报错。 --> <property name="hibernate.hbm2ddl.auto"></property>
* 加载映射文件
<mapping resource="cn/itcast/domain/Customer.hbm.xml"/>
【实战】
- 自动建表:一般配置成none(默认值)
- 重点理解:方言的作用
4`持久化类的编写规则
- 有无参的构造方法。
- hibernate通过反射来创建持久化类的实例对象。
- 属性私有化,对属性提供get/set方法。
- java的要求,属性私有化。hibernate,要封装数据,要求get/set方法。
- 属性尽量使用包装类。
- 不使用基本类型。因为,包装类和基本类型的初始化值不同。java.lang.Integer: null, int :0. –>在数据库中,null值和0值的不同。
- 持久化类要有一个唯一标识OID与表的主键对应。
- java区分是否同一个对象的方法:对象的地址是否相同。持久化类区分是否同一个对象的方法:oid值是否相同。
- 持久化类,不要使用final修饰符。
- final修饰符,修饰的类,是不能被继承的。load方法,提供延迟加载功能,延迟加载的机制:依赖于目标类(持久化类)通过继承生成的代理对象。使用final修饰后,没有办法生成代理对象,导致延迟加载失效。
5`hibernate持久化类的状态
持久化类的三种状态
hibernate为了更好的管理持久化类。给持久化类分为三种状态。
回顾什么是持久化类。
持久化类的三种状态是:
- 瞬时状态(Transient): 没有oid,没有关联session对象
- 持久状态(Persistent):有oid, 关联session对象
- 托管状态(Detached): 有oid, 没有关联session对象
持久化类中持久态的特性(重点理解)
- 自动更新数据库
【总结】
- session内部,有两个存储区:一个是一级缓存区,一个是快照区
- 持久化类在持久状态下,进行的修改,只会修改缓存区的数据。快照区的数据,一致保持和数据库的原始数据一致。
- 当事务提交的时候,对比一级缓存区和快照区,如果一致,说明,数据与数据库的原始数据一致,无需update。如果,不一致,说明需要进行update修改。
6`hibernate的主键策略
- 数据库主键的分类
- 自然主键:对象本身的属性,作为表的主键
- 代理主键:不使用对象本身的属性,而使用没有任何意义的一个字段,作为主键。(推荐)
- hibernate提供的主键生成策略
- increment:适合代理主键,适用于int,short,long(整型)。由hiberante提供,原理:先查询select max(id) from user ,+1 作为新记录的主键值。 缺陷:在多线程、集群环境中,不能使用。
- indentity:适合代理主键,适用于int,short,long(整型)。由底层数据库实现,要求支持自增长类型(auto_increment),例如:mysql。
- sequence:适合代理主键,适用于int,short,long(整型)。由底层数据库实现,要求支持序列,例如:oracle.
- native:适合代理主键,适用于int,short,long(整型)。特点:本地化,根据底层数据库支持的类型不同,采取不同的策略。如果,底层数据库支持自增长类型,native相当于indentity; 如果,底层数据库支持序列,natvie相当于sequence.实现跨数据源的特性(推荐)
- uuid:适合代理主键,支持字符串。(一般不使用)
- assigned:默认值,适合自然主键,主键的值,要求程序手动输入。
7`缓存
什么是缓存(面试题)
- 英文资料描述:缓存就是存贮数据(使用频繁的数据)的临时地方,因为取原始数据的代价太大了,所以我可以取得快一些。
- 个人理解:缓存理解成一种策略。利用高速存储区域的空间,提高对低速存储区域数据的读取速度。即空间换时间。
- 生活中的类似的场景:我们最经常使用的物品,放在最显眼的地方。把最常穿的衣服,挂载衣柜,不常穿的,放在箱子。
缓存的原理
- 利用高速存储区域,保存低速存储区域的数据,从而提供缓存功能。
- 考虑三层:cpu–内存–硬盘
hibernate一级缓存:定义和存在的依据
hibernate的缓存分为两个级别:一级缓存和二级缓存。
- 一级缓存: session级别的缓存,自带的不可卸载。一级缓存的生命周期与session一致。
- 二级缓存:SessionFactory级别的缓存,不是自带的需要进行配置才能使用的。二级缓存的生命周期与SessionFactory一致。不过,实际中,用redis代替二级缓存,故不使用。
hibernate缓存的原理
- 通过OID来区分持久化对象。OID相同,认为是同一个持久化对象。
- hibernate调用get/load进行查询时,先查询一级缓存,通过OID区分,如果该持久化对象存在,直接使用,不发送SQL语句。如果,不存在,就发送SQL语句,并且把查询到的持久化对象,保存到缓存中。
- hibernate调用save(),update()等操作,都一样。
8`hibernate的事务管理
hibernate设置的隔离级别
每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通过数据库配置设置,也可在应用程序中设置。例如Hibernate:在hibernate.cfg.xml中设置隔离级别:
1—Read uncommitted isolation:读未提交 2—Read committed isolation:读提交 4—Repeatable read isolation:重复读 8—Serializable isolation:序列化 <!-- 事务隔离级别 --> <property name="hibernate.connection.isolation">2</property>
hibernate的事务管理代码
在hibernate.cfg.xml中配置:
<!-- 配置session绑定本地线程 -->
<property name="hibernate.current_session_context_class">thread</property>
- hibernate的sessionFactory提供getCurrentSession()创建一个session和ThreadLocal绑定方法。
9`javaBean如何表达表与表的关系
java类如何表达:1对多
使用实际的场景分析:部门和员工,典型的:1对多关系。
java(使用面向对象的观点)如何表达:1对多
class Department{ Integer id; String name; Set<Employee> employees = new HashSet<Employee>(); } class Employee{ Integer id; String name; Department department; // 部门的对象
}
表达多对多
class Student{ Integer id; String name; Set<Course> courses = new HashSet<Course>(); } class Course{ Integer id; String name; Set<Student> students=new HashSet<Student>(); }
10`一对多配置映射文件
hibernate:1对多之建立关系(双向1对多)
数据库:增加外键约束.
alter table 表名 add column 字段名 类型;
alter table 从表名 add foreign key(外键字段) references 主表名(主键字段);
java类的表达
hbm.xml映射文件的表达(重点)
// 1的这方对应的持久化的类的配置: set标签:一的这方,保存多的这方,使用的是set集合。 name: 持久化类中属性的名字 key标签:column: 外键的字段名称 one-to-many标签:从一的一方出发,寻找多的一方的数据。class:找到的数据,需要封装成那个类的对象的全路径名称 <set name="employees"> <key column="e_did"></key> <one-to-many class="cn.itcast.domain.Employee"/> </set>
【记忆的方式
* 思考查询:现在从部门,查找这个部门所有的员工
* key:查找的关键,根据外键的值,去寻找。
* one-to-many: 表示方向,从一的一方找多的一方。class表达了找到的结果,需要封装的类型
// 多的一方:
name: 类中属性的名称
class:需要封装的类的全路径名称
column: 外键的名称
<many-to-one name="department" class="cn.itcast.domain.Department" column="e_did"></many-to-one>
【记忆的方式】
- 思考查询:已知员工信息,寻找员工对应的部门
- many-to-one:表达方向,从多的一方去寻找一的一方。class: 找到的数据,需要封装的类型。 column: 寻找的依据,根据什么数据来找
11`级联cascade
级联的配置:(1对多)
- 映射文件中,和都可以配置级联属性:cascade
级联的常见取值
none: 默认值。所有情况下均不进行关联操作 save-update: 在执行save/update/saveOrUpdate时进行关联操作。 delete: 在执行delete时进行关联操作 all: 所有情况下均进行关联操作。
12`放弃外键维护inverse
inverse:逆转, 默认值是:flase,不放弃, 设置成:true,放弃外键维护权
- 针对1对多的关系:应该由多的这方维护。
13`hibernate:cascade和inverse的区别
侧重点不同
- cascade:强调操作一个对象的时候,是否操作其关联对象
- inverse:强调的是外键的维护权
14`多对多映射文件的编写
hibernate:多对多之建立关系
数据库:新建表。
java类的表达
hbm.xml映射文件的表达
<!-- set标签:通过set集合,保存的学生所选的所有课程 name: 类中属性的名字 table: 中间表的名字(表与表关系建立的中间表) key标签: column: 当前类对应的表,在中间表中的外键字段的名字 many-to-many: class: 找到的课程数据,需要封装的类型。 column: 在中间表中,课程表对应的外键的字段名称 --> <set name="courses" table="student_course"> <key column="sc_sid"></key> <many-to-many class="cn.itcast.domain.Course" column="sc_cid" ></many-to-many> </set>
【记忆的方法】
- 思考的出发点:查询:已知学生的信息,查询这个学生所选的所有课程
- 到那里查:中间表,student_course表查询
- key: 查找的关键,我们现在知道的是学生的信息,我们需要通过学生的学号,在中间表中通过学生表对应的外键,进行查询
- many-to-many: 标识多对多查询,class查询到的结果,需要封装成那个对象。column:在中间表中,只能获得课程对应的外键,根据这个外籍,查询课程表的记录。
15`hiberante的查询方式:查询方式的概述(面试题)
- OID查询(get,load)
- HQL
- QBC
- 原生的SQL(SQLQuery查询)
- 对象导航查询
16`QBC查询
Criteria的使用
通过Session对象,获取Criteria对象。session.createCriteria(Class persistentClass)
通过Restrictions的静态方法创建Criterion对象。每个Criterion对象实例代表一个查询条件
常见的Restrictions的静态方法
方法 说明 Restrictions.eq = Restrictions.allEq 利用Map来进行多个等于的限制 Restrictions.gt >(greater than) Restrictions.ge >=(greater than or equal) Restrictions.lt < (less than) Restrictions.le <=(less than or equal) Restrictions.between BETWEENl Restrictions.like LIKE Restrictions.in in Restrictions.and and Restrictions.or or Restrictions.sqlRestriction 用SQL限定查询
- 向Criteria对象添加Criterion查询条件。add()方法
执行list()或者uniqueResult()获得结果。
Criteria实际上是一个查询容器,它对查询条件表达式的添加进行了封装。
- 查询条件Criterion对象,是通过add()方法添加的
查询条件Criterion对象是通过Restrictions的静态方法制定
- 高级功能
限定返回的条数(分页查询)
setFirstResult()和setMaxResult()结合使用
对查询结果排序
Order类,对结果排序,其中asc(升序),desc(降序)
Criteria criteria=session.createCriteria(Eemployee.class); criteria.add(Restrictions.ge("id",2)); //>=2 criteria.addOrder(Order.asc("id")); List list=criteria.list();
生成的SQL语句:
Select * from employee where eid>=2 order by id asc;
分组和统计
- hibernate3新增的功能,实现方式:
Projection(分组和统计的核心对象), Projections(工具类),ProjectList(添加多个核心对象)
- Projections.groupProperty(属性名):按照某属性名分组
- Projections的avg()/rowCount()/count()/max()/min()/countDistinct()等方法来实现统计功能
实例代码1: 按照部门分组
Criteria criteria=session.createCriteria(Eemployee.class); Projection projection = Projections.groupProperty("department"); criteria.setProjection(projection);
List list=criteria.list();
生成的SQL:
select * from employee group by e_did;
实例代码2: 查询编号最大的雇员
Criteria criteria=session.createCriteria(Eemployee.class); Projection projection = Projections.max("id"); criteria.setProjection(projection); List list=criteria.list(); 生成的SQL: select max(eid) from employee;
实例代码3:多条件的分组和统计:查询每个部门的人数
Criteria criteria = session.createCriteria(Employee.class);
// 查询条件的集合 ProjectionList prolist=Projections.projectionList(); prolist.add(Projections.groupProperty("department")); prolist.add(Projections.rowCount()); criteria.setProjection(prolist); List<Object[]> list = criteria.list(); 生成的SQL: select e_did, count(*) from employee group by e_did;
QBC的皇冠:DetechedCriteria(离线查询对象)(重要)
DetechedCriteria的定义
- 翻译成离线条件查询,可以脱离Session使用。因为Criteria对象,必须有Session对象创建,即先有Session对象,后有Criteria对象。而DetchedCriteria则无需Session即可创建。
使用示例
//1. 创建离线查询对象,并且封装好查询条件 :id =5 -- web层(servlet处理) DetachedCriteria dc = DetachedCriteria.forClass(Employee.class); dc.add(Restrictions.eq("id", 5)); //2. 获取session(dao层处理,只需要一个参数:离线查询对象) Session session =HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //3. 离线查询对象通过session,获得可以使用的Criteria Criteria criteria = dc.getExecutableCriteria(session); //4. criteria直接使用,查询条件在离线条件对象时,已经准备好了 List<Object[]> list = criteria.list();
修改成:普通Criteria
Session session =HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Criteria criteria =Criteria.forClass(Employee.class);
criteria.add(Restrictions.eq("id", 5));
List<Object[]> list = criteria.list();
- 意义
提示:从web开发的三层结构分析
- 实际开发中,查询的条件是不固定 年龄() 爱好()
- hql查询:非常难受:
- 什么条件都没有: select a from Employee a where 1=1 ;
- 用户输入年龄: select a from Employee a where 1=1 and age=? ;
实际开发中,使用QBC(Criteria)查询,离线查询对象。
在web层(servlet里面),创建一个离线查询对象,把所有的查询条件,全部封装到里面。
DetachedCriteria dc = DetachedCriteria.forClass(Employee.class);
if(age!=null)
{
dc.add(Restrictions.eq("age", ?));
}
if(爱好!=null)
{
dc.add(Restrictions.eq("爱好", ?));
}
dao层代码,传入的参数,只有一个:离线查询对象
17`HQL查询
Query
Query的介绍
- Query代表面向对象的一个hibernate查询。其接受一个HQL语句。
- HQL:hibernate query language,hibernate查询语言,语法类似SQL,但是是面向对象的。
- 又成为HQL查询
Query的使用
- 编写HQL语句。 类似与SQL语句
- Session对象通过HQL语句,创建Query对象。session.createQuery(String hql)
- 执行query的list()或者uniqueResult()方法获取结果
18`HQL的多表查询
- hibernate的查询中,QBC(Criteria)主要针对单表操作,所以,多表查询,我们只需要关注HQL查询。
整体分类
- 连接查询:
- 交叉连接:from Department, Employee
- 内连接:
- 隐式内连接:from Department x, Employee y where x.id = y.department
- 显式内连接:from Department x inner join x.employees
- 迫切内连接:from Department x inner join fetch x.employees
- 都是封装到部门对象,数据重复:select distinct x from Department x inner join fetch x.employees
- 还可以 new HashSet<>(list) 去list 重复
- 外连接:
- 左外连接:from Department x left join x.employees
- 迫切左外连接: from Department x left join fetch x.employees
- 右外连接:from Department x right join x.employees
迫切内联接和普通内联接的区别:
- 普通的内连接:把查询到的结果,封装到两个对象。
- 迫切内联接:把查询到的结果,封装到一个对象A(前面的那个对象), 另外一个对象B的数据,封装到A对象的关联对象的属性里面。
【总结】
- 显式内联接:from Department x inner join x.employees(无需写连接条件)
- 了解迫切内联接与普通内连接的区别
- 迫切内联接,查询的结果,需要去重。
19`OID查询
hibernate核心API:Session对象中get和load的异同(面试题)
相同点:
- 返回值,参数,发送的sql语句,都相同
不同点:
- get:立即加载策略:执行get方法,立即发送SQL语句,获得user对象,也是正常的。
- load:延迟加载策略:懒(lazy)加载:执行load方法,不会立即发送sql语句,只有在真正需要数据的时候,发送SQL语句。获得user对象,本质上一个user的代理对象。
- 如果找不到对象,get方法:NullPointerException;
- load方法,报错:ObjectNotFoundException
20`SQL查询
有的应用程序可能需要根据底层数据库的 SQL 方言,来
生成一些特殊的查询语句。在这种情况下,可以利用 Hibernate 提供的 SQL 检索方式。使用 SQL 检
索方式检索对象的示例代码,如下所示:
SQLQuery sqlQuery = session.createSQLQuery(“select id,name,age,city from
customer”);
21`hibernate的查询优化–抓取策略
获取关联对象的时机(开关)。
- 抓取策略是什么
简单而言:hibernate如何获取关联对象的方法。
是hibernate提升性能的一种方法。
不过,在使用抓取策略的时候,延迟加载也会影响查询性能,所以,抓取策略和延迟加载,是配合使用。
- 为什么学习抓取策略
项目开发中,查询无处不在。但是hibernate本身的查询效率不是很好(hibernate是对jdbc的封装,多一层封装,效率降低一些)。hibernate有关联机制(1对多,多对多),什么时候获取关联对象,如何获取关联对象(控制,什么时候获取),对查询性能的影响很大。所以,hibernate通过抓取策略,来对关联对象的查询,进行优化。
hibernate的延迟加载
什么是延迟加载
- 延迟加载的定义
- 延迟加载,也成为懒加载,只有真正需要数据的时候,才真正执行数据加载操作(sql查询语句)。
- 延迟加载的分类:
- 类级别的延迟加载
- 查询某个对象时,是否延迟,标签上配置lazy属性,默认值是:ture,默认延迟加载。
- 关联级别的延迟加载。
- 查询一个对象的关联对象时,是否延迟。在或上配置延迟。
- hibernate关联对象默认采用延迟加载。可以避免一些无谓的性能开销
类级别的延迟加载
- load方法,即类级别的延迟加载。让其失效的方法有两个:
- 持久化类上,增加final修饰符,让代理对象创建失败。
- 在上配置lazy=”false”,让延迟加载失效。默认值:true
【实战】
- 类级别延迟加载,我们一般不修改,采用默认值,因为,如果你想使用立即加载,调用get()方法即可。
关联对象的抓取策略和延迟加载:在set上的fetch和lazy
- 配置抓取策略,只配置在和上。
场景:查询某个部门,及该部门的员工人数
- set标签
- fetch的取值:抓取策略
- select.默认值,发送普通的select语句
- join:发送迫切左外联接去查询, 无所谓lazy的取值
- subselect。发送一条子查询语句查询其关联对象.(查询多个对象,才能体现)
- lazy的取值:
- true。默认值,采用延迟加载
- false.不采用延迟加载
- extra. 极其懒惰的
- fetch的取值:抓取策略
fetch: 抓取策略。确定查询关联对象,使用那种方式(SQL语句)
lazy: 延迟加载。确定什么时候发送SQL语句,查询关联对象
- 总结:
- fetch=”join”,发送迫切左外联接,把当前对象及其关联对象,一条语句,全部获取到。无需再次发送SQL语句,进行关联对象查询,也就无所谓延迟加载的设定(lazy失效)
- fetch=’subselect’,需要查询多个对象,才能体现。lazy是extra的时候,子查询失效。
关联对象的抓取策略和延迟加载:在上的fetch和lazy
注意:1的一方的配置先取消
- 标签
- fetch的取值:
- select.默认值,发送普通的select语句
- join:发送迫切左外联接去查询
- lazy的取值:
- proxy。默认值,是否延迟取决于一的一方类上lazy属性
- false.不采用延迟加载
- no-proxy:不用研究
- fetch的取值:
关联对象的抓取策略:批量抓取
- 场景1:查询所有部门,查询每个部门的所有员工的名字
从一的一方开始查询,在set上配置:batch-size=”3”
- 场景2:查询所有员工,并且查询其对应部门的名称
从多的一方开始查询,batch-size必须配置在一的一方的标签上
今日内容总结
- 核心任务:掌握离线查询对象的使用,以及在项目开发中的作用
- 抓取测试,只需要了解即可。
- hal多表查询,只需要了解。
hibernate的总结
- helloword入门案例
- 掌握3种查询的使用:QBC, HQL, SQLQuery查询
- 掌握hibernate事务的处理(service层的价值)
- SessionFactory工具类,抽取的方法(思路)
- 其他,面试的需求,多于工作。(持久类编写规则,三种状态,抓取策略)
hibernaete如何优化(面试题)
a. 使用缓存,hibernate的二级缓存(SessionFactory级别缓存)不适用于集群环境,我们使用redis代替。
b. 配置抓取策略。分析项目需求,对于那些必须使用关联对象的操作,使用立即加载关联对象。
c. 针对不同的项目环境,灵活的处理关联机制()。有一种情况:代码上没有外键,心中有外键。
- CRM项目:这个软件的使用人数有限(固定),并发特别少
- 注重数据的安全性,
- 移动互联网项目:使用的人数特别多,并发量特别大
-
- 注重效率。在这种情况下,考虑:放弃外键约束,放弃配置hibernate表与表的关联关系,放弃级联,关联查询这些机制。我在业务的代码中,保证外键的有效性。
-