hibernate深蓝课堂笔录_1101_精简版

来源:互联网 发布:淘宝联盟订单查询 编辑:程序博客网 时间:2024/04/30 17:41
1 orm  对象关系映射  以及各个orm框架的区别(hibernTE IBATIS EJB JDO TOPLINK)
2 hibernate 的原理及其步骤
   2.1 读取配置文件 默认hibernate.cfg.xml 放在类路径 根下  Configuation  可以改变默认的配置文件 
   2.2 用Configuation 实例创建Sessionfactory  Sessionfactory 是线程安全的
   2.3 用SessionFactory创建session session 是线程不安全的
          SessionFactory提供了两种方式得到sedesion
   1 openSession  得到的session是线程不安全的 可以通过本地线程 Threadlocal机制解决 就是吧session放入Threadlocal中 达到隔离线程的目的
     得到的session需要手动关闭
   2 getCurrentSession可以解决  session 线程不安全问题 因为它总是把session绑定到当前线程,并且在事务提交的时候自动关闭。但需要配置
       <property name="hibernate.current_session_context_class">thread</property>《!--针对jdbc的--》
   2.4 利用session开启事务
   2.5  利用session进行Crud操作
   2.5 提交事务
   2.6 关闭session以及sessionFactory
3 Hibernate的核心类和接口:
   <1>Configuration
负责管理Hibernate的配置信息,这些配置信息都是从配置文件hibernate.cfg.xml或者
Hibernate.properties读取的,当然也可以自定义文件名称,只要在实例化Configuration
的时候指定具体的路径就可以了
   <2>SessionFactory
Configuration的实例会根据当前的配置信息,构造SessionFactory实例。
SessionFactory是线程安全的,一般情况下一个应用中一个数据库共享一个SessionFactory实例。
   <3>Session
一般的持久化方法(CRUD)都是通过Session来调用的,Session是非线程安全的
   <4>Transaction
事务管理
   <5>Query
查询的接口
4 hibernate po实体的3种状态
  po:persistent object   持久化对象 
  pojo: piain old java object  原始java对象
    1 瞬时状态   数据库中没有数据与之对应,一般是new出来且与session没有关联的对象,超过作用域会被JVM垃圾回收器回收,
    2 持久化状态  数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;
                  持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)
    3 离线状态(脱管状态)曾经被持久化过,数据库中有数据与之对应,但当前没有session与之关联;脱管对象状态发生改变,hibernate不能检测到
5 hibernate的查询方式
  5.1 hql hibernate query language
      支持占位符和命名参数的查询
  5.2 qbc(query by Criteria )查询
  5.3 qbe(query by Example) 样板查询  根据你的样板实例属性与数据库中的字段值进行比对
  5.4 sql查询   默认查询出来的list不是persobn对象,而是Object数组,可以通过
      SQLQuerry.addEntity(Person.class)


    
      三种查询方式优缺点
HQL功能最强大,适合各种情况,但是动态条件查询构造起来很不方便 
Criteria最适合动态条件查询,不太适合统计查询,QBE还不够强大,只适合相当简单的查询 
NativeSQL可以实现特定数据库的SQL,但是可移植性就牺牲了 


  总结:不管是SQL还是HQL,当你查询出来的不是整个对象,而是其中的某些字段,或者说:当几个表关联查询的时候,取二个表中的某些字段,      
   返回的不是一个封装好的对象的List集合,那么这个List里到底是什么东西呢,这里分为两种情况
   <1>如果只有一个字段,list里每个元素都是Object
   <2>如果多于一个字段,list里每个元素都是Object[], 所以,这时候就需要自己去封装对象了,将object数组中的元素迭代出来,
      然后封装到一个对象里。
6 hibernate主键生成机制
    assigned  主键由外部程序负责生成,在 save() 之前指定一个。
    hilo 会多生成一个表 hibernate_unique_key  支持自增长
    increment (两条sql) 由hibernate管理主键,自动以递增的方式生成标识符,每次增量为1。其在每次插入前取得一个当前最大的id+1作为主键,该主键必须为Integer类型
    identity:由底层数据库生成标识符。identity是由数据库自己生成的,但这个主键必须设置为自增长,前提条件是低层数据库支持自动增长字段类型
             有可能增长的幅度不一定为1
    native   由Hibernate根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式。
    uuid.hex 由 Hibernate 基于128 位 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
    foreign: 使用另外一个相关联的对象的标识符作为主键。 
    sequence 采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。
6 映射
    1 基本类型的映射
    2 集合映射    加一个表 主键关联起来
        1 list映射 有序可重复
2 set  无序不可重复
3 bag 无序可重复
4 map映射 


    3  关联映射 
      3.1 一对一  
         1.1 外键关联 把外键对应的字段加上唯一约束
1.2 主键关联  我的主键就是你的主键  采用的主键生产机制是foreign
      3.2 一对多  *******级联与反转********
         我们偏爱双向一对多  在一方使用级联与反转    注意这里的一方
  如果不用反转 hibernate 做法是一个一个的对集合里面的数据进行插入,关联字段为空,之后发一条一条的uodate语句去更新关联字段
          如果我们用反转 表明将我们的关联关系维护权交给另一方(多方)管理,有多方维护关联字段的话,不会产生update语句,带来了性能上的
 提升


 


 级联:当主对象做操作侧时候起关联的对象也做与主对象类似的操作,可以减少我们的代码,一般在一方使用,因为我们认为在多方设置级联没有什么意义
      级联 Cascade的取值:
用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用的Cascade取值有:
none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge
          
  级联时 给关联对象设置id时需要注意,hibernate会对关联的对象进行update,二如果关联对象在数据库不存在的时候会报错


          级联删除:
 1 关联关系不是由你控制,级联删除会报外键约束错误
 
 2 控制权交给你 ,如果当前对象是非持久化状态,则先解除关系,把关联字段设置为NULL,然后删除自己
  
 3 当前对象是持久化状态,不管有没有控制权,都可以删除它自己以及它关联的对象






 反转:是否将关联关系维护权交给另一方控制   inverse 在<one-to-many的一方set集合上使用或者是多对多的时候才存在


     3.3 多对多 很少使用  加一个中间表  many-to-many  实则是转成两个一对多  一方使用inverse="true" 一方使用inverse="false"


   4 继承映射
       1 每个类一张表  采用joined-subclass  
       2 每个子类一张表 采用union-subclass   注意不能用Identity的主键生成机制
       3 所有类共用一张表 采用subclass  通过鉴别器字段区分数据
       三种方法的优缺点: 参照详细版 备注 该内容本人觉得不太重要 写在这里影响简洁 有违精简版的初衷
          
      
     5 组件映射  与关联映射区别  组件是一个普通的java类 没有映射文件
         


7 缓存
           缓存是介于物理数据源与应用程序之间,是数据库数据在内存中的存放临时copy的容器,
      其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用的运行性能。
Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,
      如果在缓存中找到了需要的数据(我们把这称做“缓存命中"),则就直接把命中的数据作为结果加以利用,
      避免了建立数据库查询的性能损耗。
      缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
      
      使用缓存的条件
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。


   一级缓存,Session级共享。
save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。 


   二级缓存,SessionFactory级共享。实现为可插拔,
        1 session的:save(这个方法不适合native生成方式的主键),update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,
 但只有(没打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)。


        2 开启二级缓存的步骤:二级缓存的配置和使用:
* 开启二级缓存,修改hibernate.cfg.xml文件
<property name="hibernate.cache.use_second_level_cache">true</property>
* 指定缓存产品提供商,修改hibernate.cfg.xml文件
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
* 指定那些实体类使用二级缓存(两种方法)
* 在映射文件中采用<cache>标签
                                  <cache usage="read-only"/>  
 usage="read-only"是“只读”缓存策略。这个<cache>标签只能放在<class>标签的内部,而且必须处在<id>标签的前面
  这个<cache>标签放在哪些<class>标签下面,就说明会多这些类的对象进行缓存
  其中usage:read-only,read-write,nonstrict-read-write,transactional是缓存策略
* 在hibernate.cfg.xml文件中,采用<class-cache>标签
   <class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" />   注意,这个<class-cache>标签必须放在<mapping>标签的后面。
        
3 监测缓存   用统计接口 可以查看缓存里的一些信息  要打开统计开关 
       




    查询缓存
Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;
1 打开查询缓存开关
2 并且调用query.setCacheable(true)或criteria.setCacheable(true)。
 
 SessionFactory中提供了evictXXX()方法用来清除缓存中的内容


       里面放什么东西
       1 对于查询出来的list是一个个实体的话,查询缓存 缓存实体的id 这样就可以再根据id去一级缓存和2级缓存 有效的利用了缓存
       2 如果list不是一个个实体的话,就是部分字段查询时,则缓存整个结果集
   




8 (lazy)  懒加载  也叫延迟加载  final类会影响懒加载
     1 重要的概念:
   1、lazy的概念,指在需要数据的时候才发出sql
   2、lazy策略只是在session打开期间才是有效的
   3、Hibernate加载本身就是延迟加载,在*.hbm.xml配置文件中<class>里配置lazy="false"将其改
为非延迟加载。
   4、Hibernate属性的延迟加载,在*.hnm.xml配置文件中<property>里配置lazy="false"即可。
            5 类的延迟加载并不影响属性的延迟加载;
   6 连接抓取会使lazy失效
     2 lazy策略可以用在
  * <class>标签上:可以取值true/false
  * <property>标签上,可以取值true/false,这个特性需要类增强    要对类增强器进行类增强 字段内容比较大的时候最有意义的
  * <set>/<list>等集合上,可以取值为true/false/extra
    extra:会发一条比较聪明的sql
  * 用在单端关联<one-to-one>/<many-to-one>等标签上,可以取值false/proxy/no-proxy 
  proxy 懒加载 相当于原来的true
  其中no-proxy 这种懒加载特性需要对类进行增强,其关联对象不是代理类 
   其关联的字段也实现类懒加载 和proxy不一样 如加载学生时候,假设关联字段是xxx,no-proxy不加载xxx,而proxy加载xxx


9   关于  Session的几个主要方法
1.save,persist保存数据,persist在事务外不会产生insert语句 ,在主键生成机制是native中可以看到。登记动作(非native)
2.delete,删除对象
3.update,更新对象,如果数据库中没有记录,会出现异常。
4.get,根据ID查,会立刻访问数据库。
5.Load,根据ID查,(返回的是代理,不会立即访问数据库)。
6.saveOrUpdate,merge(根据ID和version的值来确定是save或update), 调用merge你的对象还是脱管的。merge当你的id在缓存中存在对应的实体时,
 用你的合并它,saveOrUpdate碰到这种情况会报错
7.lock(把对象变成持久对象,但不会同步对象的状态)。 
        8 fiush 将一级缓存与数据库同步 ,有一个例外是,如果对象使用native方式来生成ID(持久化标识)的话,它们一执行save就会被插入。
8 evict(Object object) 将object对象逐出一级缓存
10 clear 清空一级缓存


        session flush 方法主要做了2件事
    * 清理缓存
    * 执行sql
session在什么情况下执行flush
    * 默认在事物提交时
    * 显示的调用flush
    * 在执行某些查询前 ,如iterate
         当程序执行完flush() 就已经向数据库发出了语句,但是我们是看不到数据的,这是由于数据库有一个隔离级别的问题。Mysql 默认重复读。
可以修改数据库的隔离级别 就可以看到了
    
        get和load的区别?
* get不支持lazy,load支持lazy
* 采用get加载数据,如果没有匹配的数据,返回null,而load则抛出异常org.hibernate.ObjectNotFoundException

Query的两个方法,list() 和 iterate() ,两个方法都是把结果集列出来, 但它们有以下区别
   1: 获取数据的方式不一样,当查询缓存不存在时,list()会直接查数据库返回结果集, iterate()会先到数据库中把id都取出来,
然后真正要遍历某个对象的时候先到缓存中找,如果找不到,以id为条件再发一条sql到数据库,
这样如果缓存中没有数据,则查询数据库的次数为n+1。
   2:query.list()用查询缓存,query.iterate不用  
   3 query.iterate利用一级缓存和二级缓存  而list会填充一级缓存和二级缓存 可以这么设想 如果都一次用list,之后用iterate,效果应比较好
  
10 抓取策略 
         连接抓取  fetch="join" 通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效  
查询抓取  fetch="select" 另外发送一条select语句抓取当前对象关联实体或集合
         子查询抓取 fetch="subselect" 另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合 通过子查询in完成
         批量抓取    batch-size设置
11 批量数据操作与分页   
      大批处理 大量操作数据时可能造成内存溢出,解决办法如下:

1.清除session中的数据 flush时将一级缓存与数据库同步
for(int i=0;i<100000;i++)session.save(obj);
for(int i=0;i<100000;i++){
session.save(obj);
if(i% 50 == 0){session.flush();session.clear();}
}
2.用StatelessSession接口:它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。
 StatelessSession s = sessionFactory.openStatelessSession();该接口的方法与Session类似。
3.Query.executeUpdate()执行批量更新,会清除相关联的类二级缓存(sessionFactory.evict(class)),也可能会造成级联,和乐观锁定出现问题
4 jdbc
    
    
     hibernate的分页机制  setFirstResult(int )与setMaxResults(int )
    setFirstResult(2)表示从第3条记录开始
    与setMaxResults(10)取10条记录
   
12 乐观锁与悲观锁
    乐观锁
大多数基于数据版本记录机制(version)实现,一般是在数据库表中加入一个version字段
读取数据时将版本号一同读出,之后更新数据时版本号加一,如果提交数据时版本号小于或等于数据表中的版本号,则认为数据是过期的,否则给予更新
    实现步骤
 <1> 在class标签上 optimistic-lock="version"  如  <class name="com.Person" table="person" optimistic-lock="version">
 <2> 在id属性后增加 <version name="version"/>
 <3> 给pojo类增加version属性 int类型 增加set get方法
测试 修改数据库的数据 当数据没有变化时,版本号不会更新 数据变化时 版本号加1
   采用阻塞代码进行手动表中的version修改看效果  事务提交前阻塞
session.update(p);
BufferedInputStream b = new BufferedInputStream(System.in);
b.read();
session.getTransaction().commit();
    悲观锁
      悲观锁的实现,通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改  利用LockMode.UPGRADE
13 Hibernate性能优化方案
         1.数据库设计优化
         2.Hibernate的hql优化
         3.API的正确使用
         4.缓存的配置管理(如:二级缓存中使用的技术是:EhCascade)
         5.关闭hql的打印输出(在hibernate.cfg.xml中不使用show sql)
         6.ID生成策略,延迟加载,关联优化
14 拦截器与监听器
    拦截器 
       你可以直接实现Interceptor接口,也可以(最好)继承自EmptyInterceptor。拦截器可以有两种:Session范围内的,和SessionFactory范围内的。
       1 Session session = sessionfactory.openSession( new MyInterceptor() ); //session范围内的
       2 new Configuration().setInterceptor( new MyInterceptor() );//SessionFactory范围内的
    监听器 
      如果需要响应持久层的某些特殊事件,你也可以使用Hibernate3的事件框架。 该事件系统可以用来替代拦截器,也可以作为拦截器的补充来使用。
       基本上,Session接口的每个方法都有相对应的事件。比如 LoadEvent,FlushEvent
       1实现监听器接口 如SaveOrUpdateEventListener 重写方法
       2 注册监听器 两种形式  编程的方式可以多个监听器类型之间共享监听器的实例 配置的每次都会产生一个新实例
          2.1 xml配置  在hiberante.cfg.xml注册
     <event type="save-update">
<listener class="com.lxit.basic.MyListener"/>
<listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener"/><!--加上 系统原来的监听器 否则功能会丢失 -->
</event>
 2.2 编程方法注册
     Configuration cfg = new Configuration();
              LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
              cfg.EventListeners().setLoadEventListeners(stack);
        
       


15 opensessioninview模式   解决session关闭后导致 hibernate的懒加载错误问题 Could not initialize proxy - the owning Session was closed 
    
    OpenSessionInViewFilter是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开 Hibernate的Session,
    一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
   
     好处  1解决事务边界问题
           2解决懒加载问题
     缺陷
      1 事务扩大了 加锁导致资源等待时间过长
      2 session范围变大  如果页面加载页面的时间过长 如网速比较慢   内存时间过长 导致系统性能下载
      3 数据库连接不能及时释放
16 spring 整合hibernate 
    1 为什么整合 有什么好处
        1 基于依赖注入的SessionFactory管理机制
2 优秀的session的管理机制 spring提供每事务一次session的机制,大大提高了系统性能.spring对session的管理机制是透明的,无须在代码中操作session
3 统一的事务管理
4 统一的异常处理机制
5 HibernateTemplate 和 HibernateDaoSupport 支持类 


     2 怎么整  3个类 LocalSessionFactoryBean  HibernateTemplate   HibernateDaoSupport 支持
        1 用LocalSessionFactoryBean 创建Sessionfactory
  需要注入 数据源dataSource  hibernate属性hibernateProperties  映射文件mappingResources
        2 给HibernateTemplate类注入Sessionfactory,之后就可以用HibernateTemplate类封装的hibernate api操作了
3 继承 HibernateDaoSupport类使代码更加简洁(表现在少写注入sessionfactory方法),给HibernateTemplate类注入Sessionfactory,之后就可以
 用它的getHibernateTemplate方法获得模板类了
     3 事务整合 采用hibernate的事务管理器
     4 懒加载问题  
         1 get方法
2 配置lazy=false
         3 回调机制  使用HibernateTemplate的execute方法进行回调,在里面操纵session
         4 用HibernateInterceptor
5 如果是在wen环境中 用opensessioninview模式 spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor(在spring mvc SimpleUrlHandlerMapping中使用 )
   
        5多个配置文件
17 最佳实践 参考hibernate官方参考手册
    
18 其他  (孤儿删除 唯一记录uinqueResult  外置命名查询  离线查询   不适合的项目场景)
   1 离线查询 DetachedCriteria类使你在一个session范围之外创建一个查询,并且可以使用任意的 Session来执行它。 用在动态sql的构建上 如在struts1或struts2中构建
    DetachedCriteria query = DetachedCriteria.forClass(Cat.class).add( Property.forName("sex").eq('F') );
    List results = query.getExecutableCriteria(session).setMaxResults(100).list();
   2 唯一记录uinqueResult 当你确定本次查询只有一条记录 可以用uinqueResult返回一个实体对象
   session.createQuery(sql语句).uniqueResult()
            3 孤儿删除
     举个例子 一对多情况  班级与学生 我们查询出一个班级后,班级有一个属性set存放着学生,此时我们把set中的某个删除,意思
     从set集合删除了一个学生,set集合发生了变化,此时这个删除的学生变成了孤儿,那么当我们update班级,同步数据库的时候要不要
     从数据库里删除掉刚刚那个学生记录呢 ,如果采取的级联不是孤儿删除的话,则不删除这条记录,而是把这条学生记录的所对应的班级
     id修改为NULl,如果级联是孤儿删除的话,则删除该条记录
     
    
19 Hibernate不适合的场景
    1 不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing联机事务处理)。
    2 对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。
    3 数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。