【Java中级】13.0 SSH之Hibernate框架(十)—

2020-02-11  本文已影响0人  bobokaka

本篇内容源代码:
Github —— HibernateQueryDemo

1.0 QBC在多表查询上有局限。
2.0 SQL的多表查询
  • 连接查询
  • 交叉连接:最不经常用,获得两个表的笛卡尔积(两个表的乘积)
  • 内连接:inner join(inner可以省略),查到的是两个表公共的部分(两个表的交集),相当于只查两个表有关联的数据。
  • 隐式外连接:在SQL语句中不能看到inner join两个关键字,但是最后查到的结果和inner join查到的是一样的。
  • 显式外连接:在SQL语句中能够看到inner join这两个关键字
  • 外连接:outer join
  • 左外连接:left outer join(outer可以省略)查询左边表的全部数据和两个表的公共部分。
  • 右外连接:right outer join(outer可以省略)查询右边表的全部数据和两个表的公共部分。
  • 子查询:SQL语句的嵌套

相应的SQL语句为:

//交叉连接
SELECT * FROM cst_customer,cst_linkman;

//显式内连接
SELECT * FROM cst_customer JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
SELECT * FROM cst_customer INNER JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
//可以起别名,上面语句等同于:
SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id = l.lkm_cust_id;
//隐式内连接
SELECT * FROM cst_customer,cst_linkman WHERE cst_customer.cust_id = cst_linkman.lkm_cust_id;

//左外连接
SELECT * FROM cst_customer LEFT JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
SELECT * FROM cst_customer LEFT OUTER JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
//右外连接
SELECT * FROM cst_customer RIGHT JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
SELECT * FROM cst_customer RIGHT OUTER JOIN cst_linkman ON cst_customer.cust_id = cst_linkman.lkm_cust_id;
3.0 HQL进行多表的查询
  • 连接查询
  • 交叉连接
  • 内连接
  • 隐式外连接
  • 显式外连接
  • 迫切内连接
  • 外连接
  • 左外连接
  • 右外连接
  • 迫切左外连接:没有迫切右外连接
  • 子查询
    @Test
    /**
     * 
     * @Title: demo9
     * @Description: HQL的多表查询
     * @param
     * @return void
     * @throws
     *
     */
    public void demo9() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        // 显示内连接,根据配置,自动关联
        // SELECT * FROM cst_customer JOIN cst_linkman ON cst_customer.cust_id =
        // cst_linkman.lkm_cust_id;
// List<Object[]> list = session.createQuery("from Customer c inner join
// c.linkMans").list();
// for (Object[] objects : list) {
// System.out.println(Arrays.toString(objects));
// }

        // 迫切内连接,在普通的内连接inner join后添加一个关键字fetch
        // fetch:通知Hibernate将另一个对象的数据封装到对象中
        //// 一条数据,数据里面有1个联系人
// List<Customer> list = session.createQuery("from Customer c inner join fetch
// c.linkMans").list();
// 三条数据,数据里面有10条联系人
        List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMans")
                .list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        transaction.commit();
    }
4.0 SQL检索
    @Test
    /**
     * 
     * @Title: demo1
     * @Description: SQL查询
     * @param
     * @return void
     * @throws
     *
     */
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

//      NativeQuery nativeQuery = session
//              .createSQLQuery("SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id = l.lkm_cust_id;");
//
//      List<Object[]> list2 = nativeQuery.list();
//      for (Object[] objects : list2) {
//          System.out.println(Arrays.toString(objects));
//      }
        
        //封装到对象中
        NativeQuery nativeQuery = session
                .createSQLQuery("SELECT * FROM cst_customer;");
        nativeQuery.addEntity(Customer.class);
        List<Customer> list = nativeQuery.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        transaction.commit();
    }
5.0 Hibernate的抓取策略(优化)

抓取策略往往不单独使用,和延迟加载同时使用。(往往和其中的关联级别的延迟加载一起使用)来优化语句。

5.1 延迟加载

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

5.2 延迟加载的分类
1. 类级别的延迟加载

指的是通过load方法查询某个对象的时候,是否采用延迟。session.load(Customer.class,1l);
通过<class>上的lazy进行配置,如果想要lazy失效,可以:

  1. lazy设置为false,但是只对自己的普通属性有效,对关联对象无效。
  2. 将持久化类使用final修饰。
  3. 调用Hibernate.initialize();方法。
image.png
@Test
    /**
     * 
     * @Title: demo1
     * @Description: 类级别的延迟加载
     * 在<class>的标签上配置lazy =true
     * @param
     * @return void
     * @throws
     *
     */
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        Customer customer = session.load(Customer.class, 1l);
        //该语句使延迟加载失效
//      Hibernate.initialize(customer);
        System.out.println(customer);
        
        transaction.commit();
    }
2. 关联级别的延迟加载

指的是在查询到某个对象的时候,在查询其关联的对象的时候,是否采用延迟加载。
Cuteomer customer = session.get(Customer.class,1l);
customer.getLinkMans();//通过客户获得联系人的时候,联系人对象是否采用了延迟加载,称为是关联级别的延迟。

设置Customer.hbm.xml中(一的一方的表中),<set>标签,或者LinkMan.hbm.xml表中(多的一方的表)<many-to-one>标签的lazy属性。

image.png
image.png
5.3 抓取策略的概述

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

  • 设置Customer.hbm.xml中(一的一方的表中),<set>标签,或者LinkMan.hbm.xml表中(多的一方的表)<many-to-one>标签的fetch属性进行配置。
  • fetch和这些标签上的lazy如何设置优化发送SQL语句


    image.png
5.4 <set>上的fetch和lazy
  • fetch:抓取策略,控制SQL语句格式。
  • select(默认值):发送普通的select语句,查询关联对象。
  • join:发送一条迫切左外连接查询关联对象。
  • subselect:发送一条子查询查询其关联对象。
  • lazy: 延迟加载,控制查询关联对象的时候是否采用延迟
  • true(默认值):查询关联对象时,采用延迟加载
  • false:查询关联对象时,不采用延迟加载
  • extra:极其懒惰,用什么语句发什么样的语句。
1. 默认情况:
    @Test
    /**
     * 
     * @Title: demo1
     * @Description:默认情况
     * @param
     * @return void
     * @throws
     *
     */
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        // 查询1号客户
        Customer customer = session.get(Customer.class, 1l);//发送一条查询客户的SQL
        System.out.println(customer.getCust_name());
        // 查看1号客户的每个联系人的信息
        for (LinkMan linkMan : customer.getLinkMans()) {//发送一条根据客户ID查询联系人的SQL
            System.out.println(linkMan.getLkm_name());
        }
        System.out.println(customer);

        transaction.commit();
    }

运行:


image.png

等同于:<set name="linkMans" lazy="true" fetch="select">
设置Customer.hbm.xml:

<?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>
    <!-- hibernate-mapping是根标签 -->
    <!-- 建立类与表的映射 catalog="hibernate0207" -->
    <class name="com.edp.hibernate.domain.Customer" table="cst_customer" lazy="true">
    
    <!-- 配置一对多的关系:放置的是多的一方的集合 -->
        <!-- 联系人是多,客户是一 -->
        <!-- set标签
            name:对的一方的对象集合的属性名称
            cascade:级联,用逗号隔开,就同时拥有保存或更新、删除功能
            inverse:放弃外键维护权,true是确定放弃外键维护权。默认的false是不放弃。
        -->
        <set name="linkMans" lazy="true" fetch="select">
            <!-- key标签
                column:多的一方的外键的名称
            -->
            <key column="lkm_cust_id"></key>
            <!-- one-to-many标签
                class:多的一方的类的全路径
            -->
            <one-to-many class="com.edp.hibernate.domain.LinkMan"/>
        </set>
    </class>

</hibernate-mapping>
2. 其他配置

fetch配置成join,lazy配置没有意义,发送迫切左外连接,两个表的数据全查了,只发送一次语句。
fetch配置成select,lazy配置true,相当于默认情况。
fetch配置成select,lazy配置false,在第一次运行时就会发送2条sql语句。
fetch配置成select,lazy配置extra,如果调用个数,就会发送一条select count() from……
fetch配置成subselect,lazy配置true,子查询加懒查询。
fetch配置成subselect,lazy配置false,子查询和本体查询一起完成,没有懒。
fetch配置成subselect,lazy配置extra,子查询,更加懒。

在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。

5.5 <many-to-one>上的fetch和lazy
  • fetch:抓取策略,控制SQL语句格式。
  • select(默认值):发送普通的select语句,查询关联对象。
  • join:发送一条迫切左外连接查询关联对象。
  • lazy: 延迟加载,控制查询关联对象的时候是否采用延迟
  • proxy(默认值):查询关联对象时,采用延迟加载
  • false:查询关联对象时,不采用延迟加载
  • no-proxy:(不会使用)

此时这样的设计,用于联系人查客户(多的一方查一的一方)

  • fetch配置成select,lazy配置proxy,默认情况,发送两次sql语句。取决于客户一端(一的一方)的lazy是true还是false,如果是true, 则<many-to-one>上proxy属性等于true。
  • fetch配置成select,lazy配置false,一次发送2次SQL语句。
  • fetch配置成join,lazy配置无效,迫切左外连接查询,只需要一条查询语句。

在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。

6.0 批量抓取

一批关联对象一起抓取,batch-size默认值为1。
测试批量抓取

    @Test
    /**
     * 
     * @Title: demo1
     * @Description: 获取客户的时候,批量抓取联系人
     * @param
     * @return void
     * @throws
     *
     */
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        // 以下代码会发送5条SQL语句
        List<Customer> list = session.createQuery("from Customer").list();
        for (Customer customer : list) {
            System.out.println(customer.getCust_name());
            for (LinkMan linkMan : customer.getLinkMans()) {
                System.out.println(linkMan.getLkm_name());
            }
        }
        // 使用批量抓取
        // 在Customer.hbm.xml中设置<set name="linkMans" batch-size="4">
        //batch-size默认是1
        // 只发送2条sql语句
        transaction.commit();
    }

    @Test
    /**
     * 
     * @Title: demo2
     * @Description: 获取联系人的时候,批量抓取客户
     * @param
     * @return void
     * @throws
     *
     */
    public void demo2() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        // 以下代码会发送4条SQL语句
        List<LinkMan> list = session.createQuery("from LinkMan").list();
        for (LinkMan linkMan : list) {
            System.out.println(linkMan.getLkm_name());
            System.out.println(linkMan.getCustomer().getCust_name());
        }
        // 使用批量抓取
        // 在Customer.hbm.xml中设置<class name="com.edp.hibernate.domain.Customer" table="cst_customer" batch-size="4" >
        // 只发送2条sql语句
        transaction.commit();
    }

END

上一篇下一篇

猜你喜欢

热点阅读