美文网首页
SSH框架之Hibernate进阶持久化对象及事务管理(二)

SSH框架之Hibernate进阶持久化对象及事务管理(二)

作者: Seapp | 来源:发表于2019-03-27 17:07 被阅读0次

    第一节: 持久化类的编写规则

    1.1 什么是持久化类

    • 持久化:将内存中的一个对象持久化到数据库中的过程,Hibernate是持久层的ORM映射框架。
    • 持久化类:一个java对象与数据库表建立了映射关系,则该类被称为持久化类。

    1.2 持久化类的编写规则

    • ①对持久化类提供一个无参数的构造方法 :Hibernate底层需要使用反射生成实例;
    • ②属性需要私有,对私有属性提供public的get和set方法:Hibernate中获取对象值;
    • ③对持久化类提供一个唯一标识OID与数据库主键对应:Hibernate中通过持久化类的OID的属性来区分是否是同一个对象。
    • ④持久化类中的属性尽量使用包装类型类来定义:基本数据类型的默认值为0,易引起歧义。
    • ⑤持久化类不要使用final进行修饰:延迟加载本身是Hibernate一个优化的手段,返回的是一个代理对象(对没有实现接口的类产生代理,javassist使用了非常底层的字节码增强技术,集成这个类进行代理)如果该类不能被继承,则不能产生代理对象,则延迟加载也就失效了。

    1.3 Hibernate中主键生成策略

    • 1.3.1 主键的分类
      自然主键:主键的本身就是表中的一个字段(实体中的一个具体属性);
      代理主键:主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)。
      在实际的开发中,尽量使用代理主键,满足OCP原则(对程序的扩展是open的,对源代码的修改是close)

    • 1.3.2 主键的生成策略
      在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库。在Hibernate中提供了多种主键的生成策略。如下:

    <id name="cust_id" column="cust_id">
                <!--主键生成策略-->
                <!--
                * increment:Hibernate中提供的自动增长的机制,适用于long,short或int类型的主键。在单线程中使用,
                    在集群下不要使用(存在线程安全的问题);
                * identity:适用于short,int,long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增
                    长机制的数据库(MySql,MSSQL),Oracle不适用。
                * sequence:适用于short,int,long类型的主键,采用的是序列的方式。Oracle支持。
                * uuid:适用于字符串类型的主键,使用hibernate中的随机方式生成字符串主键。
                * native:本地策略,可以在identity和sequence之间自动切换。
                * assigned:hibernate放弃主键的管理,需要用户自行设定
                * foreigen:使用另外一个相关联的对象的标识符。它通常和 <one-to-one> 联合起来使用。
                -->
                <generator class="native"/>
            </id>
    

    第二节:持久化类的三种状态

    Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理之间化类,将持久化类分成三种状态。

    • 瞬时态(transient)
      瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建,开辟内存空间的对象,不存在持久化标识OID(相对于主键值),尚未与Hibernate session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是gu孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。
    • 持久态(presistent)
      持久态的对象拥有持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。(持久态的对象可以自动更新数据库)
    • 脱管态(detached)
      脱管态也称离线态或者游离态,当某个持久化状态的实例与session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。

    2.1 如何区分这三种状态?

    瞬时态对象,没有唯一标识OID,没有被session管理。
    持久态对象,拥有唯一标识OID且被session管理。
    脱管态对象,拥有唯一标识OID,但没有被session管理。

    2.2 持久化对象的三种状态转换

    image.png

    第三节 Hibernate的一级缓存

    缓存是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提供应用的运行性能。缓存汇总的数据是数据存储源中数据的拷贝。缓存的物理介子通常是内存。
    Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据库数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。

    3.1 什么是Hibernate的一级缓存

    Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的Java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配的OID值的对象,就直接将该对象从一级缓存中取出使用。不会再查询数据库。如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
    在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为Session基本的缓存。

    3.2 Hibernate的一级缓存特点:

    ①当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动把从数据库中查询到的相应对象信息加入到一级缓存中去。
    ②当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
    ③当调用session的close()方法时,Session缓存会被清空。

    3.3 Hibernate一级缓存的特殊区域(快照区)

    image.png

    第四节 Hibernate的事务管理

    4.1 数据库事务的回顾

    • 4.1.1 什么是事务:
      事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元执行时要么全部成功,要么全部失败;

    • 4.1.2 事务的特性:
      原子性:代表事务不可分割
      一致性:代表事务执行前后,数据完整性保持一致
      隔离性:代表一个事务的执行过程中,不应该受到其他事务的干扰
      持久性:代表事务执行完成后,数据就持久化保持中数据库中

    • 4.1.3 如果不考虑事务的隔离性,会引发那些问题:
      ①读问题

      • 脏读:一个事务读到另一个事务未提交的数据
      • 不可重复读:一个事务读到另一个事务已经提交update的数据,导致在前一事务中多次查询结果不一致
      • 虚读:一个事务读到另一个事务已经插入insert数据,导致在前一个事务多次查询结果不一致

      ②写问题(了解)

      • 引发两类丢失更新问题。
    • 4.1.4 读问题的解决方法
      设置事务的隔离级别:

      • Read uncommitted:以上读问题都会发生
      • Read committed:解决脏读,不可重复读和虚读有可能发生
      • Repeatable read:解决脏读和不可重复读,虚读有可能发生
      • Serializable:解决所有读的问题

    4.2 Hibernate中事务隔离级别的设置

    在hibernate配置文件中配置如下属性:

       <!--设置事务的隔离级别-->
            <!--
                隔离级别取值对应关系:
                1 : Read uncommitted isolation
                2 : Read committed isolation
                4 : Repeatable read isolation
                8 : Serializable isolation
            -->
            <property name="hibernate.connection.isolation">4</property>
    

    4.3 Service层事务管理

    Service业务层的复杂业务对DAO层的调用必须保证连接对象是同一个,通常的解决方案有:
    ①:向下传递
    ②:使用ThreadLocal对象进行绑定,将连接对象绑定到当前线程中,在DAO的方法调用中,通过当前线程来获取连接对象。

    4.5 Hibernate中针对Service层事务的管理方法

    Hibernate框架内部已经绑定了ThreadLocal,在SessionFactory中提供了一个方法getCurrentSession()来获取连接对象。但这个方法的调用前提需修改Hibernate的属性,如下

     <!--配置Hibernate对Session事务的管理方式-->
            <!--
                用于指定session管理方式
                * thread : Session对象的生命周期与本地线程绑定。
                * jta:Session对象的生命周期与JTA事务绑定
                * managed: Hibernate委托程序来管理Session对象的生命周期
            -->
            <property name="hibernate.current_session_context_class">thread</property>
    

    则session连接对象的获取方式:该连接对象在执行完成后无需手动关闭。

    /**
         * currentSission对象的获取
         * @return
         */
        public static Session getCurrentSission(){
            return sessionFactory.getCurrentSession();
        }
    

    第五节:Hibernate中的查询对象简介

    5.1 Query

    Query代表面向对象的一个Hibernate查询操作。在Hibernate中,通常使用session.createQuery()方法接受一个HQL语句,然后调用Query的list()或uniqueResult()方法执行查询。所谓的HQL是Hibernate Query Language缩写,其语法很像SQL语法,但它是完全面向对象的。

    在Hibernate中使用Query对象的步骤:

    • 获得Hibernate的Session对象
    • 编写HQL语句
    • 调用session.createQuery()创建查询对象
    • 如果HQL语句包含参数,则调用Query的setXxx设置参数
    • 调用Query对象的list()或uniqueResult()方法执行查询
        @Test
        public void findByQuery(){
            Session currentSission = HibernateUtil.getCurrentSission();
            Transaction transaction = currentSission.beginTransaction();
            //查询所有人员数据
    //        String hql = "from Customer";
            //条件查询(使用命名参数的方式)
    //        String hql = "SELECT c from Customer c where c.cust_name like :name ";
            //分页查询
            String hql = "from Customer";
    
            Query query = currentSission.createQuery(hql);
            //插入检索条件
    //        query.setParameter("name","小%");
            //插入分页查询条件
            query.setFirstResult(0);//从第i条开始
            query.setMaxResults(1);//每页展示的数据为0条
            List<Customer> list = query.list();
            for (Customer customer: list) {
                System.out.println(customer.toString());
            }
            transaction.commit();
        }
    

    5.2 criteria

    Query by Criteria 是更加面向对象的一种查询方式。

     @Test
        public void findByCriteria(){
            Session currentSission = HibernateUtil.getCurrentSission();
            Transaction transaction = currentSission.beginTransaction();
            //通过criteria进行查询
    //        Criteria criteria = currentSission.createCriteria(Customer.class);已过时
            //1.import javax.persistence.criteria.CriteriaQuery;获取Criteria对象
            CriteriaQuery<Customer> query = currentSission.getCriteriaBuilder().createQuery(Customer.class);
            //2.指定根条件
            query.from(Customer.class);
            //3.执行查询
    //        List<Customer> resultList = currentSission.createQuery(query).getResultList();
            //3.1 执行分页查询
            List<Customer> resultList = currentSission.createQuery(query)
                    .setFirstResult(0)//设置开始位置
                    .setMaxResults(1)//设置每页条数
                    .getResultList();
            for (Customer customer: resultList) {
                System.out.println(customer.toString());
            }
            transaction.commit();
        }
    

    5.3 SQL Query

    SQLQuery用于结束sql,特别复杂的情况下使用SQL。

    相关文章

      网友评论

          本文标题:SSH框架之Hibernate进阶持久化对象及事务管理(二)

          本文链接:https://www.haomeiwen.com/subject/dpravqtx.html