Day39-Hibernate02

主要内容:

* * Hibernate持久化类的编写规则
* * Hibernate主键生成策略
* * Hibernate持久化类的三种状态
* * Hibernate一级缓存(核心)
* * Hibernate事务管理(核心)
* * Hibernate其他的API:Query,Criteria, SQLQuery

hibernate持久化类及编写规则:

  1. 持久化类的定义

    • 持久化:将内存中的数据永久存储到关系型数据库中。把容易丢失的数据,长期保存的过程,持久化。
    • 持久化类:指的是一个java类与数据库表建立了映射关系,那么这个类就成为持久化类;参与这个持久化操作的类,就是持久化类。
    • 持久化类的特点:一个类与数据库的表建立了映射关系。
      • 持久化类= java类(更类似javaBean类) + 映射文件
  2. 持久化类的编写规则
    a. 有无参的构造方法。
    * hibernate底层使用反射创建对象。 比如get和load方法

    b. 属性私有化,对属性提供get/set方法。
    * hibernate底层会将查询到的数据进行封装

    c. 属性尽量使用包装类。
    * int和Integer的区别:初始化的值:int–》0 ,Integer –>null。
    因为,在数据库里面,0值和null值,代表不同的意义。

    d. 持久化类要有一个唯一标识OID与表的主键对应。
    * java判断两个对象相同,是通过地址进行比较。而hibernate判断两个持久化对象是否相同,是通过OID的值。(缓存的时候,体现)

    e. 持久化类,不要使用final修饰符。
    * final修饰类,这个类不能被继承。导致load方法的延迟加载特性失效。
    * 因为load的延迟加载的原理:通过继承持久化类,生成一个代理对象,从而实现延迟加载。如果用final修饰符,不能被继承,导致不能生成代理对象,hibernate就会使用直接加载。

  3. 细节
    a. 数据库中0值和null值的不同
    * 工资表中,有人的工资是0, 有人的工资是null.
    * 领导指示,工资低于1000的,加200块钱。
    * update 工资表 set 工资=工资+200 where 工资<1000;
    * 领导指示,工资低于1000的或者没有工资的人,加200块钱。
    * update 工资表 set 工资=工资+200 where 工资<1000 or 工资 is null;

    b. final修饰符的影响
    * load机制的延迟加载,是通过继承持久化对象,产生一个代理对象实现的。final修饰类之后,无法继承,从而无法产生代理对象,导致延迟加载失效。load方法,会使用立即加载。

    c. 唯一标识符OID的理解
    * 在持久化类中,对表示主键属性的统称。


hibernate主键及主键生成策略:

1)数据库的主键的类型
1. 1数据库主键分类
* 自然主键:对象本身的属性,作为表的主键(把具有业务含义的字段作为主键)
* 代理主键:不使用对象本身的属性,而使用没有任何意义的一个字段,作为主键。(推荐)
使用自然主键,就会失去主动的控制权。一般情况下就都不要使用自然主键,推荐使用代理主键

1.2. 主键的生成方式
* 自然主键:需用户手动输入
* 代理主键:由系统自动生成。mysql: auto_increment, oracle:序列

2)hibernate的主键生成策略:
这里写图片描述
hibernate的跨数据库平台特性:
核心配置文件中的方言dialect、映射配置文件中的主键生成策略,都体现了hibernate的跨数据库平台开发的特性。主键生成策略,使用native是正确的,因为可以支持跨数据库平台的特性。


hibernate持久化对象的状态

1)持久化对象的三种状态:
持久化对象,就是持久化类的生成的实例,hibernate为了更好的管理持久化对象。给持久化对象分为三种状态。

*         * 瞬时状态(Transient): 没有oid,没有关联session对象
* 
    * 也成为临时态或者自由态,瞬时态的实例是由new命令创建的,开辟内存空间的对象,不存在持久化表示OID,尚未与Hibernate session关联。在数据库中也没有记录,失去引用后将被JVM回收。与数据库中的数据没有任何关联,仅仅是一个信息携带的载体。

*         * 持久状态(Persistent):有oid, 关联session对象
* 
    * 持久态的对象存在持久化表示OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录。每条记录值对应唯一的持久化对象。需要注意的是,持久态对象是在事务还未提交前变成持久态的。

*         * 脱管状态(Detached): 有oid, 没有关联session对象
* 
    * 脱管态也成为离线态或者游离态,当某个持久化状态的实例与session的关联被关闭的时候就变成了托管态,托管态对象存在持久化标志OID,并且仍然与数据库中的数据存在关联,只是失去了当前Session的关联。托管状态对象发生改变的时候Hibernate不能够检测到。

测试:

   @Test
      public void test03() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           // 瞬时态:没有oid,和session没有关联
           User user = new User();
           user.setUsername("刘德华3号");
           user.setPassword("123");

           System.out.println("瞬时:" + user.toString());

           // 持久态:有oid,和session有关联
           session.save(user);
           System.out.println("持久:" + user.toString());

           transaction.commit();
           session.close();

           // 托管态:有oid,和session没有关联
           System.out.println("托管:" + user.toString());
      }

2) 持久化对象三种状态的切换
a. 保存: 瞬时–》持久态–》脱管态
b. 查询:持久态–》脱管态
这里写图片描述

3)持久化对象处于持久态的特性(重点)
* 自动更新数据库。
如果调用了update方法也没有错,但是内部控制了,只会执行一次。
这里写图片描述


Hibernate的一级缓存

1)缓存的理解:
缓存理解成一种策略。利用高速存储区域的空间,提高对低速存储区域数据的读取速度。即空间换时间。内存是缓存的一种体现,缓存不仅仅只是内存
缓存的原理
这里写图片描述
* 利用高速存储区域,保存低速存储区域的数据,从而实现缓存功能。空间换时间。作用是降低应用程序直接读取永久性数据存储源的频率,从而提高应用的运行性能。缓存的物理介质通常是内存;
* 案例:cpu–内存–硬盘

2)缓存的替换算法
a. 原因:高速存储区空间,必定要小于低速存储空间。随着程序的运行,缓存的数据越多,高速存储区空间会全部使用,即存储空间满。这个时候,需要缓存替换算法。
b. 最基本两个策略:
(1) 先入先出(队列):最先保存的数据,最先被替换。
(2) 最少使用:使用最少的数据,最先被替换。
还有多种策略和算法,比如综合上述两种策略,采取权重算法。

3)hibernate缓存
3.1. 缓存分类:一级缓存和二级缓存。
a. 一级缓存: session级别的缓存,默认可使用。一级缓存的生命周期与session一致。
只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束,
b. 二级缓存:SessionFactory级别的缓存,需要手动配置才能使用的。二级缓存的生命周期与SessionFactory一致。不过,实际中,用redis代替二级缓存,故不使用。(集群环境下,二级缓存效果不佳)
这里写图片描述

3.2. hibernate缓存的目的
* 减少对数据库访问的次数。

3.3. hibernate缓存的原理
* 通过OID来区分持久化对象。OID相同,认为是同一个持久化对象。

3.4. hibernate缓存的使用
a. hibernate调用get/load进行查询
b. 先查询缓存。根据OID区分,如该持久化对象存在,直接使用
c. 没有查到,发送sql语句,从数据库查询。
d. 查询到的数据,session缓存保存一份(自动),并返回给调用程序。
【备注】hibernate调用save(),update()等操作,也使用缓存。

3.5. 代码演示缓存的存在

     @Test
      public void test03() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           User user = session.get(User.class, 2);
           System.out.println("第一次查询:" + user.toString());

           User user2 = session.get(User.class, 2);
           System.out.println("第二次查询:" + user2.toString());

           transaction.commit();
           session.close();
      }

打印语句为:
这里写图片描述
可以看到,查询数据库的sql语句值出现了一次,说明只查询了一次数据库,第二次查询相同id的user的时候,使用session的一级缓存。

3.6. 清除缓存的方法:session的API
* clear():清除一级缓存中全部对象
* evict(Object obj): 清除一级缓存中指定对象

3.7一级缓存的内部结构:快照区
这里写图片描述
举例说明:
id为2的user的username的值原本就为“测试”,先取出来之后,设置为了“修改了修改了”,然后再改回了原来的值“测试”,然后再进行事务的提交commit;

     @Test
      public void test03() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           User user = session.get(User.class, 2);
           System.out.println("第一次查询:" + user.toString());

           user.setUsername("修改了修改了");
           user.setUsername("测试");
           transaction.commit();
           session.close();

      }

结果:
只有第一次查询的sql语句,并没有数据库的更新语句!
这就是快照的体现,当从数据库中取出user的时候会同时复制一份对象到Hibernate快照中去,当使用commit方法提交事务的时候,就会清理Session的一级缓存,这时候会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象的属性发生了变化,就会执行了update语句,将缓存的内容同步到数据库并更新快照,如果一致,那么update语句就不会执行。
这里写图片描述

!!!!!!
引申出来:hibernate持久化对象处于持久态时自动更新数据库的原理:
这里写图片描述
原理:
a. session内部,有两个存储区:一个是一级缓存区,一个是快照区
b. get/load查询到的数据,一级缓存区和快照区,各自保存一份(注意:保存对象本身,而不是引用)。
c. 持久状态下,进行修改。只修改缓存区的数据,快照区的数据不变(即一直保持和数据库的原始数据一致)。
d. 事务提交时,对比缓存区和快照区。如果相同,说明数据与数据库的原始数据一致,无需update。如果不同,执行update。


hibernate的事务控制:

事务的特性:
* 事务包含4种重要的特性,统称为ACID(原子性、一致性、隔离性和持久性)
a. 原子性(Atomic):事务是一个整体的工作单元,事务对数据库所做的操作要么都执行,要么都取消。如果某条语句执行失败,则所有语句全部回滚。
b. 一致性(ConDemoltent):事务在完成时,必须使所有的数据都保持一致状态。如果事务成功,则所有数据将变为一个新的状态;如果事务失败,则所有数据将处于开始之前的状态。
c. 隔离性(Isolated):指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
d. 持久性(Durability):当事务提交后,对数据库所做的修改就会永久保存下来。

事务的并发问题
* 在实际的使用中,数据库被多个用户共同访问,即出现多个事务并发执行的情况。
* 在多个事务同时使用相同的数据时,可能出现并发问题。 查询,无需事务的操作。因为,查询,不会修改数据
* 并发问题分类:
a. 脏读: 一个事务读到了另一个事务未提交的数据.
b. 不可重复读: 一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致.
c. 虚读(幻读): 一个事务读到了另一个事务已经提交的insert数据,导致多次查询结果不一致.

事务的隔离级别
1. 解决事务并发问题并考虑数据库的效率,设计了多种隔离级别:

  1. 数据库事务的隔离级别有4个
    (1). 由低到高依次为Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(重复读)、Serializable(序列化)

    (2). 这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
    这里写图片描述

    1. 体会–“平衡”2字
      a. 事务的隔离级别,没有固定的完美的解决方案。可序列化,可以解决一切并发问题,但是效率太低,很少使用。
      b. 在实际工作,根据数据库存储数据的特点,来确定隔离级别。隔离级别最高和最低,都很少使用。一般,偏重性能,设置为:读提交;偏重安全,设置为:重复读
      c. mysql默认:重复读 oracle默认:读提交

hibernate设置隔离级别
在hibernate.cfg.xml中设置隔离级别:

  <!-- 事务隔离级别 -->
    <property name="hibernate.connection.isolation">2</property>
    * 其中数字的含义:
        1—Read uncommitted isolation:读未提交
        2—Read committed isolation:读提交
        4—Repeatable read isolation:重复读
        8—Serializable isolation:序列化

hibernate管理session的三种方式
Hibernate提供了三种管理Session对象的方法
(1) Session对象的生命周期与本地线程绑定
(2) Session对象的生命周期与JTA事务绑定
(3) Session对象的生命周期由Hibernate委托程序管理

hibernate事务控制api:

     sessionFactory.getCurrentSession()
 获取和当前线程相关联的Session,底层使用ThradLocal实现的

注意:
1)使用前,需要在核心配置文件hibernate.cfg.xml进行配置:

          <!-- 配置session绑定本地线程 -->
          <property name="hibernate.current_session_context_class">thread</property>

2)这是Hibernate官方提供的工具类,是不能够关闭这个session的。在线程销毁的时候,hibernate自动关闭这个session

3)害人的小细节
(1) 没有“thread”配置,使用getCurrentSession()方法,会报错。
(2) getCurrentSession()获取的session对象。在线程销毁时,自动关闭,千万不要手动调用session.close()方法,会报错。
例如:抽取工具类:
HibernateUtils:

public class HibernateUtils {

    private static final Configuration configuration;
    private static final SessionFactory sessionFactory;
    private static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();

    static {
        configuration = new Configuration();
        configuration.configure();
        sessionFactory = configuration.buildSessionFactory();
    }

    /**
     * @Title: HibernateUtils.java
     * @Description: TODO(获得session对象)
     * @return
     * @author jjizh
     * @date 2017年7月26日 下午8:07:59
     * @version V1.0
     */
    public static Session openSession(){
        return sessionFactory.openSession();
    }

    /**
     * @Title: HibernateUtils.java
     * @Description: TODO(利用ThradLocal,获得本地线程的Session对象,应用于事务中)
     * @return
     * @author jjizh
     * @date 2017年7月27日 下午5:09:11
     * @version V1.0
     */
    public static Session getThreadLocalSession(){
        Session session = threadLocal.get();
        if(session == null){
             session = openSession();
             threadLocal.set(session);
        }
        return session;
    }

    /**
     * @Title: HibernateUtils.java
     * @Description: TODO(关闭资源)
     * @author jjizh
     * @date 2017年7月27日 下午5:11:54
     * @version V1.0
     */
    public static void closeSession(){
        Session session = threadLocal.get();
        if(session != null){
            session.close();
            session = null;
        }
        threadLocal.remove();
    }

    /**
     * @Title: HibernateUtils.java
     * @Description: TODO(hibernate官方提供的从本地线程获取的session对象的方法,不需要关闭)
     * @return
     * @author jjizh
     * @date 2017年7月27日 下午5:50:06
     * @version V1.0
     */
    public static Session getCurrentSession(){
        return sessionFactory.getCurrentSession();

    }

}

hibernate的其他API

1)Query
Query的介绍
a. Query是一个面向对象的查询。其核心是HQL语句(区分大小写)。(又称为HQL查询)
b. HQL:hibernate query language,hibernate查询语言,语法类似SQL(案例中感受)。
查询步骤:
这里写图片描述
相关API:
a. session.createQuery(String hql): –创建Query对象
b. list(): –查询结果是多个
c. uniqueResult(): 查询结果是一个
d. setXxx(int position,Xxx val):–设置查询参数,Xxx标识类型,postion标识位置,从0开始
e. setFirstResult(int firstResult): –分页查询1,设置第一个结果的起始位置,从0开始
f. setMaxResults(int maxResults):–分页查询2,设置结果的总数
这里写图片描述

案例:

public class QueryDemo {

      @Test   //查询表中所有数据,多个数据
      public void test01(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           //User就是一个持久化类的类名
           Query query = session.createQuery("from User");
           List<User> list = query.list();
           for (User user : list) {
                 System.out.println(user);
           }

           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test    //查询表中指定条件数据,多个数据
      public void test02(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("from User where money = ?");
           query.setInteger(0, 2000);  //和JDBC中不同,下标从0开始
           List list = query.list();
           for (Object object : list) {
                 System.out.println((User)object);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test   //查询表中的单个数据
      public void test03(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("from User where username = ?");
           query.setString(0, "zs");
           User user = (User) query.uniqueResult();
           System.out.println(user);
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test     //分页查询
      public void test04(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("from User");
           query.setFirstResult(2);  //下标从0开始
           query.setMaxResults(2);   //每页显示多少条数据
           List<User> list = query.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test   //模糊查询
      public void test05(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("from User where username like ?");
           query.setString(0, "%z%");
           List<User> list = query.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
}

hibernate的其他API

2)Criteria(重要)
1. Criteria的介绍
a. Criteria:完全面向对象的查询。又称为QBC查询,query by criteria.
b. 不用管底层数据库实现和SQL语句的编写。

  1. API介绍
    a. session.createCriteria(Class persistentClass): –创建Criteria对象。persistentClass:需要查询对象的字节码对象名称。需要传入字节码文件,因为将查询的结果都直接封装成为一个对象了
    b. list(): –查询结果是多个
    c. uniqueResult(): –查询结果是一个
    e. setFirstResult(int firstResult):–分页查询1,设置第一个结果的起始位置,从0开始
    f. setMaxResults(int maxResults): –分页查询2,设置结果的总数
    g. add(Criterion criterion): –添加查询条件对象(支持添加多个,互相是and关系)

  2. 烦人的完全面向对象

    • Criteria查询,把查询条件封装成了“查询条件对象”
    • 查询条件对象,使用Criterion和Restrictions两个类来实现
      (1) Criterion: 称为查询条件对象,每个对象实例,代表一个查询条件。
      (2) Restrictions: 工具类,通过其静态(static)方法,创建Criterion对象

      * 其常见的Restrictions的静态方法
      方法                    说明
      Restrictions.eq            =
      Restrictions.allEq         利用Map来进行多个等于的限制
      Restrictions.gt            >(greater than)
      Restrictions.ge            >=(greater than or equal)
      Restrictions.lt            < (less than)
      Restrictions.le            <=(less than or equal)
      Restrictions.between    BETWEEN
      Restrictions.like        LIKE
      Restrictions.in            in
      Restrictions.and        and
      Restrictions.or            or
      Restrictions.sqlRestriction    用SQL限定查询
      
  3. Criteria的使用
    a. 通过Session对象,获取Criteria对象。session.createCriteria(Class persistentClass)
    b. 通过Restrictions的静态方法创建Criterion对象。每个Criterion对象实例代表一个查询条件
    c. 向Criteria对象添加Criterion查询条件。add()方法
    d. 执行list()或者uniqueResult()获得结果。

案例:

      @Test   //查询持久类在数据库中的所有对象列表
      public void test06(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Criteria criteria = session.createCriteria(User.class);
           List<User> list = criteria.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test    //添加查询的限制条件
      public void test07(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Criteria criteria = session.createCriteria(User.class);
           SimpleExpression gt = Restrictions.gt("id", 2);
           //上述这一句等同于:
           //Criterion gt = Restrictions.gt("id", 2);
           criteria.add(gt);
           List<User> list = criteria.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test   //添加查询的限制条件
      public void test08(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Criteria criteria = session.createCriteria(User.class);
           Criterion gt = Restrictions.eq("username", "zs");
           criteria.add(gt);
           User user = (User) criteria.uniqueResult();
           System.out.println(user);
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test   //分页查询
      public void test09(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Criteria criteria = session.createCriteria(User.class);
           criteria.setFirstResult(2);
           criteria.setMaxResults(2);
           List<User> list = criteria.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test   //合并多个查询条件的时候
      public void test10(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Criteria criteria = session.createCriteria(User.class);
           Criterion lhs = Restrictions.gt("id", 4);
           Criterion rhs = Restrictions.lt("id", 2);
           Criterion or = Restrictions.or(lhs, rhs);
           criteria.add(or);
           List<User> list = criteria.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }

hibernate的其他API

3)SQLQuery

  1. SQLQuery介绍
    a. 使用原始的SQL语句进行查询。
    b. SQLQuery不会直接封装到实体对象。

  2. API介绍
    a. session.createSQLQuery(String sql):–通过sql语句,创建对象
    b. list(): –查询结果是多个
    c. uniqueResult(): –查询结果是一个
    d. addEntity(Class entityType): –把查询结果封装成entityType类的对象。

  3. SQLQuery介绍
    a. 编写SQL语句。
    b. Session对象通过SQL语句,创建SQLQuery对象。session.createSQLQuery(String sql)
    c. 执行SQLQuery的list()或uniqueResult()方法获取结果。结果形式是:Object[]。
    d. 不会直接封装到实体对象,需要手动调用addEntity(Class entityType)方法。

案例:

      @Test
      public void test11(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createSQLQuery("select * from transferCustomer");
           List<Object[]> list = query.list(); //查询出来是以数组的形式存在的
           for (Object[] object : list) {
                 System.out.println(Arrays.toString(object));
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
      @Test
      public void test12(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           SQLQuery query = session.createSQLQuery("select * from transferCustomer");
           query.addEntity(User.class);  //将查询出来的结果封装成为对象
           List<User> list = query.list();
           for (User user : list) {
                 System.out.println(user);
           }
           transaction.commit();
           HibernateUtils.closeSession();
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值