美文网首页
【Java中级】13.0 SSH之Hibernate框架(十)—

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

作者: bobokaka | 来源:发表于2020-02-11 18:11 被阅读0次

    本篇内容源代码:
    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

    相关文章

      网友评论

          本文标题:【Java中级】13.0 SSH之Hibernate框架(十)—

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