hibernate day02

本节内容

1.实体类的编写规则

2. hibernate主键生成策略

3 实体类操作

4 hibernate的一级缓存

5 hibernate的事务操作

6 hibernate其他的api(查询)

一、实体类编写规则

持久化类:hibernate是一个持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久性的存储到关系型数据库中。那么什么是持久化类:持久化类指的是一个java类与数据库表建立了映射关系,那么这个类称为持久化类。

持久化类的编写规则:

1.持久化类需要提供无参数的构造方法。因为hibernate底层需要使用反射生成类的实例

2.持久类中的属性需要私有化,对私有属性提供get与set方法。因为在hibernate底层会将查询到的数据进行封装

3.持久化类的属性尽量使用包装类的类型。因为基本类型和包装类型的默认值不同,包装类的类型语义更清晰而基本数据类型不容易描述。

4.持久化类要有一个唯一标识OID与表的主键对应

5.持久化类尽量不要使用final修饰.

二、hibernate主键生成策略

主键:

 自然主键:把具有业务含义的字段设置为主键,称为自然主键:比如用户信息表中有一个用户姓名,使用姓名作为主键。

 代理主键:把不具备业务含义的字段作为主键,称为代理主键,例如id,通常为整数类型。

 hibernate要求实体类里面有一个属性作为唯一值,对应表主键,主键可以不同生成策略

hibernate提供了几个内置的主键生成策略

三、实体类操作

对实体类进行CRUD操作

添加操作:

 使用session中的save方法:

//        添加操作
         Shop shop = new Shop ();
         shop.setS_name ("面条");
         shop.setS_describe ("意大利面");
         shop.setS_stock (123);
         shop.setS_price (10.0);

         session.save (shop);

根据id查询:

//         第一个参数:实体类的class  第二个参数:id值
         Shop shop1 = session.get (Shop.class,1);

修改操作:

1.首先根据id查询,然后修改

//         修改操作
//         第一个参数:实体类的class  第二个参数:id值
         Shop shop1 = session.get (Shop.class,1);
//         修改查询到的数据
         shop1.setS_name ("披萨");
//         进行修改:到shop1对象中找到id根据id进行修改
         session.update (shop1);

删除操作;

先查询后删除:

//      删除操作
        Shop shop2 = session.get (Shop.class,1);

         session.delete (shop2);

实体类对象状态:

hibernate为了更好的来管理持久化类,特将持久化类分为了三中状态:瞬时态、持久态、脱管态。

1.瞬时态:

瞬时态称为:临时态或者自由态,瞬时态的实例是由new命令创建的,开辟内存空间的对象,不存在主键值,没有与hibernate Session关联,在数据库也没有记录,与数据库没有关系。

2.持久态

持久态的对象存在id主键值,并且相关联的session没有关闭,在数据库中有对应的记录,没条记录只对应唯一的持久化对象,持久态对象是在事务没有提交之前变成的持久态。

3.脱管态

托管态也称为离线态或者游离态,当某个持久化状态的实例与session的关联被关闭时,就变成了托管态,托管态的对象存在id主键值,并与数据库的数据存在关联,但是没有了session关联。发生改变hibernate不能检测。

 四、Hibernate的一级缓存

1.什么是缓存

  缓存是计算机领域非常通用的一个概念。它介于应用程序与永久性数据存储源(如磁盘文件或者数据库)之间,其作用就是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是从数据源存储中数据拷贝。缓存的物理介质通常是内存。

hibernate的缓存分为一级缓存和二级缓存,hibernate的这两级缓存都位于持久化层,存储的都是数据库的备份。其中一级缓存为Hibernate的内置缓存,不能被卸载。

2.什么是hibernate的一级缓存

hibernate的一级缓存就是指Session缓存,Session缓存就是一块内存空间,用来存放相互管理的Java对象,在使用hibernate查询对象的时候,会首先使用对象属性的OID 值在,hibernate的一级缓存中进行查找,如果找到匹配的IOD值对象,就直接将该对象返回,不会再查询数据库,如果没有找到相同的OID值的对象,则会去数据库查询,当从数据库中查到后,将该数据信息保存到一级缓存中。hibernate的一级缓存的作用高就是为了减少对数据库的访问次数。

hibernate一级缓存的特点:

  当应用程序调用Session接口中的save、update、saveOrUpdae时,如果缓存中没有相应的对象,Hibernate就会 自动把数据库查询的相应的对象保存到一级缓存中。

当调用Session接口的load、get方法,一级Query中的list、iterator方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询对应的对象,并保存到一级缓存中。

当调用session中的close方法时,Session缓存会被清空。

测试一级缓存:

//    测试一级缓存
    @Test
    public void test1(){
        //1.加载配置文件
        //自动在src下查找hibernate.cfg.xml文件
        Configuration configuration = new Configuration ().configure ();


        //2.创建一个SessionFactory
        SessionFactory sessionFactory = configuration.buildSessionFactory ();

        //3.创建一个session对象  session对象类似于connection

        Session session = sessionFactory.openSession ();

        //4.开启事务
        Transaction transaction = session.beginTransaction ();
//       马上发送一条sql语句查询数据库,并将数据添加到一级缓存
        Shop shop1 = session.get (Shop.class,1);
//没有发送SQL语句,从一级缓存中获取
        System.out.println (shop1);
        Shop shop2 = session.get (Shop.class,1);
        System.out.println (shop2);
//  缓存缓存的是对象的地址
        System.out.println (shop1==shop2);

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


    }

运行结果:

Hibernate: 
    select
        shop0_.s_id as s_id1_0_0_,
        shop0_.s_name as s_name2_0_0_,
        shop0_.s_describe as s_descri3_0_0_,
        shop0_.s_stock as s_stock4_0_0_,
        shop0_.s_price as s_price5_0_0_ 
    from
        shop shop0_ 
    where
        shop0_.s_id=?
com.hibernate.pojo.Shop@72fe8a4f
com.hibernate.pojo.Shop@72fe8a4f
true

从以上代码的运行结果上可以看出,执行第一次查询时,发送了一条sql语句,当执行第2次查询时,发现没有发送sql,直返返回了对象。第二次直接从缓存中获取的数据。

我们知道hibernate的持久态对象可以自动更新数据库,其实就是依赖于一级缓存,那么一级缓存为什么可以去更新数据库,是因为一级缓存的一块特殊区域就是快照区

一级缓存的内部结构(快照区)

hibernate向一级缓存中存放数据时,同时会复制一份数据放入到Hibernate快照区中,当使用 commit方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照区中的对象是否一致

五、hibernate的事务操作

Hibernate是对JDBC的轻量级封装,其主要功能是操作数据库,在操作数据库过程中,经常会遇到事务处理的问题,那么我们接下来就学习Hibernate的事务管理。

1.什么是事务

事务:由一条或者多条操作数据库的SQL语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库,如果有一项操作没有完成,则整个事务会被回滚。

总结:一条或者一组sql语句要么全部执行成功,要么全部失败。

2.事务的四大特性(ACID)

原子性:(Atomic)表示将事务中所做的操作捆绑成一个不可分割的单元,即事务所进行的数据修改等操作。要么全部执行,要么全部不执行。

一致性:(Consistency):表示事务完成时,必须使所有的数据都保持一致状态。

隔离性:(Isolation):指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性:(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。提交后的其他操作或者故障不会对其有任何影响。

3.事务的并发问题:

在实际使用事务的过程中,数据库数据要被多个用户所共同访问的,在多个事务同时使用相同的数据时,可能会发生并发的问题:

脏读:一个事务读取到另一个事务未提交的数据。

不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在同一个事务中的多次查询结果不一致。

虚读/幻读:一个事务读取到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次多次查询结果不一致。

4.事务的隔离级别

为了避免事务并发问题的发生,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。

读未提交:(Read Uncommitted  1)一个事务在执行过程中,即可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此数据。防止丢失更新

已提交读:(Read Committed 2)一个事务在执行过程中,即可以访问其他事务成功提交,新插入的数据,又可以访问成功修改的数据,读取数据的事务允许其他事务继续访问该数据,但是未提交事务将会禁止其他事务访问该行数据,有效防止脏读

可重复读:(Repeatable Read 4)一个事务在执行过程中,可以访问其他事务成功提交的数据,但是不可以访问成功修改的数据,读数据的事务将会禁止写事务(允许读),写事务则禁止任何其他事务,防止不可重复读和脏读

序列化/串行化:(Serializable 8)提供严格的事务隔离。它要求事务序列化执行。事务只能一个接一个的执行,但不能并发执行。次隔离级别可有效防止脏读,不可重复读,或者幻读。

事务隔离级别,是由数据库提供的,并不是所有数据库都支持四种隔离级别。

在使用数据库时候,隔离级别越高,安全性越高,性能越低。实际开发中,不会选择最高或者最低隔离级别。一般选择默认的级别Oracle(Read Committed) mysql(Repeatable Read)

 

5.hibernate中的事务管理

在Hibernate中可以通过代码来操作管理事务,如通过:Transaction tx = session.beginTransaction 开启一个事务,持久化操作后,通过 tx.commit 提交事务,如果事务出现异常,通过 tx.rollback 操作来撤销事务(事务回滚)

除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别,在hibernate.cfg.xml文件中的<session-factory>标签中进行。

    <!--设置事务的隔离级别-->
        <property name="hibernate.connection.isolation">4</property>

设置事务的隔离级别,那么我们的事务一般不使用在dao层,而是使用在service层中,在一个service中调用多个dao中的方法来实现一个逻辑操作。

那么我们需要保证的是在service中开启事务时,使用的Session对象和Dao中多个操作使用的是同一个session对象。

有2种方式可以实现:

      1 可以在业务层获取到Session,并将Session作为参数传递给Dao

      2可以使用 ThreadLocal 将业务层获取到的,Session绑定到当前线程中,然后在dao层获取Session的时候,从当前线程中获取。

一般我们都使用第二种,具体的实现不需要我们来完成,hibernate内部已经做好了,我们只需要完成一段配置即可。

Hibernate5自身提供了3种管理Session对象的方法。

Session对象的生命周期与本地线程绑定。

Session对象的生命周期与JTA事务绑定

Hibernate委托程序管理 Session对象的生命周期。

在Hibernate的配置文件中,通过hibernate.current_session_context_class来指定Session的管理方式:

thread:Session对象的生命周期与本地线程绑定

jta:session对象的生命周期与JTA事务绑定

managed:Hibernate委托程序管理Session对象的生命周期。

在hibernate中配置:

  

    <property name="hibernate.current_session_context_class">thread</property>

hibernate提供sessionFactory.getCurrentSession() 创建一个 session和ThreadLocal绑定方法。

package com.hibernate.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

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

      }


      //  获取当前线程绑定的会话
      public static Session getCurrentSession(){


          return sessionFactory.getCurrentSession ();
      }

}

hibernate中提供的这个与线程绑定的Session可以不用关闭,当线程执行结束后,就会自动关闭。

六、Hibernate的其他API(查询)

 1.Query

   query代表面向对象的一个Hibernate查询操作,在hibernate中通常使用createQuery方法接收一个HQL语句,然后调用Query的list方法或者uniqueResult 方法执行查询。

HQL:Hibernate Query Language 缩写 语法很像sql,但是他是完全面向对象的。sql操作的是表,HQL操作的是实体类。

Hibernate中使用Query对象的步骤:

 1.获取Hibernate的session对象

 2.编写HQL语句

3.调用createQuery创建查询对象

4.如果HQL语句包含参数,则使用Query的setXXX方法设置参数

5.调用Query的list方法或者uniqueResult 方法执行查询

Session session = HibernateUtil.getCurrentSession ();
     Transaction transaction =  session.beginTransaction ();
     //查询所有
//    Query query = session.createQuery ("from Shop");

//   List<Shop> list =  query.list ();
//
//   list.forEach ( shop ->{
//       System.out.println (shop.getS_name ());
//   });
//     //条件查询
//     Query query1 = session.createQuery ("from Shop where s_id=?0");
//
//     query1.setParameter (0,1);

        //分页查询
        Query query = session.createQuery ("from Shop ");

        query.setFirstResult (1);
        query.setMaxResults (10);





    List<Shop> list = query.list ();
           list.forEach ( shop ->{
       System.out.println (shop.getS_name ());
   });

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


    }

 2.Criteria

Criteria是一个完全面向对象,可扩展的条件查询API,它不用考虑数据库底层的实现,以及SQL如何编写,它是Hibernate框架的核心查询对象,Criteria查询又称为“QBC”查询,他是Hibernate的另一种对象检索方式。由于它的API比较多,使用比较繁琐,所以在我们的开发中很少有人使用。

使用步骤;

1.创建查询:CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();

2.设置查询语句信息 :criteriaBuilder.createQuery

3.定义查询的From子句中能出现的类型,也可以用root.get()获取具体的某个属性:

Root root = criteriaQuery.from (Shop.class);

4.设置条件
  criteriaQuery.where
5.创建Query对象并获取结果集list
Query query =  session.createQuery (criteriaQuery);
List<Shop> list = query.list ();

  @Test
    public void test4(){
        Session session = HibernateUtil.getCurrentSession ();
        Transaction transaction =  session.beginTransaction ();
        CriteriaBuilder criteriaBuilder =session.getCriteriaBuilder ();
        CriteriaQuery criteriaQuery = criteriaBuilder.createQuery (Shop.class);

        Root root = criteriaQuery.from (Shop.class);
        criteriaQuery.select (root);

        Path<Integer> path = root.get("s_id");
        Path<String> path1 =root.get ("s_name");
        //多条件查询
        Predicate predicate =criteriaBuilder.or (criteriaBuilder.equal (path,1),criteriaBuilder.equal (path1,"面条"));
        Predicate predicate1 =criteriaBuilder.or (criteriaBuilder.equal (path,2));

  // criteriaQuery.where (criteriaBuilder.equal (root.get ("s_id"),1),criteriaBuilder.equal (root.get ("s_name"),"面条"));
     criteriaQuery.where (predicate,predicate1);

     Query query =  session.createQuery (criteriaQuery);
     List<Shop> list = query.list ();

      list.forEach (shop ->{
          System.out.println (shop.getS_price ());
      });

    }

3.SQLQuery

  SQLQuery这个就比较简单了,这个借口用于接收一个sql语句进行查询,然后调用list或者uniqueResult方法进行查询,但是sql语句不会直接封装到实体对象中,需要我们手动写代码才可以封装到实体中

    @Test
    public void test5(){
        Session session = HibernateUtil.getCurrentSession ();
        Transaction transaction =  session.beginTransaction ();
        //全查询
//        String sql = "select * from shop";
//        NativeQuery nativeQuery =session.createNativeQuery (sql,Shop.class);
//
//       List<Shop> list =  nativeQuery.getResultList ();
        //分页查询
        String sql = "select * from shop";
        NativeQuery nativeQuery = session.createNativeQuery (sql,Shop.class).setFirstResult (1)
                .setMaxResults (10);
         List<Shop> list =  nativeQuery.getResultList ();

        
        transaction.commit ();
         list.forEach (shop ->{
             System.out.println (shop.getS_name ());
         });


    }
原文地址:https://www.cnblogs.com/wuzhilong/p/9953359.html