Hibernate课堂笔记
1 Hibernate是什么
Hibernate是一个数据库的操作框架
1.1 Jdbc的缺点
l jdbc的效率比较低
每一次请求,打开连接、发送sql语句,在数据库中解析sql语句等需要消耗大量的资源。
重复性代码特别多
每一次都需要关闭connection,statement,rs
Jdbc没有做到面向对象编程
没有做到数据缓存
现在世界上数据缓存做的很不错的框架(hibernate,oscache,ehcache)
Oscache和ehcache都有页面缓存
* 就是把页面上的数据缓存到本地磁盘上
* 当应用程序再次访问的时候,直接从磁盘上读取数据就
可以了
* 页面缓存不适合做更新特别频繁的数据
1.2 Jdbc的优点
是最底层的操作数据库的框架,所以在java的层面上说速度最快
Jdbc做到了connection pool
1.3 Hibernate优点
l 面向对象编程
l Hibernate的底层也是由jdbc实现的
l Hibernate的数据缓存做的相当棒
l Hibernate做到了connection pool
1.4 Hibernate缺点
Hibernate不会让程序员干涉sql语句的生成,sql语句是hibernate内部操作jdbc生成的,所以在一个项目中,如果对sql的优化要求比较高,这个时候,不适合用hibernate
如果一张表的数据量特别大(千万级别以上),也不适合用hibernate
2 Hibernate的学习内容
l 学习hibernate的crud操作
l 持久化对象的状态
l 一对多和多对多(重点)
l 数据缓存(一级缓存、二级缓存、查询缓存)(重点) 数据缓存的特点
l Hql语句
3 hibernate的crud操作
3.1 类与表的映射关系
3.2 Hibernate配置文件的加载方式
3.2.1 方式一
注意:
1、 配置文件的名称必须是hibernate.cfg.xml
2、 把hibernate.cfg.xml文件必须放在classpath下
3.2.2 方式二
注意:
1、 利用该方法配置文件的名称可以随意写
2、 路径可以随意放
3、 Resource代表路径名称+文件名称
注意:hibernate的事务默认不是自动提交的
3.3 crud操作注意事项
3.3.1 session.get方法
1、 要使用session.get方法,那么相对应的持久化类中必须保证有一个无参的构造函数
2、 该方法的第二个参数与持久化类中的主键的类型要保持一致。
3、 Session.get方法的第二个参数的形参:Serializable.因为只有这样才能接受所有的数据库的主键的类型
3.3.2 session.update
参数可以是session.get方法得到的对象,也可以是新创建的对象,但是cid的值必须和数据库的值相对应。
4 Hibernate流程
5 sessionFactory
1、 sessionFactory中存放配置文件和映射文件的所有的信息
2、 一个数据库有且仅有一个sessionFactory
3、 sessionFactory是一个单例的对象
4、 sessionFactory是一个重量级别的类,只加载一次
5、 sessionFactory是一个线程安全类
6 主键的生成机制
考虑hibernate主键的生成机制可以从三方面去想:
客户端
Hibernate内部可以生成主键
数据库内部的机制生成主键
6.1 Incrment
1、 获取主键的最大值,然后加1
2、 主键的类型必须是数字类型
3、 第一步的过程是由hibernate内部做的
4、 发出select max(cid) from Classes 这样的sql语句,所以效率比较低
5、 主键的值是连续的
6.2 Identity
1、 适用于数字类型
2、 数据库表本身的自动增长
3、 主键是在数据库内部生成的
4、 表必须得设置为自动增长
6.3 Assigned
由程序员自己设置生成
6.4 Uuid
由hibernate内部生成一个唯一的字符串
7 Hibernate类型
Hibernate内部实现了从java类型到数据库类型的映射,所以在映射文件中选择java类型比选择hibenate类型效率要高
8 对象的状态
对象的状态的定义
临时状态的对象:刚创建的对象
持久化状态的对象:该对象和session已经发生交互了
脱管状态的对象:由和session的交互变为和session的脱离
8.1 案例1
说明:
1、 第39行代码发出了update语句
2、 在执行第36行代码的时候,利用session.get方法提取出来一个对象,该对象是持久化状态的对象
3、 处于持久化状态的对象不用执行session.update方法
4、 session.save和session.update方法都是把一个对象的状态变成持久化状态
8.2 案例2
说明:
1、 在执行40行代码的时候不会发出update语句,因为修改后的数据和原来的数据是一样的。
2、 在hibernate内部,会生成该对象的一个镜像文件,在执行update方法的时候,
会查看镜像文件,如果一致,则不执行update,如果不一样才要执行update语句
8.3 案例3
说明:
1、 第51行代码把classes对象由临时状态转变成持久化状态
2、 在第53行代码之前不用执行session.update方法
8.4 案例4
说明:
1、59行的session和67行的session是两个session
2、61行产生的classes与67行产生的session直到程序结束没有发生关联
3、一个对象是否是持久化对象是针对一个session而言的
8.5 案例5
分析:
1、 当执行82行代码的时候,把classes从持久化状态的对象转化成脱管状态,这个时候和session没有关联了
2、 在执行84行代码的时候没有执行update语句
3、 要想执行update语句,必须使classes成为持久化状态的对象,所以执行update方法
8.6 对象状态转化图
说明:
只有持久化状态的对象才能和session发生交互
9 Hibernate对象关系
研究:
1、 crud的操作
2、 外键(面向对象:关系)
9.1 一对多单项
9.1.1 Cascade
1、 cascade只能出现在多表中
2、 针对一对多的情况
在保存或者更新classes的时候,针对student应该做什么样的操作
9.1.1.1 案例1
分析:因为classes与student都是临时状态,所以应该是在保存classes的同时保存student
9.1.1.2 案例2
分析:
1、 因为在客户端和在映射文件中都有设置级联,所以cascade是起作用的
2、 在上述代码中,因为classes的一般属性并没有发生改变,所以针对classes不会发出update语句
3、 因为classes的集合发生改变了,而且级联起作用,所以hibernate内部会去检查classes中的集合的状态,因为集合中多了一个数据,所以增加了一个student
4、 级联操作是对象与对象的操作
9.1.1.3 案例3
当提交的时候,因为是级联操作,因为对student的一般属性发生了改变,所以在更新classes的时候更新了student
9.1.1.4 总结
1、 级联是对象与对象之间的操作,和关系无关
2、 对一个对象进行操作的同时,怎么样操作级联对象,看级联对象是否是持久化对象(看级联对象对应数据库是否有数据),如果是持久化对象,则执行update操作,如果是临时状态的对象,则执行insert操作
9.1.2 Inverse
对象对关系是否维护
上述的图的意思为Classes是否要维护student表的关系(外键),如果inverse为false,则维护,如果为true,则不维护
9.1.2.1 案例1
分析:
1、 该例子从数据库的角度分析,把sid为2的值的外键修改一下就可以了
2、 从hibernate的角度是修改关系
3、 查看classes.hbm.xml映射文件中,classes能否修改classes与student之间的关系。
4、 因为关系的修改就是一个update操作而已,所以没有必要解除关系。
5、 第136行代码是从classes端维护关系。如果从student端维护关系,那
代码应该是student.setClasses();
9.1.2.2 案例2
分析:
1、 根据映射文件可以得出classes维护关系
2、 在对classes进行删除、增加、修改的时候同时要操作student
3、 因为classes负责维护关系,所以在删除掉classes之前,hibernate内部会自动执行解除所有的关系的操作
9.1.2.3 案例3
分析:
1、 解除班级和所有学生之间的关系实际上就是把classes中的集合清空。
2、 Classes.setStudents(null)这个时候hibernate内部会直接把关系设置为null,效率最高,根本不需要去查student
9.1.3 总结
1、 因为是一对多的单项,所以只能是classes维护关系
2、 Classes维护关系,所以客户端的代码应该是通过classes对关系进行建立或者解除
3、 只要涉及到关系的操作就得发出update语句,用来更新关系。所以用classes维护关系效率并不高
4、 一对多,用一的一方维护关系,效率比较低。当数量级别比较大的时候,会发出n多update语句
9.2 一对多的双向
9.2.1 案例1
分析:
1、 通过映射文件可以看出,在Student.hbm.xml文件中,不存在外键,意味着如果通过student建立与classes的联系,在hibernate内部是做不到。
2、 Student.getClasses会报空指针异常,得不到,因为没有联系
3、 通过classes操作student,看classes.hbm.xm文件,通过student操作classes,
看student.hbm.xml文件
4、 所以在classes.hbm.xml文件和student.hbm.xml文件中都必须体现外键
9.2.2 案例2
分析:
1、 通过代码34行可以得出是通过student联系classes,所以看student.hbm.xml
2、 35行代码是保存学生,因为在34行代码中通过student建立了与classes之间的关系,所以在插入学生的同时,在hibernate内部自动维护了关系
3、 用学生端维护关系,不需要发出update语句
4、 一对多,多的一方维护效率比较高
9.3 多对多
9.3.1 学习重点
1、 映射文件的创建和持久化类的创建
2、 关系
说明:
1、 关系的操作
建立关系 就是在第三张表中插入一行数据
解除关系 在第三张表中删除相应的一行数据
改变关系
1、 修改相应的行的数据
2、 先删除再增加
3、 多对多谁维护关系效率都一样,都得对第三张表发出相应的SQL语句
9.3.2 练习
9.3.2.1 案例1
分析:
1、 因为要级联,因为是通过course级联,所以在course.hbm.xml文件中,针对set集合,应该设置cascade属性
2、 因为多对多谁维护关系效率都一样,所以在两个映射文件中都维护关系
3、 第53行代码,即级联又维护关系
9.3.2.2 案例2
分析:
1、 经过需求分析可以得出应该是修改学生和课程之间的关系
2、 因为在学生类中存在的是课程的集合,而课程类中存在的也是学生的集合
3、 集合不存在修改,所以修改关系就等于先解除后增加
4、 通过哪个端修改关系都可以,但是要统一
10 性能
10.1 懒加载(延迟加载)
概念:需要时加载
10.1.1 类的懒加载
分析:
1、 利用session.load方法就是类的延迟加载
2、 利用session.load方法产生的是代理对象,利用session.load方法的出来的对象只有id值(标识符属性)
3、 当得到具体的属性的时候才要发出sql语句
4、 类的延迟加载只针对一般属性有效
5、 必须在session关闭之前把数据提取出来
10.1.2 集合的懒加载
分析:
1、 集合的懒加载,只有在根据一的一方加载多的一方的情况下才要讨论这种情况
2、 如果采用默认的加载方式,就是集合的懒加载
3、 在遍历集合的时候才要发出sql语句
4、 如果在set元素中lazy为false
1、 在执行31行代码的时候,发出两条sql语句
第一条查询的是classes
第二条查询的是根据cid查询student
5、 集合的懒加载还有一个值为extra,更进一步的懒加载
分析:因为不需要加载student的属性,只需要一个函数的结果,所以就发出了上面的sql语句
10.1.3 单端关联的懒加载
因为单端关联只加载一个数据,所以用默认情况做就可以了
False/proxy(true)/no-proxy
10.2 抓取策略
根据一方怎么样提取关联端
根据一的一方怎么样提取多的一方
根据多的一方怎么样提取一的一方
10.2.1 方式
前提条件:针对集合
懒加载 |
抓取策略 |
Sql语句 |
True/false/extra |
Join(子查询除外) |
发出一条,把所有的数据全部加载了 |
True/false/extra |
select |
在遍历集合的时候才要发出sql语句,这个时候发出n+1条sql语句/在加载classes的时候就把student的数据加载出来了/extra在这里等同于true |
True/false/extra |
subselect |
如果检测到由子查询,则用subselect |
说明:
1、如果第一页面只有一的一方的数据,然后根据超级连接才要显示多的一方的数据,这种情况用select或者subselect
2、如果页面上既有一的一方的数据也有多的一方的数据,用join是比较好的选择。
1、select 默认的
1、先查询班级(cid:1,2,3)
2、根据班级中的每一个cid去查相应的学生
2、subselect 子查询
根据需求分析先写出sql句,只要sql语句中含有子查询,那么肯定是子查询的效率比较高
3、join 连接
1、如果根据需求分析,得出的sql语句含有子查询,这个时候用join(左外连接)
不起作用
3、 如果没有子查询,则利用join的方式(左外连接),就可以一次性的把一和多的数据全部查询出来
10.3 懒加载与抓取策略的结合
10.4 Session
10.4.1 Session的产生方式
10.4.1.1 openSession
分析:
1、 只要代码中遇到sessionFactory.openSession,那么在hibernate内部就会新创建一个session
2、 只要openSession一次,hibernate内部就会连接数据库一次,所以在代码中opesnSession的次数,越多效率越低
3、 只要执行sessionFactory.openSession一次就会产生一次连接
4、 Hibernate中的事务是依赖session的
5、 最好一个方法只有一个session
10.4.1.2 currentSession
分析:
1、 在特别复杂的项目中,一个service方法可以操作很多内容,而这些内容有可能涉及到很多框架,而框架对于hibernate的操作对于程序员来说是不可见的
2、 但是要想使上面的service方法运行成功,必须在一个事务环境下运行
3、 要想在一个事务环境下运行,必须只有一个session
4、 把当前线程绑定到session是一个特别好的解决方案
注意:
只要用到当前线程产生的session,crud操作必须在事务的环境下运行
当前线程的session还和事务绑定,这样可以提高安全性能
当事务提交的时候,当前线程中的session关闭了
用当前线程产生的session,不需要手动关闭session。
配置方式:
在hibernate.cfg.xml文件中
在代码中利用sessionFactory.getCurrentSession就可以实现了
10.4.2 Session的生命周期
10.4.3 Session中的缓存
10.4.4 安全
说明:
1、 不同的客户端访问有不同的线程,不同的线程就有不同的session,特定的数据在特定的session中
2、 一个数据只能让一个客户端拥有
3、 把session绑定到当前线程中解决了session中数据线程安全的问题
11 Hibernate错误分析
11.1 错误一
分析:
l 在配置文件中加载该文件,但是路径写错了,找不到
l 该文件根本没有加载到配置文件中
11.2 错误二
分析:
当生成sql语句的时候,利用get方法获取值,这个时候,需要映射文件中的name属性拼接成get方法,然后利用java的反射机制赋值,拼接的时候出错了,所以属性找不到。
11.3 错误三
分析:说明持久化没有找到,持久化类没有找到并不一定意味着该类不存在,有可能映射文件路径写错了
以上两个错误结合一起,就是总结出:
在映射文件中,class元素的name属性的值写错了
11.4 错误四
在hibernate的配置文件中,映射文件的路径写错了
11.5 错误五
分析:
1、 有可能在映射文件中,class元素的name属性的值写错了
2、 在客户端session.save进行操作的时候,因为操作的是持久化对象,所以该对象所在的类必须是映射文件中class元素的name属性,如果不一致,也会报这样的错误
11.6 错误六
分析:
Session.get方法两个参数,第二个参数的类型必须与持久化类中的主键的类型保持一致。
11.7 错误七
分析:
表本身默认的值,没有支持自动增长
11.8 错误八
客户端截图:
分析:根据64行代码可以判断出classes与students建立关联
映射文件:Classes.hbm.xml
总体分析:
1、 把session.save(classes)这样的保存称为显示保存
2、 在保存classes的同时保存student,保存student的操作是由hibernate内部完成的,我们把这样的操作称为隐式保存
3、 在classes.hbm.xml文件set元素中并没有配置cascade
4、 只有在客户端建立classes与student之间的关系,然后在映射文件中再次确认关联(cascade)两个地方都配置了,级联保存才能有效
5、 因为在classes.hbm.xml文件中,没有针对student的cascade,所以student对象处于临时状态,hibernate是不允许这种情况出现的
6、 这个错误只有在多表的情况下才有可能产生
11.9 错误九
当session关闭以后还要视图从数据库中提取数据就会报这样一个错误。
解决方案:
在session关闭之前把数据全部提取出来