Hibernate课堂笔记
Hibernate是一个数据库的操作框架
l jdbc的效率比较低
每一次请求,打开连接、发送sql语句,在数据库中解析sql语句等需要消耗大量的资源。
重复性代码特别多
每一次都需要关闭connection,statement,rs
Jdbc没有做到面向对象编程
没有做到数据缓存
现在世界上数据缓存做的很不错的框架(hibernate,oscache,ehcache)
Oscache和ehcache都有页面缓存
* 就是把页面上的数据缓存到本地磁盘上
* 当应用程序再次访问的时候,直接从磁盘上读取数据就
可以了
* 页面缓存不适合做更新特别频繁的数据
是最底层的操作数据库的框架,所以在java的层面上说速度最快
Jdbc做到了connection pool
l 面向对象编程
l Hibernate的底层也是由jdbc实现的
l Hibernate的数据缓存做的相当棒
l Hibernate做到了connection pool
Hibernate不会让程序员干涉sql语句的生成,sql语句是hibernate内部操作jdbc生成的,所以在一个项目中,如果对sql的优化要求比较高,这个时候,不适合用hibernate
如果一张表的数据量特别大(千万级别以上),也不适合用hibernate
l 学习hibernate的crud操作
l 持久化对象的状态
l 一对多和多对多(重点)
l 数据缓存(一级缓存、二级缓存、查询缓存)(重点) 数据缓存的特点
l Hql语句
注意:
1、 配置文件的名称必须是hibernate.cfg.xml
2、 把hibernate.cfg.xml文件必须放在classpath下
注意:
1、 利用该方法配置文件的名称可以随意写
2、 路径可以随意放
3、 Resource代表路径名称+文件名称
注意:hibernate的事务默认不是自动提交的
1、 要使用session.get方法,那么相对应的持久化类中必须保证有一个无参的构造函数
2、 该方法的第二个参数与持久化类中的主键的类型要保持一致。
3、 Session.get方法的第二个参数的形参:Serializable.因为只有这样才能接受所有的数据库的主键的类型
参数可以是session.get方法得到的对象,也可以是新创建的对象,但是cid的值必须和数据库的值相对应。
1、 sessionFactory中存放配置文件和映射文件的所有的信息
2、 一个数据库有且仅有一个sessionFactory
3、 sessionFactory是一个单例的对象
4、 sessionFactory是一个重量级别的类,只加载一次
5、 sessionFactory是一个线程安全类
考虑hibernate主键的生成机制可以从三方面去想:
客户端
Hibernate内部可以生成主键
数据库内部的机制生成主键
1、 获取主键的最大值,然后加1
2、 主键的类型必须是数字类型
3、 第一步的过程是由hibernate内部做的
4、 发出select max(cid) from Classes 这样的sql语句,所以效率比较低
5、 主键的值是连续的
1、 适用于数字类型
2、 数据库表本身的自动增长
3、 主键是在数据库内部生成的
4、 表必须得设置为自动增长
由程序员自己设置生成
由hibernate内部生成一个唯一的字符串
Hibernate内部实现了从java类型到数据库类型的映射,所以在映射文件中选择java类型比选择hibenate类型效率要高
对象的状态的定义
临时状态的对象:刚创建的对象
持久化状态的对象:该对象和session已经发生交互了
脱管状态的对象:由和session的交互变为和session的脱离
说明:
1、 第39行代码发出了update语句
2、 在执行第36行代码的时候,利用session.get方法提取出来一个对象,该对象是持久化状态的对象
3、 处于持久化状态的对象不用执行session.update方法
4、 session.save和session.update方法都是把一个对象的状态变成持久化状态
说明:
1、 在执行40行代码的时候不会发出update语句,因为修改后的数据和原来的数据是一样的。
2、 在hibernate内部,会生成该对象的一个镜像文件,在执行update方法的时候,
会查看镜像文件,如果一致,则不执行update,如果不一样才要执行update语句
说明:
1、 第51行代码把classes对象由临时状态转变成持久化状态
2、 在第53行代码之前不用执行session.update方法
说明:
1、59行的session和67行的session是两个session
2、61行产生的classes与67行产生的session直到程序结束没有发生关联
3、一个对象是否是持久化对象是针对一个session而言的
分析:
1、 当执行82行代码的时候,把classes从持久化状态的对象转化成脱管状态,这个时候和session没有关联了
2、 在执行84行代码的时候没有执行update语句
3、 要想执行update语句,必须使classes成为持久化状态的对象,所以执行update方法
说明:
只有持久化状态的对象才能和session发生交互
研究:
1、 crud的操作
2、 外键(面向对象:关系)
1、 cascade只能出现在多表中
2、 针对一对多的情况
在保存或者更新classes的时候,针对student应该做什么样的操作
分析:因为classes与student都是临时状态,所以应该是在保存classes的同时保存student
分析:
1、 因为在客户端和在映射文件中都有设置级联,所以cascade是起作用的
2、 在上述代码中,因为classes的一般属性并没有发生改变,所以针对classes不会发出update语句
3、 因为classes的集合发生改变了,而且级联起作用,所以hibernate内部会去检查classes中的集合的状态,因为集合中多了一个数据,所以增加了一个student
4、 级联操作是对象与对象的操作
当提交的时候,因为是级联操作,因为对student的一般属性发生了改变,所以在更新classes的时候更新了student
1、 级联是对象与对象之间的操作,和关系无关
2、 对一个对象进行操作的同时,怎么样操作级联对象,看级联对象是否是持久化对象(看级联对象对应数据库是否有数据),如果是持久化对象,则执行update操作,如果是临时状态的对象,则执行insert操作
对象对关系是否维护
上述的图的意思为Classes是否要维护student表的关系(外键),如果inverse为false,则维护,如果为true,则不维护
分析:
1、 该例子从数据库的角度分析,把sid为2的值的外键修改一下就可以了
2、 从hibernate的角度是修改关系
3、 查看classes.hbm.xml映射文件中,classes能否修改classes与student之间的关系。
4、 因为关系的修改就是一个update操作而已,所以没有必要解除关系。
5、 第136行代码是从classes端维护关系。如果从student端维护关系,那
代码应该是student.setClasses();
分析:
1、 根据映射文件可以得出classes维护关系
2、 在对classes进行删除、增加、修改的时候同时要操作student
3、 因为classes负责维护关系,所以在删除掉classes之前,hibernate内部会自动执行解除所有的关系的操作
分析:
1、 解除班级和所有学生之间的关系实际上就是把classes中的集合清空。
2、 Classes.setStudents(null)这个时候hibernate内部会直接把关系设置为null,效率最高,根本不需要去查student
1、 因为是一对多的单项,所以只能是classes维护关系
2、 Classes维护关系,所以客户端的代码应该是通过classes对关系进行建立或者解除
3、 只要涉及到关系的操作就得发出update语句,用来更新关系。所以用classes维护关系效率并不高
4、 一对多,用一的一方维护关系,效率比较低。当数量级别比较大的时候,会发出n多update语句
分析:
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文件中都必须体现外键
分析:
1、 通过代码34行可以得出是通过student联系classes,所以看student.hbm.xml
2、 35行代码是保存学生,因为在34行代码中通过student建立了与classes之间的关系,所以在插入学生的同时,在hibernate内部自动维护了关系
3、 用学生端维护关系,不需要发出update语句
4、 一对多,多的一方维护效率比较高
1、 映射文件的创建和持久化类的创建
2、 关系
说明:
1、 关系的操作
建立关系 就是在第三张表中插入一行数据
解除关系 在第三张表中删除相应的一行数据
改变关系
1、 修改相应的行的数据
2、 先删除再增加
3、 多对多谁维护关系效率都一样,都得对第三张表发出相应的SQL语句
分析:
1、 因为要级联,因为是通过course级联,所以在course.hbm.xml文件中,针对set集合,应该设置cascade属性
2、 因为多对多谁维护关系效率都一样,所以在两个映射文件中都维护关系
3、 第53行代码,即级联又维护关系
分析:
1、 经过需求分析可以得出应该是修改学生和课程之间的关系
2、 因为在学生类中存在的是课程的集合,而课程类中存在的也是学生的集合
3、 集合不存在修改,所以修改关系就等于先解除后增加
4、 通过哪个端修改关系都可以,但是要统一
概念:需要时加载
分析:
1、 利用session.load方法就是类的延迟加载
2、 利用session.load方法产生的是代理对象,利用session.load方法的出来的对象只有id值(标识符属性)
3、 当得到具体的属性的时候才要发出sql语句
4、 类的延迟加载只针对一般属性有效
5、 必须在session关闭之前把数据提取出来
分析:
1、 集合的懒加载,只有在根据一的一方加载多的一方的情况下才要讨论这种情况
2、 如果采用默认的加载方式,就是集合的懒加载
3、 在遍历集合的时候才要发出sql语句
4、 如果在set元素中lazy为false
1、 在执行31行代码的时候,发出两条sql语句
第一条查询的是classes
第二条查询的是根据cid查询student
5、 集合的懒加载还有一个值为extra,更进一步的懒加载
分析:因为不需要加载student的属性,只需要一个函数的结果,所以就发出了上面的sql语句
因为单端关联只加载一个数据,所以用默认情况做就可以了
False/proxy(true)/no-proxy
根据一方怎么样提取关联端
根据一的一方怎么样提取多的一方
根据多的一方怎么样提取一的一方
前提条件:针对集合
懒加载 |
抓取策略 |
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的方式(左外连接),就可以一次性的把一和多的数据全部查询出来
分析:
1、 只要代码中遇到sessionFactory.openSession,那么在hibernate内部就会新创建一个session
2、 只要openSession一次,hibernate内部就会连接数据库一次,所以在代码中opesnSession的次数,越多效率越低
3、 只要执行sessionFactory.openSession一次就会产生一次连接
4、 Hibernate中的事务是依赖session的
5、 最好一个方法只有一个session
分析:
1、 在特别复杂的项目中,一个service方法可以操作很多内容,而这些内容有可能涉及到很多框架,而框架对于hibernate的操作对于程序员来说是不可见的
2、 但是要想使上面的service方法运行成功,必须在一个事务环境下运行
3、 要想在一个事务环境下运行,必须只有一个session
4、 把当前线程绑定到session是一个特别好的解决方案
注意:
只要用到当前线程产生的session,crud操作必须在事务的环境下运行
当前线程的session还和事务绑定,这样可以提高安全性能
当事务提交的时候,当前线程中的session关闭了
用当前线程产生的session,不需要手动关闭session。
配置方式:
在hibernate.cfg.xml文件中
在代码中利用sessionFactory.getCurrentSession就可以实现了
说明:
1、 不同的客户端访问有不同的线程,不同的线程就有不同的session,特定的数据在特定的session中
2、 一个数据只能让一个客户端拥有
3、 把session绑定到当前线程中解决了session中数据线程安全的问题
分析:
l 在配置文件中加载该文件,但是路径写错了,找不到
l 该文件根本没有加载到配置文件中
分析:
当生成sql语句的时候,利用get方法获取值,这个时候,需要映射文件中的name属性拼接成get方法,然后利用java的反射机制赋值,拼接的时候出错了,所以属性找不到。
分析:说明持久化没有找到,持久化类没有找到并不一定意味着该类不存在,有可能映射文件路径写错了
以上两个错误结合一起,就是总结出:
在映射文件中,class元素的name属性的值写错了
在hibernate的配置文件中,映射文件的路径写错了
分析:
1、 有可能在映射文件中,class元素的name属性的值写错了
2、 在客户端session.save进行操作的时候,因为操作的是持久化对象,所以该对象所在的类必须是映射文件中class元素的name属性,如果不一致,也会报这样的错误
分析:
Session.get方法两个参数,第二个参数的类型必须与持久化类中的主键的类型保持一致。
分析:
表本身默认的值,没有支持自动增长
客户端截图:
分析:根据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、 这个错误只有在多表的情况下才有可能产生
当session关闭以后还要视图从数据库中提取数据就会报这样一个错误。
解决方案:
在session关闭之前把数据全部提取出来