Hibernate小结

一、Hibernate概述
    1.概述

        (1)JDBC优缺点
            * 优点:直接底层操作,提供了很简单、便捷的访问数据库的方法,跨平台性比较强.灵活性比较强,可以写很复杂的SQL语句.
            * 缺点:    无法做到面向对象编程.
                    操作繁琐,重用性低.
                    批量操作时,频繁与数据库交互,效率极低.
        (2)Hibernate解决了什么问题(优点)
            * 它实现了面向对象的数据库编程.
            * 相比JDBC来说,代码书写简单化了.
            * Hibernate提出缓存机制,反问数据的效率得到极大提高.
            * Hibernate使用java反射机制,而不是字节码增强程序类实现透明性.
        (3)ORM框架-hibernate
            * 是一个开源的持久层框架.
            * ORM(Object/Relation Mapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.
            * 连接java应用和数据库的中间件.
            * 对JDBC进行封装,负责java对象的持久化.
            * 在分层结构中处于持久化层,封装对数据库的访问细节,是业务逻辑层更专注于实现业务逻辑.
     2.一个简单的Hibernate程序
        (1)所需要的lib包
            * antlr-....jar: 语言转换工具,hibernate利用它实现Hql到sql的转换.
            * common-collections-...jar: collections Apache的工具集,增强java对集合的处理能力.
            * dom4j-...jar: xml解析器
            * javassist-...GA.jar: 代码生成工具,hibernate用它在运行时扩展java类.
            * jta-...jar: 标准的java事务处理接口.
            * mysql-connector-jara-..-bin.jar: mysql的连接驱动包.
            * slf4j-api-...jar: hibernate使用的一个日志系统.
            * log4j.jar
            * slf4j-log4jf2.jar:同上.
            * hibernate3.jar: 核心包.
        (2)写配置文件:hibernate.cfg.xml
            写持久化类:例如:Person.java
            写持久化类的映射文件:Person.hbm.xml
        (3)加载配置文件并获得SessionFactory
            private static SessionFactory sessionFactory = null;
            static{
                Configuration  configuration = new Configuration();
                //默认加载classpath下的hibernate.cfg.xml,如果在别处请写明path
                configuration.configure("com/liu/manytomany/hibernate.cfg.xml");
                sessionFactory = configuration.buildSessionFactory();
            }
     3.Hibernate的运行流程
        (1)应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息.
        (2)用这些信息生成一个SessionFactory对象.
        (3)然后从SessionFactory对象生成一个Session对象.
        (4)并用Session对象生成Transaction对象.
            * 可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()
                等方法对PO进行加载,保存,更新,删除,等操作.
            * 在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;
                如果没有异常,Transaction对象将提交这些操作到数据库中.

        (5)运行流程图如下:


    4.主键生成机制
        标识符生成器:    描述
        Increment:     由hibernate自动以递增的方式生成标识符,每次增量1
                    Hibernate会先读取 NEWS表中的主键的最大值, 而接下来向 NEWS 表中插入记录时,
                    就在 max(id) 的基础上递增, 增量为 1.
                    适用于只有单个 Hibernate 应用进程访问同一个数据库的场合.
                    
        Identity:     由底层数据库生成标识符,条件是数据库支持自动增长数据类型
                    支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase 等.
                    
        Sequence:    Hibernate根据底层数据库序列生成标识符,条件是数据库支持序列
                    支持序列的数据库包括:DB2、Oracle等.
                    
        Native:        根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo
                    适合跨数据库平台开发.
                    
        Uuid.hex:    Hibernate采用128位的UUID算法来生成标识符.
                    该算法能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的
                    主键比整数类型的主键占用更多的数据库空间.
                    
        assigned:    适用于自然主键,由java程序负责生成标识符.不能把setID()声明为private.
                    使用于主键有一定含义等,一般不推荐使用.
     5.持久化对象的状态(三种)
        (1)持久化状态(也叫"托管",Persist)
            * OID不为null
            * 位于Session缓存中
            * 持久化对象和数据库中的相关记录对应.
            * Session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库.
            * 在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象.
            * Session的许多方法都能出发java对象进入持久化状态:
                Session的save()方法把临时对象转变为持久化对象.
                Session的load()或get()方法返回的对象总是处于持久化状态.
                Session的update()、saveOrUpdate()和lock()方法使游离对象转变为持久化状态.
                当一个持久化对象关联一个临时对象,在允许级联保存的情况下,Session在清理缓存时会把这个临时对象也转变为持久化对象.
        (2)临时状态(transient)
            * 在使用代理主键的情况下,OID通常为null
            * 不处于Session的缓存中
            * 在数据库中没有对应的记录
            * 以下情况,java对象进入临时状态:
                当通过new语句刚创建了一个java对象,它处于临时状态,此时不知数据库中的任何记录对应.
        (3)游离状态(也叫"脱管",Detached)
            * OID不为null
            * 不再处于Session的缓存中.
            * 一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录.
            * Session的一下方法可以使持久化对象转变成游离对象
                调用Session的close()方法时,Session的缓存被清空,缓存中的所有持久化对象都变为游离对象,
                    如果在应用程序中没有引用变量引用这些游离对象,他们就会结束生命周期.
                Session的evict()方法能够从缓存中删除一个持久化对象,使它变为游离状态,当Session的缓存中
                    保存了大量的持久化对象,会消耗许多内存空间,为了提高性能可以考虑调用evict()方法,从缓存中删除
                    一些持久化对象.

        (4)Hibernate对象状态转化图


二、细说Hibernate
    1.映射一对多关联关系

        (1)单向关联
            * 仅建立一对多的关联
            * 仅建立多对一的关联        
            * Hibernate使用<many-to-one>元素来映射多对一的关联关系.
            * 级联保存和更新:当hibernate持久化一个临时对象时,默认情况下不会自动持久化所关联的其它临时对象,会抛出
                        TransientObjectException.若设定<many-to-one>的cascade属性为save-update
                        的话,可以实现自动持久化所关联的对象.
        (2)双向关联
            * 双向1-n需要在1的一段可以访问n的一端,反之亦然.        
            * Hibernate使用set元素来映射一对多关联关系.
        (3)inverse属性详解
            * 来源
                在hibernate中通过对inverse属性的值决定是由双向关联的哪一方来维护表的关系.默认为false
                inverse=false的为主动方,为true的为被动方,主动方负责维护关联关系.
            * 在没有设置inverse=true的情况下,父子两边都维护父子关系.
            * 原则:
                在1-n关系中,将n方设为主控方将有助于性能改善(例如:主席记住全国人民的名字不可能,反过来则很容易).
                如果把1方设为主控方,会额外多出update语句.因此,一般把1的一方设置为true,来提高效率.
        (4)使用inverse小结
            * 在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,可以提高性能.
            * 在建立两个对象的关联时,应该同时修改关联两端的对应属性:
                        customer.getOrders().add(order);
                        order.setCustomer(customer);
                这样会使程序更加健壮,提高业务逻辑层的独立性.
              同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
                          Customer.getOrders().remove(order);
                          Order.setCustomer(null);
        (5)<set>元素的order-by属性
            例如:设置order-by属性等于id,则会根据id进行排序.

        (6)cascade属性一览


     2.映射多对多的关联关系
    3.深入Session
    
        (1)session概述
            Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存,
            更新, 删除和加载Java 对象的方法.
        (2)理解session的缓存
            * 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存.
                 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
            * 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,
                该对象仍然处于生命周期中。当试图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库.
        (3)清理session的缓存
            * Session具有一个缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应.
                Session能够在某些时间点,按照缓存中的对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程称为清理缓存(flush)
            * 默认情况下Session在一下时间点清理缓存:
                当应用程序调用Transaction的commit()方法时,该方法先清理缓存(session.flush()),
                    然后再向数据库提交事务(tx.commit()).
                当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先清理缓存,
                    以保证查询结果能够反映持久化对象的最新状态.
        (4)flush,commit,refresh,clear区别
            * flush:进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步,执行一些sql语句,但不提交事务.
            * commit: 先调用flush()方法,然后提交事务.
            * refresh: 刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.
            * clear: 清空缓存,等价于list.removeAll();
        (5)Session执行批量操作
            示例:
                for(int i=0; i<10000; i++){
                    Session.save(object);
                }
            注意:这样会使一万个对象的缓存全部存在内存中,这样做加大了内存的压力,所以应该定期清理session的缓存,
                也就是flush一下,这样内存才能保证足够的空间.
        (6)session的产生方式
            *  sessionFactory.openSession
                  每次都会新创建一个session,只要新创建一个session,hibernate都会打开应用程序和数据库的连接
                    所以这种方式效率比较低
              *    sessionFactory.getCurrentSession
                      如果当前线程中没有session,先openSession,然后把session存放到当前线程中
                      从当前线程中得到session
                      crud操作必须有事务环境
                      不用手动去close掉
    4.session.load与session.get方法
        (1)根据需求选择两种方法
            * 需求一:用户要取数据库的一张表的一个字段,这个字段很可能就是一个字符,很短.
            * 需求二:用户要取数据库的一张表的一个字段,而这个值很可能是blob类型,也许存的是一个很大的视频.
            * 上述两个需求,针对数据库中的大数据,不希望特别早的加载到内存中,当用到它的时候才加载.
        (2)类的懒加载
            *  在默认情况下,类就是执行懒加载
            *  只有使用了load方法以后才能用懒加载
                *  如果在相应的映射文件中,设置<class>的lazy="false"懒加载将失去效果
        (3)集合的懒加载
                 针对一多对的情况或者多对多的情况
                 根据一方加载set集合,决定在什么时候给set集合填充数据
              * true
                     在遍历集合中的每一个元素的时候发出sql语句
             * false
                在得到集合的时候,发出sql语句
             * extra
                    students.size()这个时候用extra仅仅能够得到大小
        (4)单端关联(<one-to-one> 或 <many-to-one>)
            lazy可选属性: False,proxy, no-proxy
            * 对于多对一的<many-to-one>:由于数量特别少,选什么都可以,差别不大
            * 对于一对一的<one-to-one>:
                proxy:当前对象的単值相关对象只有在调用它的主键外的其他属性的get方法时才加载它,相当于true.
                no-proxy:当前对象的単值相关对象只有在调用它的属性时才加载,需要字节码增强.
     5.Hibernate检索策略
        (1)类级别的检索策略(class的lazy属性,3.x默认为true,2.x默认为false)
            * 立即检索:lazy=false
            * 延迟检索: lazy=true
            * get():总是使用立即检索,没有数据则返回空.
            * load(): lazy=true时,延迟检索,调用getXxx()(非getOID())时进行查询,没有数据则抛异常.
                      lazy=false立即检索,没有数据则抛异常.
        (2)关联级别的检索策略
            在映射文件中,用<set>元素来配置一对多关联关系,<set>元素有lazy(默认为true)和fetch(默认为select)属性.
            * fetch取值join,lazy属性将被忽略,此时策略均为:迫切左外联接检索
            * fetch取值select,lazy取值false,此时策略为:立即检索
            * fetch取值select,lazy取值true,此时策略为:延迟检索
            * fetch取值select,lazy取值extra,此时策略为:延迟检索
            * fetch取值subselect,根据lazy取值也分三种情况.
        (3)各种检索策略优缺点
            * 立即检索
                优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象.
                缺点:select语句多
                    可能会加载应用程序不需要访问的对象,浪费许多内存空间
                优先考虑使用场合:类级别
                            应用程序需要立即访问的对象
                            使用了二级缓存
            * 延迟检索
                优点:由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。
                    因此能提高检索性能,并节省内存空间
                缺点:应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化.
                优先考虑使用场合:一对多或者多对多关联.
                            应用程序不需要立即访问或者根本不会访问的对象.
            * 迫切左外连接检索
                优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。
                    使用了外连接,select语句少.
                缺点:可能会加载应用程序不需要访问的对象,浪费内存.
                    复杂的数据库表连接也会影响检索性能.
                优先考虑使用场合:多对一.
                            需要立即访问的对象.
                            数据库有良好的表连接性能.
     6.一级缓存(session级别的缓存)
        (1)一级缓存的生命周期:
            一级缓存在session中存放,只要打开session,一级缓存就存在了,当session关闭时,一级缓存就不存在了.
        (2)利用session的get,update,save方法可以把数据存放到一级缓存中.
        (3)利用session的get方法可以获取一级缓存中的数据.
        (4)只要是一个持久化状态的数据就在一级缓存中,利用session.refresh方法,可以同步数据库的数据到一级缓存中,只能同步一个对象.
        (5)session.evcit方法可以从一级缓存中清除某一个对象,并把这个对象从持久化状态转为脱管状态(游离)
        (6)session.clear()方法可以清空一级缓存的所有对象.    
        (7)session中存放私有数据,通过新建session和从当前线程中获取session保证数据的安全性.
    7.二级缓存(sessionFactory级别的缓存,是外置缓存)
        (1)二级缓存是一个共享缓存,存放共享数据.
        (2)二级缓存在sessionFactory中,因为sessionFactory本身是线程安全的,所以二级缓存的数据线程也是安全的.
        (3)二级缓存的声明周期和sessionFactory一样.
        (4)通过session的get,load,update方法都可以把对象放入到二级缓存中.
        (5)通过session的get,load方法可以提取二级缓存的数据.
        (6)启用二级缓存的条件:
            * 很少被修改
            * 很多系统模块都要用到
            * 不是私有数据,是共享数据.
        (7)配置二级缓存
            二级缓存供应商有许多,这里我们采用EhCache,此java缓存框架是Hibernate中默认的CacheProvider.
            * 首先拷贝ehcache-...jar到当前工程的lib目录下.
            * hibernate.cfg.xml配置文件中开启二级缓存
                <property name="cache.use_second_level_cache">true</property>
            * hibernate.cfg.xml中指定缓存的供应商
                <property name="cache.provider_class">
                    org.hibernate.cache.EhCacheProvider
                </property>
            * 指定使用二级缓存的类
                方法一:在持久化类的映射文件中配置(*.hbm.xml)
                    <class name="com.liu.Customer" table="customer" lazy="false">
                        <!-- 配置类级别的二级缓存 -->
                        <cache usage="read-write"/>
                        ....
                        <set name="orders" table="orders" inverse="true">
                            <!-- 配置集合级别的二级缓存-->
                            <cache usage="read-write"/>
                    </class>    
                方法二:在hibernate.cfg.xml文件中配置(建议使用)
                    <!-- 指定使用二级缓存的类放在mapping下面 -->
                    <!-- 配置类级别的二级缓存 -->
                    <class-cache class="com.liu.Customer" usage="read-write"/>
                    <class-cache class="com.liu.Order" usage="read-write"/>
                    <!-- 配置集合级别的二级缓存 -->
                    <collection-cache collection="com.liu.Customer.orders" usage="read-write"/>
                把二级缓存的数据放在磁盘上: 在classpath下存放一个文件ehcache.xml
     8.常用方法
        (1)session.get 或session.load
            * 把一个对象变成持久化状态的对象.
            * 把该对象存到session的一级缓存中.
            * 把该对象存到二级缓存中.
            * session.clear清空一级和二级缓存.
        (2)session.update
            * 也会把对象存到二级缓存
        (3)session.save
            * 该方法只把对象放入到一级缓存中,并没有放入到二级缓存中.
            * 在执行session.flush的时候,把数据保存到二级缓存中.
        (4)查询缓存
            * 查询缓存是建立在二级缓存基础之上的.
            * 查询缓存不是默认开启的,需要在hibernate.cfg.xml中设置
                <property name="cache.use_second_level_cache">true</property>
            * 在代码中开启:query.setCacheable(true);
        (5)无论list,load还是iterate,只要读出一个对象,都会填充缓存。
            但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,
            如果在缓存里面有,就从缓存取,没有的话就去数据库load
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值