04hibernate

1. 概述

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任

1.1 CRM

> ```
> 客户关系管理是指企业为提高核心竞争力,利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和服务的过程。其最终目标是吸引新客户、保留老客户以及将已有客户转为忠实客户,增加市场。 
> ```

1.2 ORM

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。
Object Relational Mapping,对象关系映射,将对象与关系型数据库的表建立映射关系,操作对象就可以操作表。

2.配置文件和XML表头

xml文件头信息可以到D:\JavaWeb\TestHibernate\lib\hibernate-core-5.0.7.Final.jar!\org\hibernate\hibernate-configuration-3.0.dtd下找到,即进入core包里面的org\hibernate/就有

2.1 映射文件 Customer.hbm.xml

  • 类中的属性名和表中的字段名如果一致,column可以省略

【id标签】:

  • 属性:
    • name、column、length、type

【property标签】

  • 属性:
    • name、column、length、type、not-null,uinque
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <!--建立类与表的映射-->
    <class name="com.gxufe.hibernate.demo01.Customer" table="cst_customer">
        <!--建立类中的属性与表中的主键对应-->
        <id name="cust_id" column="cust_id">
            <generator class="native"></generator><!--生成策略-->
        </id>
        <property name="cust_name" column="cust_name"/>
        <property name="cust_source" column="cust_source"/>
        <property name="cust_industry" column="cust_industry"/>
        <property name="cust_level" column="cust_level"/>
        <property name="cust_phone" column="cust_phone"/>
        <property name="cust_mobile" column="cust_mobile"/>
    </class>
</hibernate-mapping>

2.2 配置文件 hibernate.cfg.xml

  • 方言

    方言对应不同的数据库使用不同的方言,可以去下载下来的hibernate文件的`hibernate-release-5.0.7.Final\project\etc`目录下的`hibernate.properties`文件里面找对应的方言
    
  • 可选的配置

    • 显示SQL :hibernate.show_sql
    • 格式化sql :hibernate.format_sql
    • 自动建表 : hibernate.hbm2ddl.auto
      • none :不使用hibernate的自动建表
      • create :如果数据库已经有表,删除原有的表,重新创建,如果没有表,新建表(测试)
      • create-drop :如果数据库中有表了,删除原有表,执行操作,删除这个表,如果没有表,新建一个表,使用完了也删除改表(测试)
      • update :如果数据库中有表,使用原有表,如果没有表,创建新表(可以更新表结构)
      • validate :如果没有表,不会创建表,只会使用数据库中存在的表(校验映射和表结构)
  • 映射文件的引入

  • 也可以在cfg.xml添加c3p0的配置(了接即可)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate?serverTimezone=UTC</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">162263</property>

        <!--=========可选配置======-->
         <!--打印sql语句-->
        <property name="hibernate.show_sql">true</property>
        <!--格式化sql-->
        <property name="hibernate.format_sql">true</property>
        <!--自动创建表-->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!--设置事务的隔离级别 4 是mysql的默认级别-->
        <property name="hibernate.connection.isolation">4</property>
        <!--配置当前线程绑定的Session-->
        <property name="hibernate.current_session_context_class">thread</property>

        <!--========b必须配置============-->
        <!--配置hibernate的方言-->
        <!--<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>-->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

        <!--配置映射文件-->
        <mapping resource="com/gxufe/hibernate/demo01/Customer.hbm.xml"/>


    </session-factory>
</hibernate-configuration>

2.3 Hibernate加载文件工具类

public class HibernateUtils {
    public static final Configuration cfg ;
    public static final SessionFactory sf;

    static{
        cfg = new Configuration().configure();
        sf = cfg.buildSessionFactory();
    }

    public static Session openSession(){
        return sf.openSession();
    }

    public static Session getCurrentSession(){
        //线程绑定
        return sf.getCurrentSession();
    }

}

3. Hibernate核心API(对象)

3.1 Configuration

Hibernate的配置对象

  • 作用
  • 加载核心配置文件

    • hibernate.porperties

      configuration cfg = new Configuration();

    • hibernate.cfg.xml
      Configuration configuration = new Configuration.configure();
      
  • 加载映射文件

3.2 SessionFactory

Session工厂:SessionFactory内部维护了hibernate的连接池和Hibernate的二级缓存(被redis替换了)。是线程安全的对象,一个项目创建一个对象即可

  • 抽取工具类
public class HibernateUtils {
    public static final Configuration cfg ;
    public static final SessionFactory sf;

    static{
        cfg = new Configuration().configure();
        sf = cfg.buildSessionFactory();
    }

    public static Session openSession(){
        return sf.openSession();
    }

}

3.3 Session

类似JDBC中的Connection对象

Session代表的是Hibernate与数据库的连接对象,不是线程安全的。所以不能设置成全局变量,必须是局部变量,内部维护了一级缓存,是与数据库交互的桥梁

  • Session中的常用API
  • 保存
    • Serializable save(Object obj) -->返回的是可序列化的id
  • 查询方法(get()和load()的区别)
    • T get(Class c, Serializable id);

    • T load(Class c,Serializable id);

      @Test
          //查询------->get方法和load方法的区别
          public void demo02(){
              Session session = HibernateUtils.openSession();
              Transaction transaction = session.beginTransaction();
      
              /**
               * get方法
               *   * 采用的是立即加载,执行到这行代码的时候,就会马上发送Sql语句去查询
               *   * 查询返回是真实对象本身
               *   * 查询一个找不到的对象的时候,返回null
               *
               * load方法
               *    * 采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句
               *    *查询返回的是代理对象。 javassist-3.18.1.GA.jar,利用javassist技术产生的代理
               *    * 查询一个找不到的对象的时候,返回ObjectNotFoundException
               */
      
      
              //使用get的方法
              //因为是Long类型,所以后面需要加一个L
            /*  Customer customer = session.get(Customer.class, 1l);
            /*  Customer customer = session.get(Customer.class, 100l);//数据库没这个数据
              System.out.println(customer);*/
      
              //使用load()方法查询
              Customer customer = session.load(Customer.class, 2l);
             /* System.out.println(customer);//使用了除id以为的属性才发sql语句*/
              System.out.println(customer.getCust_id());//没有发送sql语句,直接从上面的load方法参数获得了id
      
              transaction.commit();
              session.close();
          }
      
  • 修改方法
    • void update(Object obj);//无返回值的方法

      public void demo03(){
              Session session = HibernateUtils.openSession();
              Transaction transaction = session.beginTransaction();
      
              //1.直接创建对象修改,数据库的其他属性没了,没有设置的覆盖为null,就是没有来源,
              /*Customer customer = new Customer();
              customer.setCust_id(1l);
              customer.setCust_name("马尔扎哈");
              session.update(customer);*/
      
              //2.(建议)先查询再修改,有来源,来源于查询得到的这行的其他属性,Bean里面的get的方法就有值
              Customer customer = session.get(Customer.class, 1l);//修改的必须是查询放回的那个对象否则就报错
              customer.setCust_name("古力娜扎");
              session.update(customer);
      
              transaction.commit();
              session.close();
          }
      
  • 删除方法

    • void delete(Object obj)

        //删除方法
          public void delete(){
              Session session = HibernateUtils.openSession();
              Transaction transaction = session.beginTransaction();
              //1.直接删除对象
              /*Customer customer = new Customer();
              customer.setCust_id(1l);//删除第一条
              session.delete(customer);*/
      
              //2.先查询再删除(推荐) ---级联删除 --》的前提就是先查询再删除
              Customer customer = session.get(Customer.class, 2l);
              session.delete(customer);
      
              transaction.commit();
              session.close();
          }
      
  • 保存或更新

      //保存或更新
        public void demo05(){
            Session session = HibernateUtils.openSession();
            Transaction transaction = session.beginTransaction();
    
            //保存
            Customer customer = new Customer();
            /*customer.setCust_name("汪大东");
            session.saveOrUpdate(customer);*/
    
            //修改
            customer.setCust_id(3l);
            customer.setCust_name("李大而已");
            session.saveOrUpdate(customer);
    
    
            transaction.commit();
            session.close();
        }
    
  • 查询所有

     //查询所有
        public  void demo06(){
            Session session = HibernateUtils.openSession();
            Transaction transaction = session.beginTransaction();
    
            //接受HQL:Hibernate Query Language 面向对象的查询语言
           /* Query query = session.createQuery("from Customer");
            List<Customer> list = query.list();
            for (Customer customer : list){
                System.out.println(customer);
            }*/
    
            //接受Sql语句
            SQLQuery sql = session.createSQLQuery("select * from cst_customer");
            List<Object[]> list1 = sql.list();
            for (Object[] objects: list1) {
                System.out.println(Arrays.toString(objects));
            }
            transaction.commit();
            session.close();
        }
    

3.4 Transaction:事务对象

hibernate中管理事务的对象

  • commit()
  • Rollback()

4. 持久化

4.1 概述

  • 什么是持久化类

持久化:将内存中的一个个对象持久化到数据库中过程。

持久化类:一个java对象与数据库的表建立了映射关系,那么这个类就hibernate中称为持久化类。(持久化类 =java类 + 映射文件)

  • 编写规则
  • 对持久化类提供一个无参数的构造方法 :Hibernate底层需要使用反射生成实例。

  • 对属性需要私有,对私有属性提供pblic的get和set方法 :Hibernate中获取,设置对象的值

  • 对持久化类提供一个唯一的标识oid与数据库主键对应 :java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,hibernate中通过持久化类的OID属性区分是否是同一个对象

  • 持久化类中的属性尽量使用使用包装类型(Integer Long String Double),因为基本数据类型默认是0,那么0就会有很多的歧义。包装类型类型默认是null,不会容易产生歧义

  • 持久化类不要使用final进行修饰,延迟加载本身就是hibernate一个优化的手段,返回的是一个代理对象(javassistkey对没有实现接口的类产生一个代理–使用了非常底层的字节码增强技术,继承这个类进行代理),如果不能被继承,不能产生代理对象,延迟加载就失效, load方法和get方法一致了。

5. 主键的生成策略

5.1主键的分类

5.1.1 自然主键
  • 主键的本身就是表中的一个字段(实体中的一个具体属性)
    • 创建一个人员表,人员都会有提供身份证号(唯一的不可重复),使用身份证作为主键,这种主键称为自然主键。
5.1.2 代理主键
  • 代理主键:主键的本身不是表中的必须的一个字段(不是实体中的某个具体的属性)
    • 创建一人员表,没有使用人员中的身份证号码,用了与这个表不想关的字段id(pno)。这种主键称为是代理主键。
  • 在实际开发中,尽量使用代理主键。
    • 一旦自然主键参与业务逻辑中,后期可能需要修改源代码。
    • 好的程序设计满足ocp原则,对程序的扩展是open的,对修改源码是close的
5.1.3 主键的生成策略

在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置(UUIDUtils)。在Hibernate中为了减少程序的编写,提供了很多种的主键的生成策略。

  • increment:hibernate中提供的自动增长的机制,使用short、int、long类型的主键,在集群下不要使用,在单线程中的程序中使用。
    • 首先发送一条数据,select max(id) from 表,然后让id+1作为下一条记录的主键。
  • identity : 使用short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增长的机制的数据库(MySql、MSSQL),但是Oracle没有自动增长。
  • squence : 适用short、int、long类型的主键,采用的是序列的方式。(Oracle支持序列),像mysql就不支持序列
  • uuid : 使用于字符串类型的主键,使用Hibernate中随机的方式生成的字符串主键
  • native : 本地策略,可以在identuty和sequence之间进行自动切换
  • assigned : Hibernate放弃外键的管理,需要手动编写程序或者用户自己设置
  • foreign : 外部的。一对一的一种关系映射的情况下使用(了解)

6. 持久化的三种状态

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

6.1 瞬时态(transient)

  • 这种对象没有唯一的标识OID,没有被session管理,称为瞬时态对象

6.2 持久态(persistent)

  • 这种对象有唯一标识的OID,被session管理,称为是持久态对象

6.3 脱管态(游离)(detached)

  • 这种对象有唯一标识的OID,没有被session管理,称为托管态对象

6.4 区分

  • 持久化类的持久态对象,可以自动更新数据库。
   @Test
    //三种状态的区分
    public void demo01(){
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();

        //1.刚new出来,瞬时态对象,没有唯一标识OID,就是数据库还没有ID ,没有被session管理
        Customer customer = new Customer();
        customer.setCust_name("马尔扎哈");

        Serializable id = session.save(customer);//2.变成持久态,数据库中已经有了唯一标识的OID,被session管理
        session.get(Customer.class,id);//操作持久化对象

        //把资源关闭了
        transaction.commit();
        session.close();

        System.out.println("客户名称:"+customer.getCust_name());//3.变成脱管态对象:有唯一标识的OID,没有被session管理
    }

6.5 持久化状态的装换

  • 瞬时态对象
  • 获得:

    • Customer customer = new Customer();
      
  • 状态转换

    • 瞬时–》持久
      • save(obj)
      • saveOrUpdate
    • 瞬时–》托管
      • customer.setCust_id(1l),有唯一标识,但是没有被session管
  • 持久化对象
  • 获得
    • get()、load()、find()、iterate()
    • session.get(Customer.class, 1l );
  • 状态转换
    • 持久–>瞬时
      • delete();
    • 持久–托管
      • session.close(); clear() 、 evict()
  • 脱管态对象
  • 获得

    • Customer customer = new Customer();
      customer.setCust_id(1);//有唯一标识,没有被session管
      
  • 状态转换

    • 托管—》持久

      • update()、saveOrUpdate();
    • 托管—》瞬时

      • customer.setCust_id(null);
        

6.6 持久态对象自动更新

原理:依赖hibernate的一级缓存

   @Test
    //持久态对象自动更新数据库
    public void demo02(){
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();

        Customer customer = session.get(Customer.class, 1l);//已经是一个持久化对象
        customer.setCust_name("古力娜扎");//修改
        //session.update(customer);//不是持久化对象需要执行该语句,已经是持久化对象回自动发sql更新语句

        //把资源关闭了
        transaction.commit();
        session.close();
    }
}

7. Hibernate的一级缓存

7.1 缓存的概述

  • 什么是缓存

    缓存是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。

7.2 Hibernate的一级缓存

Hibernate框架中提供了优化手段:缓存和抓取策略。Hibernate中提供了二种缓存机制;一级缓存和二级缓存。

  • Hibernate的一级缓存

称为是Session级别的缓存,一级缓存的生命周期与Session一致(一级缓存是由Session中的一系列的java集合构成)。一级缓存是**自带的不可卸载。**

  • Hibernate的二级缓存是SessionFactory级别的缓存,需要配置的缓存

7.3 一级缓存的存在测试

 @Test
    //证明一级换出的存在
    public void demo01(){
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();


        //连续发送两次查询数据,只会发送一条sql语句
       /* Customer customer1 = session.get(Customer.class,1l);//发送
        System.out.println(customer1);

        Customer customer2 = session.get(Customer.class,1l);//不发送
        System.out.println(customer2);

        //查看对象是否相等
        System.out.println(customer1 == customer2);//true*/

       Customer customer = new Customer();
       customer.setCust_name("马大哈");//发送一条sql语句
        Serializable id = session.save(customer);
        Customer customer1 = session.get(Customer.class, id);
        System.out.println(customer1.getCust_name());//不发送sql语句

        //把资源关闭了
        transaction.commit();
        session.close();

    }

7.4 一级缓存的内部结构

  • 一级缓存的特殊区域:快照区

比较缓存区和快照区的数据

一致:不更新数据库

不一致:更新数据库

@Test
//一级缓存的快照区
public void demo02() {
    Session session = HibernateUtils.openSession();
    Transaction transaction = session.beginTransaction();

    //发送sql语句查询,同时放入到一级缓存中
    Customer customer = session.get(Customer.class,1l);
    customer.setCust_name("古力娜扎!!");

    //把资源关闭了
    transaction.commit();
    session.close();

}

8. 事务管理

8.1 什么是事务

  • 事务:事务是指的逻辑上的一组操作,组成这组操作的各个逻辑单元要么成功,要么失败

8.2 事务的特性

  • 原子性 :代表事务不可分割。
  • 一致性 : 代表事务执行的前后,数据的完整性保持一致
  • 隔离性 : 代表一个事务执行过程中,不受到其他事务的干扰
  • 持久性 : 代表事务执行完成后,数据就持久到数据库中。
  • 如果不考虑隔离性,就会引发安全性问题
  • 读问题:
    • 脏读 一个事务读到另一个事务未提交的事务
    • 不可重复 : 一个事务读到了另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致
    • 虚读 : 一个事务读到另一个事务已经提交的insert数据,导致前一个事务多次查询结果不一致。
  • 写问题(引发两类丢失更新)
    *

8.2 读问题的解决(隔离级别)

  • 设置事务的隔离级别
  • Read uncommited : 以上问题都不解决
  • Read commited : 解决脏读,但是不可重复读和虚读都有可能发生
  • Repeatable read : 解决脏读和不可重复读,但是虚读有可能发生
  • Serializable : 解决所有读问题

8.3 Hibernate设置隔离级别

  • 在application.cfg.xml里面配置隔离级别
     <property name="hibernate.connection.isolation">4</property>
  • 隔离级别分别对应
    • Read uncommited : 1
    • Read commited : 2
    • Repeatable read : 4
    • Serializable : 8

8.4 事务需要加载业务层上

银行转账

  • service中封装业务逻辑操作,service里面有个方法aa()用到了到层的dao1()和dao2()两个方法,但是dao1里面的连接(Hibernate称为Session,JDBC称为Connection)和dao2用的不是用一个连接。
  • 必须保证连接对象 是同一个
  • JDBC中的处理方法
    • 向下传递 DBUtils使用的方法
    • 使用ThreadLocal对象(绑定线程对象)
      • 将这个连接绑定到当前线程中
      • 在Dao的方法中,通过当前的线程得到连接对象

8.5 Hibernate绑定线程

  • Hibernate 框架内部已经绑定好了一个ThreadLocal

    • 在SessionFactory中提供了绑定线程的一个方法getCurrentSession();

    • 通过一个配置完成

        <!--配置当前线程绑定的Session-->
              <property name="hibernate.current_session_context_class">thread</property>
      

9. * Hibernate的其他API

9.1 Query(HQL)

  • Query接口用于接收HQL。查询多个对象

  • HQL:Hibernate Query Language :Hibernate查询语言

  • Query的简单使用

  @Test
    //Query
    public void demo01(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //通过Session获得Query接口
        //查询所有
       // String hql = "from Customer";
        //条件查询
       // String hql = "from Customer where cust_name like ?";
        //分页查询
        String hql = "from Customer ";
        Query query = session.createQuery(hql);
        //设置条件问号参数
        //query.setParameter(0,"李%");//参数从0开始的

        //设置分页
        query.setFirstResult(0); //从第几个开始,默认从o开始
        query.setMaxResults(3); //返回最多少个数据

        List<Customer> list = query.list();
        for (Customer customer: list) {
            System.out.println(customer);
        }
        transaction.commit();
    }

9.2 Criteria (QBC)

QBC(Query By Criteria) : 条件查询

​ 更加面向对象的一种查询方式

@Test
//Criteria(标准)
public void demo02(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();

    //通过session获得Criteria的对象
    /*Criteria criteria = session.createCriteria(Customer.class);//查询所有对象
    List<Customer> list = criteria.list();*/

    //条件查询
    Criteria criteria = session.createCriteria(Customer.class);
    //Restrictions(限制条件) add的方法是添加条件限制
    //criteria.add(Restrictions.like("cust_name","李%"));

    //分页
    criteria.setFirstResult(0);
    criteria.setMaxResults(3);

    List<Customer> list = criteria.list();
    for (Customer customer : list) {
        System.out.println(customer);
    }

    transaction.commit();
}

9.3 SQLQuery

用于接收SQL,查询条件特别复杂情况下使用

10 Hibernate的一对多关联映射

10.1 一对多关系

10.1.1 一对多的建表原则:
  • 的一方创建外键指向的一方

  • 创建对象需要注意
    • 在一的一方创建集合来存储多(Hibernate默认使用Set)
    //通过ORM方式表示:一个客户对应多个联系人
        //放置多的一方的集合,一个订单分类有多个订单
        //Hibernate默认使用的是Set集合
        private Set<LinkMan> linkManSet;
    
    • 在多的一方创建一的对象代替外键属性

      //通过ORM方式表示:一个联系人只能属于某一个客户
          //放置的是一的一方的对象
          //外键名称改为一的一方的
          private Customer customer;
      
  • 映射文件配置一对多映射关系
    • 多对一(LinkMan.hbm.xml)
      • name : 另一方在本类(LinkMan)的属性名称
      • class : 一的一方的类的全路径
      • column : 在多的一方的表里面的外键名称
          <!--配置多(linkman)对一(customer)的关系:放置的是一的一方的对象
                  配置三个属性,name : 一的一方对象的属性名称
                               class :  一的一方的类的全路径
                               column : 在多的一方的表的外键名称
              -->
              <many-to-one name="customer" class="com.gxufe.hibernate.domain.Customer" column="lkm_cust_id"/>
      
      
    • 一对多(Customer)
        <!--配置一对多的映射:放置多的一方的集合
                  set标签:
                      * name : 多的一方的对象在本类对应的属性名称
                  key标签:
                      * column : 多的一方的外键名称
                  one-to-many标签:
                      * class :  多的一方的类全路径        
              -->
              <set name="linkManSet" >
                  <key column="lkm_cust_id"/>
                  <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/>
              </set>
      
  • 引入映射文件

    <mapping resource="com/gxufe/hibernate/domain/LinkMan.hbm.xml"/>
    
10.1.2 一对多级联操作

级联指的是:操作一个对象的时候,是否同时操作其他关联的对象

  • 级联是由方向性
    • 操作一的一方的时候,是否操作到多的一方
    • 操作多一方的时候,是否操作到一的一方
  • 级联保存或更新
    • 保存客户级联联系人
    • 保存联系人级联客户

xml设置cascde属性

       <set name="linkManSet"  cascade="save-update">
            <key column="lkm_cust_id"/>
            <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/>
        </set>
<many-to-one name="customer" class="com.gxufe.hibernate.domain.Customer" column="lkm_cust_id" cascade="save-update"/>

10.1.3 一对多级联删除

  • 设置映射文件(配置的主体是需要执行被删除的项)

    • 要删除的是Customer,级联删除就会把LinkMan相对应的也删除

    添加一个级联属性值:delete

<set name="linkManSet"  cascade="save-update,delete">
            <key column="lkm_cust_id"/>
            <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/>
        </set>
  • 测试类

        /**
         *级联删除
         *  * 删除客户级联删除联系人,删除的主体的是客户,需要在Customer.hbm.xml配置
         */
        public void  demo04(){
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
                //没有设置级联删除,默认情况下:修改了联系人的外键为null,删除客户
                Customer customer = session.get(Customer.class,1l);
                /*session.delete(customer);*/
    
                //删除客户,同时删除联系人
            session.delete(customer);
    
            transaction.commit();
        }
    
10.1.3 一对多设置双向关联产生多余SQL语句

多的一方可以通过一的对象属性(外键)维护外键,一的一方可以通过Set集合去维护多的一方

  • 解决多余的SQL语句

    • 单向维护

    • 是一方放弃外键维护权

      • 一的一方放弃维护,inverse="true"

                <set name="linkManSet"  cascade="save-update,delete" inverse="true">
        
      • 一对多的关联查询的修改时候 (CRM)

  • 区分cascde 和 inverse

10.2 多对多

  • 多对多建表原则:
    • 创建中间表,中间表至少有两个字段,分别作为外键指向多对多双方的主键 例如:学生表和选课表是多对多,创建中间表为学生选课表,学生选课表最少要有两个字段分别为学生id和课程id
  • 二者关系主要通过集合来操作
    • 首先查询
    • 使用Set集合里面的add、remove方法
10.2.1 多对多的类设置集合

两个类都设置一个存放对方Set集合

例如:

 private Set<Role> roles= new HashSet<Role>() ;
private Set<User> users = new HashSet<User>();	
10.2.2 多对多的映射文件Set属性设置
  • setname属性是当前类里边的Set变量的名称,多对多要使用中间表table属性就是写中间表
  • **key:**里面标签的column,-当前-对象的对应中间表的外键名称
  • m-to-m:class 是对方类的全路径 column对方的对象在中间表中的外键名称
<!--
主要设置关联中间表的属性
-->
<set name="roles" table="sys_user_role" cascade="save-update,delete" inverse="false">
    <key column="user_id"/>
    <many-to-many class="com.gxufe.hibernate.domain.Role" column="user_id"/>
</set>
10.2.3 测试类
  • 保存:多对多建立了双向的关系必须有一方放弃外键维护,一般是被动方放弃外键维护(角色被用户选择)
 @Test
    /**
     * 保存多条记录:保存多个用户角色
     */
    public void demo01(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //创建2个用户
        User user1 = new User();
        User user2 = new User();
        user1.setUser_name("迪丽热巴");
        user2.setUser_name("古力娜扎");

        //3个角色
        Role role1 = new Role();
        Role role2 = new Role();
        Role role3 = new Role();
        role1.setRole_name("研发部");
        role2.setRole_name("市场部");
        role3.setRole_name("营销部");

        //设置双向的关联关系:
        user1.getRoles().add(role1);
        user1.getRoles().add(role2);
        user2.getRoles().add(role2);
        user2.getRoles().add(role3);

        role1.getUsers().add(user1);
        role2.getUsers().add(user1);
        role2.getUsers().add(user2);
        role3.getUsers().add(user2);

        //保存:多对多建立了双向的关系必须有一方放弃外键维护
        //一般是被动方放弃外键维护(角色被用户选择)
        session.save(user1);
        session.save(user2);
        session.save(role1);
        session.save(role2);
        session.save(role3);

        transaction.commit();
    }
10.2.4 多对多的级联保存或更新
  • 配置User.hbm.xml的cascde属性

    cascade="save-update"
    
  • 保存测试

        @Test
        //多对多级联保存
        //保存用户级联保存角色,在用户的映射文件中配置
        public void demo02(){
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
    
            User user1 = new User();
            user1.setUser_name("迪丽热巴");
    
            Role role1 = new Role();
            role1.setRole_name("研发部");
    
            user1.getRoles().add(role1);
            role1.getUsers().add(user1);
    
            session.save(user1);
            transaction.commit();
        }
        
    
10.2.5 多对多级联删除(基本用不上)

因为删除学生选课不能也把这个课程给删除了

10.2.6 多对多的常用其他操作

给用户选择课程,给用户改选课程,给用户删除课程

  • 添加角色
 @Test
    //给用户选择角色
    public void demo03(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //给1号用户多选2号角色
        //查询1号用户
        User user = session.get(User.class,1l);
        //查询2号角色
        Role role = session.get(Role.class,3l);
        //给1号用户添加3号角色
        user.getRoles().add(role);

        transaction.commit();
    }
  • 修改所选之一

思想:先删除后修改

   @Test
    //给用户改选角色
    public void demo04(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //给2号用户将原有的2号角色改为3号角色
        //查询2号用户
        User user = session.get(User.class,2l);
        //查询2 . 3号角色
        Role role2 = session.get(Role.class,2l);
        Role role3 = session.get(Role.class,3l);
        //给2号用户修改2号为3号角色
        user.getRoles().remove(role2);
        user.getRoles().add(role3);

        transaction.commit();
    }
  • 删除一门课
@Test
    //给用户删除角色
    public void demo05(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //给2号用户删除1号角色
        //查询2号用户
        User user = session.get(User.class,2l);
        //查询1号角色
        Role role = session.get(Role.class,1l);
        //给2号用户删除1号角色
        user.getRoles().remove(role);

        transaction.commit();
    }

10.3 一对一

  • 一对一建表原则:
    • 唯一外键对应
    • 主键对应

11 Hibernate查询方式

Hibernate提供了很多种查询方式。Hibernate提供了5中查询方式

11.1 OID查询

OID:Hibernate根据对象的OID(主键)进行检索

分别是:

  • 使用get方法

    User user = session.get(User.class,1);
    
  • 使用load方法

    User user = session.load(User.class,1);
    

11.2 对象导航查询

Hibernate根据一个已经查询到的对象,获得其关联对象的一中查询方式

User user = session.get(User.class,1);
Role role = user.getRole();

* 11.3 HQL查询

Hiernate Query Language,Hibernate的查询语言,是一种面向对象的查询语言,语法类似SQL。通过session.createQuery(),用于接受一个HQL进行查询方式

11.3.1 简单查询
public void demo02(){
        Session sesion = HibernateUtils.getCurrentSession() ;
        Transaction transaction = sesion.beginTransaction();

        //简单写法
//        Query query = sesion.createQuery("from Customer");
        //别名查询 select c from Customer c
        Query query = sesion.createQuery("from Customer c");
        List<Customer> list = query.list();
      /*
        sql中支持的 * 号写法 select * from 表
        HQL不支持 * 写法
       */


        for (Customer customer : list){
            System.out.println(customer);
        }
        transaction.commit();
    }
11.3.2 排序查询


    @Test
    /**
     * 排序查询
     */
    public void demo03(){
        Session sesion = HibernateUtils.getCurrentSession() ;
        Transaction transaction = sesion.beginTransaction();
        //排序,默认升序(asc)
        //设置降序(desc)
        Query query = sesion.createQuery("from Customer order by cust_id  desc");
        List<Customer> list = query.list();
        for (Customer customer : list){
            System.out.println(customer);
        }
        transaction.commit();
    }
11.3.3 条件查询
 @Test
    /**
     * 条件查询
     */
    public void demo04(){
        Session sesion = HibernateUtils.getCurrentSession() ;
        Transaction transaction = sesion.beginTransaction();
        //条件查询
        //一、 按位置绑定
             //一个条件
//        Query query = sesion.createQuery("from Customer where  cust_name = ? ");
//        query.setParameter(0,"马尔扎哈");

         /*   //两个条件
        Query query = sesion.createQuery("from Customer where cust_name = ? and cust_source = ? ");
        query.setParameter(0,"马尔扎哈");
        query.setParameter(1,"lol");
        */

         //二、按名称绑定
        Query query = sesion.createQuery("from Customer where cust_source = :aaa and cust_name like :bbb");
        query.setParameter("aaa","lol");
        query.setParameter("bbb","马尔%");

        List<Customer> list = query.list();
        for (Customer customer : list){
            System.out.println(customer);
        }
        transaction.commit();
    }
11.3.4 投影查询

投影查询:查询对象的某个或某些属性

  • 单个查询

      Session sesion = HibernateUtils.getCurrentSession() ;
            Transaction transaction = sesion.beginTransaction();
            //投影查询(某些属性)
            //因为只是返回某一列不是整个Customer对象,所以泛型不能是Customer
            //单个属性
    //        List<Object> objects = sesion.createQuery("select c.cust_name from  Customer c").list();
    
            //多个属性,每一列的属性不一样
            /*List<Object[]> objects = sesion.createQuery("select c.cust_name ,c.cust_source from Customer c").list();
    
            for (Object[] object : objects){
                System.out.println(Arrays.toString(object));
            }
    
  • 多个查询

    //多个属性,每一列的属性不一样
    /*List<Object[]> objects = sesion.createQuery("select c.cust_name ,c.cust_source from Customer c").list();
    
    for (Object[] object : objects){
        System.out.println(Arrays.toString(object));
    }*/
    
  • 查询多个属性并封装到对象中

    
            //查询多个属性,但是我想封装到对象中
            //首先要提供构造方法
            List<Customer> list = sesion.createQuery("select new Customer(cust_name,cust_source) from Customer").list();
            for (Customer customer : list){
                System.out.println(customer);
            }
    
            transaction.commit();
        }
    
11.3.5 分组统计查询
  • sql的分组查询语句:
select cust_source,count(*) from cust_customer group by cust_source
  • 聚合函数的使用: count(*),max(),min(),avg(),sum()

  • 只统计cust_source个数大于2的

    select cust_source ,count(*) from Customer group by cust_source having count(*)>=2
    
 @Test
    /**
     * 分组统计查询
     */
    public void demo07(){
        Session sesion = HibernateUtils.getCurrentSession() ;
        Transaction transaction = sesion.beginTransaction();
        //聚合函数的使用: count(*),max(),min(),avg(),sum()
        //uniqueResult (只能查到一个值)
        Object o = sesion.createQuery("select count(*) from LinkMan").uniqueResult();
        System.out.println(o);//可以强转为Long


        //分组统计查询  sql:select cust_source,count(*) from cust_customer group by cust_source
        //统计cust_source的个数
//        List<Object[]> list = sesion.createQuery("select cust_source ,count(*) from Customer group by cust_source").list();
        //只统计cust_source个数大于2的
        List<Object[]> list = sesion.createQuery("select cust_source ,count(*) from Customer group by cust_source having count(*)>=2").list();
        for (Object[] object : list){
            System.out.println(Arrays.toString(object));
        }
        transaction.commit();
    }
11.3.6 分页查询
@Test
/**
 * 分页查询
 */
public void demo06(){
    Session sesion = HibernateUtils.getCurrentSession() ;
    Transaction transaction = sesion.beginTransaction();
    //分页
    //创建一个客户
    Query query = sesion.createQuery("from LinkMan");
    query.setFirstResult(10);
    query.setMaxResults(10);
    List<LinkMan> list = query.list();
    for (LinkMan linkMan : list){
        System.out.println(linkMan);
    }

    transaction.commit();
}
* HQL的多表查询
  • SQL的多表查询
    • 连接查询

      • 交叉连接:笛卡尔积(不用)

        select * from A,B
        
      • 内连接 : inner join (inner 可以省略)

        查的是两个表的公共部分

        • 隐式内连接

          select * from A,B where A.id = B.aid;
          
        • 显示内连接

          select * from A inner join B on A.id=B.aid;
          
      • 外连接 :

        • 左外连接 : left outer join(outer 可以省略)

          查到的是左边表的所有信息,和另外一个表的公共部分

          select * from A left outer join B on a.id=B.aid;
          
        • 右外连接 : right outer join (outer 可以省略)

          查到的是右边的全部,以及两个表的公共部分

          select * from A right join B on a.id=B.aid;
          
    • 子查询
  • HQL的多表查询
  • 连接查询
    • 交叉连接

    • 内连接

      • 显示内连接

        #SQL:
        select * from A inner join B on A.id=B.aid;
        
        #HQL:
        from Customer c inner join c.linkManSet
        
      • 隐式内连接

        sql:

        select * from A,B where A.id = B.aid;
        

        HQL:

        Query query = session.createQuery("from Customer c,LinkMan l where c.cust_id=l.customer");
        
      • 迫切内连接

        HQL:迫切内连接,其实就是在普通的内连接inner join后添加一个关键字fetch

        from Customer c inner join fetch c.linkManSet
        
        //迫切内连接,fetch :通知hibernate,将另一个对象封装到该对象中,所以返回的对象是Customer
                //设置distinct c(不同) 的,不然会重复 
                Query query = session.createQuery("select distinct c from  Customer c inner join fetch c.linkManSet");
                List<Customer> list = query.list();
                for (Customer customer : list ) {
                    System.out.println(customer+"\n");
                }
        
    • 外连接

      • 左外连接

      • 右外连接

      • 迫切左外连接

           //迫切左外连接
              Query query = session.createQuery("select distinct c from  Customer c left join fetch c.linkManSet");
                List<Customer> list = query.list();
                for (Customer customer : list ) {
                    System.out.println(customer+"\n");
                }
        

* 11.4 QBC查询

QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询方式

  • 查询需要使用的对象
    • add : 普通的条件 ,where后面的条件
    • addOrder :排序
    • setProjection :聚合函数group by having
11.4.1 简单查询
@Test
/**
 * 简单查询
 */
 public void demo01(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();

    //获得Criteria的对象
    Criteria criteria = session.createCriteria(Customer.class);
    List<Customer> list = criteria.list();
    for (Customer customer : list){
        System.out.println(customer);
    }

    transaction.commit();
}
11.4.2 排序查询

调用Criteria对象有排序的方法

  criteria.addOrder(Order.desc("cust_id"));
@Test
/**
 * 排序查询
 */
public void demo02(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();

    //获得Criteria的对象
    Criteria criteria = session.createCriteria(Customer.class);
    //调用
    criteria.addOrder(Order.desc("cust_id"));
    List<Customer> list = criteria.list();
    for (Customer customer :list){
        System.out.println(customer);
    }

    transaction.commit();
}
11.4.3 分页查询
  • 设置两个参数

    criteria.setFirstResult(0);
            criteria.setMaxResults(10);
    
11.4.4 条件查询
  • 需要记住一些方法名称

    @Test
        /**
         * 条件查询
         */
        public void demo04(){
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
    
            //获得Criteria的对象
            Criteria criteria = session.createCriteria(Customer.class);
            //设置条件
            /**
             * =    eq
             * >    gt  (greater than)
             * >=   ge
             * <    lt
             * <=   le  less than
             * <>   ne (不等于)
             * like
             * in
             * and
             * or
             */
            //一个条件
           //criteria.add(Restrictions.eq("cust_source","lol"));
            //两个条件
           criteria.add(Restrictions.eq("cust_source","lol"));
           criteria.add(Restrictions.like("cust_name","马%"));
           //or
            //criteria.add(Restrictions.or(Restrictions.like("cust_name","马%")));
    
            List<Customer> list = criteria.list();
            for (Customer customer :list){
                System.out.println(customer);
            }
    
            transaction.commit();
        }
    
11.4.5 统计查询
@Test
/**
 * 统计查询
 */
public void demo05(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();

    //获得Criteria的对象
    Criteria criteria = session.createCriteria(Customer.class);
    /**
     * add              :普通的条件 ,where后面的条件
     * addOrder         :排序
     * setProjection    :聚合函数group by having
     */
    //统计
    criteria.setProjection(Projections.rowCount());//返回行数
    Long num =(Long) criteria.uniqueResult();
    System.out.println("num="+num);
   
    transaction.commit();
}
* 11.4.6 离线条件查询(DetachedCriteria)

离线:脱离session使用

  • 在web层上实现

      //web层
            DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
            dc.add(Restrictions.like("cust_name","马尔%"));
    
    
  • 在dao层代码

    //dao层
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    
    Criteria criteria = dc.getExecutableCriteria(session);
    List<Customer> list = criteria.list();
    for (Customer customer : list){
        System.out.println(customer);
    }
    transaction.commit();
    

11.5 SQL查询

通过使用基本的sql查询

  @Test
    /**
     * sql基本查询
     */
    public void demo07() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //存放到数组当中
       /* SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
        List<Object[]> list = sqlQuery.list();
        for (Object[] objects : list){
            System.out.println(Arrays.toString(objects));*/

        //存放到对象当中
        SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
        sqlQuery.addEntity(Customer.class);
        List<Customer> list = sqlQuery.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        transaction.commit();
    }

12 抓取策略(优化)

12.1 延迟加载

延迟加载:lazy (懒加载)。执行该代码的时候,不会发送语气进行查询,在真正使用这个对象的属性的时候才会发送sql语句进行查询

12.1.1 延迟加载的分类
  • 类级别的延迟加载

    就是在class标签里面的设置

    指的是通过load方法查询某个对象的时候,是否采用延迟。、

    session.load(Customer.class,1);

     <class name="com.gxufe.hibernate.domain.Customer" table="cst_customer"  lazy="true">
         <!--lazy默认是true,设置为false,就会失效,load的方法就会马上加载-->
    
    • 使其失效的方法
      • 设置lazy为false
      • 将持久化类使用final修饰
      • Hibernate.initialize(obj)
  • 关联级别的延迟加载

    在set、many-to-one 标签里面设置lazy

    指的是在查询到某个对象的时候,查询其关联对象的时候,是否采用延迟加载。

    Customer customer = session.get(Customer.class,1);//里面有个Set<LinkMan>集合
    

    customer.getLinkManSet();–通过客户获得联系人的时候,是否采用了延迟加载,称为是关联级别的延迟。

12.1.2 抓取策略

通过一个对象抓取到关联对象需要发送sql语句,SQL如何发送,发送称什么样的格式进行策略进行配置

  • 通过<set>或者<many-to-one上通过fetch属性进行设置

  • fetch和这些标签上的lazy如何设置优化发送的Sql语句

    <set name="linkManSet"  fetch="join" lazy="true" >
    
  • <set>上的fetch和lazy
    • fetch:抓取策略,控制SQL语句格
      • select :默认,发送普通的select语句,查询其关联对象
      • join :发送一条迫切左外连接查询关联对象
      • subselect :发送一条子查询查询其关联对象
    • lazy:延迟加载,控制查询关联对象的时候是否采用延迟
      • true :默认,查询其关联对象的时候,采用延迟加载
      • false :查询关联对象的时候,不采用延迟加载
      • extra :极其懒惰。

在实际开发中,一般采用默认值。有特殊的需求,可能需要配置join(迫切左查询)

  • <many-to-one>上的fetch和lazy
    • fetch:抓取策略,控制SQL语句格
      • select : 默认
      • join : 发送一条迫切左外连接
    • lazy:
      • proxy : 默认 ,proxy具体的取值,取决于另一(一的一方)端的class上的lazy的值。
      • false : 查询关联对象,不采用延迟
      • no-proxy(不会使用)
  • 批量抓取

    一批关联对象一起抓取,batch-size

    • 通过一的一方查多的一方配置

       <set name="linkManSet"  fetch="select" lazy="true" batch-size="4" >
      
    • 通过多的一方查one的一方,因为多的一方many-to-one标签没有batch-size属性,所以只能在多的一方的class标签设置

       <class name="com.gxufe.hibernate.domain.Customer" table="cst_customer"  lazy="true" batch-size="3">
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值