zoukankan      html  css  js  c++  java
  • day31_Hibernate学习笔记_03

    一、Hibernate的关联关系映射(多对多)

    在数据库表中如何表达多对多关系:
      使用中间表,分别引用两方的ID。
    在对象中如何表达多对多关系:
      两方都使用集合表达
    在配置文件中如何表达一对多关系:

        <!-- 配置多对多关联关系 -->
        <set name="courses" table="t_stu_cour">
            <key column="stu_id"/>
            <many-to-many class="Course" column="cour_id"/>
        </set>
        <!-- 配置多对多关联关系 -->
        <set name="students" table="t_stu_cour">
            <key column="cour_id"/>
            <many-to-many class="Student" column="stu_id"/>
        </set>      

    操作:
      inverse:本方是否要放弃维护外键关系
      cascade:是否需要级联操作(有5个值)
    注意配置级联删除时,要小心!当双方都配置级联删除时,任意删除一条记录,整个关系链数据都会被删除。

    1.1、多对多实现【掌握】

    1.1.1、实现类

    Student.java

    package com.itheima.domain;

    import java.util.HashSet;
    import java.util.Set;

    public class Student {
        private Integer sid;
        private String sname;
        // 学生可以选择多门课程
        private Set<Course> courses = new HashSet<Course>();

        // getter和setter方法
    }

    Course.java

    package com.itheima.domain;

    import java.util.HashSet;
    import java.util.Set;

    public class Course {
        private Integer cid;
        private String cname;
        // 课程可以被多个学生选择
        private Set<Student> students = new HashSet<Student>();

        // getter和setter方法
    }

    1.1.2、配置文件

    Student.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  package="com.itheima.domain">
        <class name="Student" table="t_student">
            <id name="sid" column="sid">
                <generator class="native"></generator>
            </id>
            <property name="sname" column="sname"></property>
            <!-- 配置多对多关联关系 -->
            <set name="courses" table="t_stu_cour">
                <key column="stu_id"/>
                <many-to-many class="Course" column="cour_id"/>
            </set>
        </class>
    </hibernate-mapping>

    Course.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  package="com.itheima.domain">
        <class name="Course" table="t_course">
            <id name="cid" column="cid">
                <generator class="native"></generator>
            </id>
            <property name="cname" column="cname"></property>
            <!-- 配置多对多关联关系 -->
            <set name="students" table="t_stu_cour">
                <key column="cour_id"/>
                <many-to-many class="Student" column="stu_id"/>
            </set>
        </class>
    </hibernate-mapping>

    hibernate.cfg.xml

    ......
    <!-- 添加ORM映射文件 ,填写src之后的路径 -->
    <mapping resource="com/itheima/domain/Student.hbm.xml"/>
    <mapping resource="com/itheima/domain/Course.hbm.xml"/>
    ......

    1.1.3、测试代码

    架构图如下所示:


    测试多对多代码:
    package com.itheima.a_many2many;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Course;
    import com.itheima.domain.Student;
    import com.itheima.utils.HibernateUtils;

    public class Demo1 {
        @Test
        // 保存学生 =>  通过学生保存课程,由学生来维护外键
        // Student 的
        //      inverse => false
        //      cascade => save-update
        // Course 的
        //      inverse => true
        // 注意:实际开发中,在配置多对多关系时,一般需要有一方放弃维护外键关联关系

        // 5条 select 语句 + 6条 update 语句  (因为1个学生关联了3个课程)
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Student stu1 = new Student();
            stu1.setSname("李晓艺");
            Student stu2 = new Student();
            stu2.setSname("陈明军");

            Course c1 = new Course();
            c1.setCname("Struts2");
            Course c2 = new Course();
            c2.setCname("Hibernate");
            Course c3 = new Course();
            c3.setCname("Spring");

            stu1.getCourses().add(c1); // 维护关系+级联保存
            stu1.getCourses().add(c2);
            stu1.getCourses().add(c3);

            stu2.getCourses().add(c1);
            stu2.getCourses().add(c2);
            stu2.getCourses().add(c3);

            session.save(stu1);
            session.save(stu2);

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

    二、加载(抓取)策略(优化查询)

    1、加载策略种类:
        延迟加载:等到使用的时候才会加载数据。
        立即加载:不管使用不使用,都会立刻将数据加载。

    2、加载策略的应用:
        类级别的加载策略。
        关联级别的加载策略。

    3、类级别加载策略:
        get/load
            get:立即查询数据库,将数据初始化。
            load:hbm文件中,class元素的lazy属性决定该方法的类级别加载策略,默认值是true
                true:先返回一个代理对象,使用代理对象的属性时,才去查询数据库。
                falseload方法一执行就会发送sql语句,与get一致,会立即加载数据。

    4、关联级别加载策略:
        在查询有关联关系的数据时,加载一方的数据是否需要将另一方立即查询出。
        默认是:与我关联的数据,在使用时才会被加载。

        注意:下面所用的例子是:客户(一)和订单(多)
        ------------------------------------------------------------------------------------------------------------------------------
        一对多(集合的检索策略):根据客户去找订单
        <set 
            lazy:是否对set数据使用懒加载
                true        (默认值)对集合使用赖加载
                false       集合将会被立即加载
                extra       极其懒惰,在使用集合时,若调用size方法查询数量,则Hibernate会发送count语句,只查询数量,不加载集合内的数据
            fetch:决定加载集合使用的sql语句种类
                select      (默认值)普通select语句查询
                join        使用 表连接语句 查询集合数据,即使用 多表查询语句 集合数据
                subselect   使用子查询语句,在一次加载多个客户的订单数据的情况下才有效

            fetch       lazy    结果
            ------------------------
            select      true    都是默认值,会在使用集合(订单)时才加载,使用普通select语句查询集合数据
            select      false   立刻使用select语句加载集合数据
            select      extra   会在使用集合(订单)时才加载,普通select语句,如果你使用集合只是为了获得集合的长度,则Hibernate只会发送count语句查询集合长度

            join        true    因为查询集合(订单)时使用表连接语句查询,所以会立刻加载集合数据(与lazy属性无关了,lazy属性失效)
            join        false   因为查询集合(订单)时使用表连接语句查询,所以会立刻加载集合数据(与lazy属性无关了,lazy属性失效)
            join        extra   因为查询集合(订单)时使用表连接语句查询,所以会立刻加载集合数据(与lazy属性无关了,lazy属性失效)

            subselect   true    会在使用集合(订单)时才加载,使用子查询语句查询集合(订单)数据
            subselect   false   会在查询客户时,立即使用子查询语句加载客户的订单数据
            subselect   extra   会在使用集合(订单)时才加载,子查询语句,如果你使用集合只是为了获取集合的长度,则Hibernate只会发送count语句查询集合长度
        ------------------------------------------------------------------------------------------------------------------------------  
        多对一:根据订单去找客户
        <many-to-one
            lazy
                false       加载订单时,会立即加载客户
                proxy       看客户对象的类加载策略来决定
                no-proxy    不做研究
            fetch
                select      (默认值)使用普通select加载
                join        使用表连接加载数据

            fetch       lazy    结果
            ------------------------
            select      false   加载订单时,立即加载客户数据,普通select语句加载客户
            select      proxy   客户的类加载策略为:lazy="false" 时,   同上
                                                  lazy="true"  时,   加载订单时,先不加载客户数据,使用客户数据时才加载
            join        false   使用表连接查询订单以及对应客户信息,lazy属性无效
            join        proxy   使用表连接查询订单以及对应客户信息,lazy属性无效

    5、批量加载
        set
            batch-size  决定一次加载几个对象的集合数据,in 条件加载多个用户的订单

    2.1、类级别加载策略

    示例代码如下:演示类级别加载策略--懒加载

    package com.itheima.b_lazy;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Customer;

    import com.itheima.utils.HibernateUtils;

    public class Demo1 {
        @Test
        // 演示类级别加载策略--懒加载
        // load方法
        // class的lazy属性
        // 默认值是:true   load获得时,会返回一个代理对象,当使用代理对象的属性时,才去查询数据库。
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.load(Customer.class, 1);

            System.out.println(c.getCname());

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

        @Test
        // 演示类级别加载策略--懒加载
        // load方法
        // class的lazy属性
        // 属性值改为:false load方法一执行就会发送sql语句,与get方法一致。
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.load(Customer.class, 1);

            System.out.println(c.getCname());

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

    2.2、关联级别加载策略

    2.2.1、一对多:根据客户去查找订单

    一对多加载策略,也叫集合的检索策略
    示例代码如下:

    package com.itheima.b_lazy;

    import java.util.List;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.domain.Order;
    import com.itheima.utils.HibernateUtils;

    // 一对多(集合的检索策略):根据客户去查找订单
    // 特别注意:本示例代码中,为了简化演示,共有1个客户:表示客户数 >= 1 才可以演示; 共有2个客户:表示客户数 >= 2 才可以演示。
    public class Demo2 {
        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 true 时,
        // 结果:(默认值为true)即对集合使用赖加载,即与客户关联的数据,在使用时才会被加载。
        // 演示前提:一对多,1个客户有2个订单,共有1个客户
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class, 1); // 1条 select 语句,查询出1个客户

            for (Order o : c.getOrders()) { 
                System.out.println(o.getOname()); // 1条 select 语句,查询出对应客户的订单
            }

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

        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 false 时,
        // 结果:集合将会被立即加载
        // 演示环境:一对多,1个客户有2个订单,共有1个客户
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class, 1); // 2条 select 语句,查询出客户以及查询出客户对应的订单

            for (Order o : c.getOrders()) {
                System.out.println(o.getOname());
            }

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

        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 false/true/extra 时
        //      fetch 的值为 join 时
        // 结果:因为查询集合时使用表连接语句查询,所以会立刻加载集合数据,lazy的属性失效
        // 演示环境:一对多,1个客户有2个订单,共有1个客户
        public void fun3() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class, 1); // 1条 表连接查询 语句(左外连接查询)

            for (Order o : c.getOrders()) {
                System.out.println(o.getOname());
            }

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

        @SuppressWarnings("unchecked")
        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 true 时
        //      fetch 的值为 subselect 时,注意:该属性的值,在一次加载多个客户的订单数据的情况下才有效
        // 结果:会在使用集合(订单)时才加载,使用子查询语句查询集合(订单)数据
        // 演示环境:一对多,1个客户有2个订单,共有2个客户
        public void fun4() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            List<Customer> list = session.createQuery("from Customer").list(); // 1条 select 语句,查询出2个客户

            for (Customer c : list) { 
                System.out.println(c.getCname() + "下单数量:" + c.getOrders().size()); // 1条 子查询 语句,查询出2个客户各自所有的订单
            }

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

        @SuppressWarnings("unchecked")
        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 false 时
        //      fetch 的值为 subselect 时,注意:该属性的值,在一次加载多个客户的订单数据的情况下才有效
        // 结果:会在查询客户时,立即使用子查询语句加载客户的订单数据
        // 演示前提:一对多,1个客户有2个订单,共有2个客户
        public void fun5() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            List<Customer> list = session.createQuery("from Customer").list(); // 1条 select 语句,查询出2个客户   + 1条 子查询 语句,查询出2个客户各自所有的订单

            for (Customer c : list) {
                System.out.println(c.getCname() + "下单数量:" + c.getOrders().size());
            }

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

        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 extra 时
        //      fetch 的值为 select 时
        // 结果:会在使用集合(订单)时才加载,普通select语句,如果你使用集合只是为了获得集合的长度,则Hibernate只会发送count语句查询集合长度
        // 演示环境:一对多,1个顾客有2个订单,共有1个顾客
        public void fun6() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 查询Customer
            Customer c = (Customer) session.get(Customer.class, 1); // 1条 select 语句,查询出1个客户
            // 查询Customer下的订单数量
            System.out.println(c.getOrders().size()); // 1条 select count() 语句,查询出集合的长度

            // 真正使用订单中的数据
            for (Order o : c.getOrders()) { 
                System.out.println(o.getOname()); // 1条 select 语句,查询出对应客户的订单数据
            }

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

        @SuppressWarnings("unchecked")
        @Test
        // 演示关联级别加载策略
        // <set 
        //      lazy 的值为 extra 时
        //      fetch 的值为 subselect 时,注意:该属性的值,在一次加载多个客户的订单数据的情况下才有效
        // 结果:会在使用集合(订单)时才加载,子查询语句,如果你使用集合只是为了获取集合的长度,则Hibernate只会发送count语句查询集合长度
        // 演示环境:一对多,1个客户有2个订单,共有2个客户
        public void fun7() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            List<Customer> list = session.createQuery("from Customer").list(); // 1条 select 语句,查询出2个客户

            for (Customer c : list) { // 遍历Customer,获取每个Customer的订单数量
                System.out.println(c.getCname() + "下单数量:" + c.getOrders().size()); // 1条 select count 语句,查询出对应客户的订单的长度
            }                                                                       // 1条 select count 语句,查询出对应客户的订单的长度

            for (Customer c : list) {
                for (Order o : c.getOrders()) { // 遍历每个订单的具体数据
                    System.out.println(c.getCname() + "下单名称:" + o.getOname()); // 1条 子查询语句,查询出订单的详细信息
                }
            }

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

    2.2.2、多对一:根据订单去查找客户

    示例代码如下:

    package com.itheima.b_lazy;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Order;
    import com.itheima.utils.HibernateUtils;

    // 多对一 检索策略:根据订单去查找客户
    public class Demo3 {
        @Test
        // Order <many-to-one
        //      fetch: select
        //      lazy: false
        // 结果:加载订单时,会立即加载客户数据,使用普通select语句加载客户数据
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();


            Order o = (Order) session.get(Order.class, 2);  // 2条 select 语句
            System.out.println(o.getCustomer().getCname());

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

        @Test
        // Order <many-to-one
        //      fetch: select
        //      lazy: proxy
        // Customer <class
        //      lazy: false 
        // 结果:结果同fun1()
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Order o = (Order) session.get(Order.class, 2);  // 2条 select 语句
            System.out.println(o.getCustomer().getCname());

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

        @Test
        // Order <many-to-one
        //      fetch: select
        //      lazy: proxy
        // Customer <class
        //      lazy: true
        // 结果:加载订单时,先不加载客户数据,使用客户数据时才加载客户数据
        public void fun3() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Order o = (Order) session.get(Order.class, 2);  // 1条 select 语句 
            System.out.println(o.getCustomer().getCname()); // 1条 select 语句 

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

        @Test
        // Order <many-to-one
        //      fetch: join
        //      lazy: proxy|false   该属性失效
        // 结果:使用表连接查询订单以及对应客户信息,lazy属性无效
        public void fun4() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Order o = (Order) session.get(Order.class, 2); // 1条 表连接查询 语句(左外连接查询)
            System.out.println(o.getCustomer().getCname());

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

    2.2.3、批量加载策略

    示例代码如下:

    package com.itheima.b_lazy;

    import java.util.List;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.utils.HibernateUtils;

    // 演示:批量加载策略
    public class Demo4 {
        @SuppressWarnings("unchecked")
        @Test
        // 查询所有客户
        // 遍历客户,打印客户下的订单信息
        // 演示环境:一对多,1个客户有2个订单,共有2个客户
        // <set
        //      batch-size="1"
        // 结果:一次加载1个对象(客户)的集合数据
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            List<Customer> list = session.createQuery("from Customer").list(); // 1条 select 语句,查询出2个客户

            for (Customer c : list) {
                System.out.println(c.getOrders().size()); // 1条 select 语句,查询出第一个客户的所有的订单信息
            }                                             // 1条 select 语句,查询出第二个客户的所有的订单信息

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

        @SuppressWarnings("unchecked")
        @Test
        // 批量加载策略
        // <set
        //      batch-size="2"
        // 结果:一次加载2个对象(客户)的集合数据
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            List<Customer> list = session.createQuery("from Customer").list(); // 1条 select 语句,查询出2个客户

            for (Customer c : list) {
                System.out.println(c.getOrders().size()); // 1条 select ... in ... 语句,就查询出2个客户的所有订单信息
            }                                            

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

    2.2.4、检索总结

    如下表所示:

    检索策略优点缺点优先考虑使用的场合
    立即检索 对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象。即使用关联对象的属性很方便。 (1)select语句多,检索效率低。
    (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。
    (1)类级别检索中使用。
    (2)应用程序需要立即访问的对象中使用。
    (3)使用了二级缓存的情况下。
    延迟检索
    (懒加载/检索)
    (1)由应用程序决定需要加载哪些对象,可以避免执行多余的select语句。
    (2)避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。
    应用程序如果希望访问游离状态的代理类实例,必须保证它在持久化状态时已经被初始化。 (1)一对多或者多对多关联检索中使用。
    (2)应用程序不需要立即访问或者根本不会访问的对象延时检索使用。
    要特别注意代理对象的问题。
    开发中常见这种问题!
    表连接检索
    (用的比较少,不灵活)
    (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。
    (2)使用了外连接,select语句少。
    (1)可能会加载应用程序不需要访问的对象,浪费内存。
    (2)复杂的数据库表连接也会影响检索性能。
    (1)多对一或一对一关联检索中使用
    (2)需要立即访问的对象
    (3)数据库有良好的表连接性能。

    类级别加载策略:
      get/load
        get:立即查询数据库,将数据初始化。
        load:hbm文件中,class元素的lazy属性决定该方法的类级别加载策略,默认值是true。
          true:先返回一个代理对象,使用代理对象的属性时,才去查询数据库。
          false:load方法一执行就会发送sql语句,与get一致,会立即加载数据。

    由以上的类级别加载策略可知,代理对象在Session关闭之后可能会取不到值,该如何解决呢?

    • 法一:配置layz="false" ,集合将会被立即加载,此法不好,当数据量级很大时,比如:银行项目、三大运营商项目,服务器就要疯了。
    • 法二:因为是在Service层调用的Dao层的方法,那么我们在Service层就知道以后我们在页面上要用的数据,所以我们在Seesion关闭之前,在Service层先getXxx()在页面要上要用到的属性(数据),即在Service层中确保数据已经加载进来了。

    三、查询方式总结

        Hibernate查询分类:
            1get/load          根据OID检索
            2、对象导航图检索   c.getOrders();
            3、Sql语句         createSqlQuery
            4、Hql语句         createQuery
            5、Criteria查询    createCriteria 
            --------------------------------------------------    
            1.通过OID检索(查询)
                get(int);           立即、如果没有数据返回null
                load(Class, int);   延迟,如果没有数据抛异常。

            2.导航对象图检索方式(关联查询)
                customer.getOrders();

            3.Sql: Structured Query Language    结构化查询语言,原始Sql语句查询
                SQLQuery sqlQuery = session.createSQLQuery("sql语句"); // 查询的是:表,表字段(列)
                sqlQuery.list();            查询所有
                sqlQuery.uniqueResult();    查询一个

            4.HQL: Hibernate Query Language     Hibernate查询语言
                Query query = session.createQuery("hql语句"); // 查询的是:对象,对象属性

            5.QBC: Query By Criteria            条件查询,纯面向对象查询语言
                Criteria criteria = session.createCriteria(Class);  

    四、HQL【掌握】

    4.1、HQL介绍

    • HQL(Hibernate Query Language:Hibernate查询语言)是描述对象操作的查询语言,是Hibernate所特有。
    • 与SQL语法基本一致,不同的是HQL是面向对象的查询,查询的是对象和对象中的属性。
    • HQL的关键字不区分大小写,但是类名和属性名区分大小写。

    HQL语法示例:

    关键字说明
    select 别名/属性名/表达式
    from 实体类名 as 别名 (as可以省略哦)
    where 过滤条件
    group by 分组条件
    having 分组后的结果的过滤条件
    order by 排序条件

    4.2、HQL查询所有客户

    示例代码如下:

        @Test
        // HQL查询所有客户
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 下面五种语句等效
            // Query query = session.createQuery("from Customer"); // 使用简单类名
            // Query query = session.createQuery("from Customer c"); // 使用简单类名+别名
            // Query query = session.createQuery("select c from Customer c");
            // Query query = session.createQuery("select c from Customer as c");
            Query query = session.createQuery("select c from com.itheima.domain.Customer as c"); // 使用全限定类名

            List<Customer> list = query.list();
            System.out.println(list);

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

    4.3、HQL选择查询

    示例代码如下:

        @Test
        // HQL选择查询对象的某几个属性
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 注意:点后面是属性名称,因为HQL已经不跟数据库有关系了,而是跟对象有关系了,HQL是面向对象语言
            // Query query = session.createQuery("select c.cid from Customer c"); 
            Query query = session.createQuery("select c.cid, c.cname from Customer c"); 

            // 如果单列 ,select c.cname from ,需要List<Object>
            // 如果多列,select c.cid, c.cname from ,需要List<Object[]>,list存放每行,Object[]多列
            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

    4.4、HQL投影查询(部分)

    示例代码如下:

        @Test
        // HQL投影查询:在选择查询的基础上,把查询结果封装到对象中
        public void fun3() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 注意:点后面是属性名称,因为HQL已经不跟数据库有关系了,而是跟对象有关系了,HQL是面向对象语言
            // Query query = session.createQuery("select c.cname from Customer c");
            Query query = session.createQuery("select new Customer(c.cid, c.cname) from Customer c"); // 注意:Customer必须提供相应的构造方法。

            List<Customer> list = query.list();
            System.out.println(list);

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

    4.5、HQL排序

    示例代码如下:

        @Test
        // HQL排序
        public void fun5() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // Query query = session.createQuery("from Customer c order by c.cid desc"); // desc 降序:由大到小
            Query query = session.createQuery("from Customer c order by c.cid asc "); // asc 升序:由小到大

            List<Customer> list = query.list();
            System.out.println(list);

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

    4.6、HQL分页

    示例代码如下:

        @Test
        // HQL分页
        public void fun6() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c order by c.cid asc");

            // 分页思路:                第一个问号           第二个问号
            // limit ?,?    =>  setFirstResult  setMaxResults
            // setFirstResult = (当前页数-1)*每页最大记录数        当前页数是从1开始的。

            // 0,1  表示:从数据库表的第一行开始,一行为一页
            // 1,1  表示:从数据库表的第二行开始,一行为一页
            // 2,1  表示:从数据库表的第三行开始,一行为一页
            // 2,2  表示:从数据库表的第三行开始,两行为一页


            query.setFirstResult(1);    // 从数据库表的哪个索引(数据库表的行号=索引+1)开始取数据,包裹索引本身的记录,索引是从0开始的,角标是从1开始。
            query.setMaxResults(2);     // 一页查询出多少条数据

            List<Customer> list = query.list();
            System.out.println(list);

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

    4.7、HQL绑定参数

    示例代码如下:

        @Test
        // HQL绑定参数
        public void fun7() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 方式一:需要考虑索引的变化
            // Query query = session.createQuery("from Customer c where c.cid=?");
            // 第一个参数:问号作为占位符的索引 ,第一个问号的索引为0 
            // query.setInteger(0,2);


            // 方式二:不需要考虑索引的变化
            Query query = session.createQuery("from Customer c where c.cid=:haha");
            // 第一个参数:绑定实际参数作为占位符的名称
            query.setInteger("haha"2);

            Customer c = (Customer) query.uniqueResult();
            System.out.println(c);

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

    4.8、聚合函数和分组

    示例代码如下:

        @Test
        // HQL聚合函数
        public void fun8() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // Query query = session.createQuery("select count(*) from Customer c");
            // Query query = session.createQuery("select avg(c.cid) from Customer c");
            // Query query = session.createQuery("select sum(c.cid) from Customer c");
            // Query query = session.createQuery("select max(c.cid) from Customer c");
            Query query = session.createQuery("select min(c.cid) from Customer c");

            Object count = query.uniqueResult();
            System.out.println(count);

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

        @Test
        // HQL分组
        // group by ... having ...
        public void fun9() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 从订单表中,我查询满足如下条件的客户和客户的订单数量:把订单通过客户进行分组后,统计订单的数量且订单的数量大于2的 。
            Query query = session.createQuery("select o.customer, count(o) " + " from Order o " + " group by o.customer " + " having count(o)> 2");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

    4.9、HQL连接查询

    示例代码如下:

    package com.itheima.c_hql;

    import java.util.Arrays;
    import java.util.List;

    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.utils.HibernateUtils;
    // 演示:HQL详解_02 表连接

    // 内连接        =>
    // 左外连接    =>
    // 右外连接    =>

    @SuppressWarnings("unchecked")
    public class Demo2 {
        @Test
        // 交叉连   => 笛卡尔积
        // 开发时要避免出现笛卡尔积
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c,Order o");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

        @Test
        // 内连接
        // 隐式内连接    =>  在笛卡尔积基础上过滤掉无效数据
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c, Order o where o.customer=c");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

        @Test
        // 内连接
        // 显式内连接(非迫切)   =>  inner join
        // List<Object[]>
        //      Object[] => [Customer, Order]
        // 非迫切连接会将父与子对象装入数组中分别返回。
        public void fun3() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c inner join c.orders");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

        @Test
        // 内连接
        // 显式内连接(迫切)    =>  inner join fetch
        // List<Customer>
        // 迫切连接会将子装入父中,组装成一个对象再返回。
        public void fun4() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c inner join fetch c.orders");

            List<Object> list = query.list();
            for (Object obj : list) {
                System.out.println(obj);
            }

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

        @Test
        // 左外连接(非迫切)
        // left [outer] join
        public void fun5() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c left outer join c.orders");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

        @Test
        // 左外连接 (迫切)
        // left [outer] join fetch
        public void fun6() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c left outer join fetch c.orders");

            List<Object> list = query.list();
            for (Object obj : list) {
                System.out.println(obj);
            }

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

        @Test
        // 右外连接(非迫切)
        // right [outer] join
        public void fun7() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c right outer join c.orders");

            List<Object[]> list = query.list();
            for (Object[] objs : list) {
                System.out.println(Arrays.toString(objs));
            }

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

        @Test
        // 右外连接 (迫切)
        // right [outer] join fetch
        public void fun8() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Query query = session.createQuery("from Customer c right outer join fetch c.orders");

            List<Object> list = query.list();
            for (Object obj : list) {
                System.out.println(obj);
            }

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

    4.10、HQL命名查询

    思想:将HQL从java源码中,提取到配置文件中。
    分类:全局、局部
    配置:
      全局


      局部

    获得:
      全局:
        session.getNamedQuery("queryName");
      局部:
        session.getNamedQuery("className.queryName"); // 需要使用类的全限定名称
    示例代码如下:
    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  package="com.itheima.domain">
        <class name="Customer" table="t_customer" lazy="false">
            <id name="cid" column="cid">
                <generator class="native"></generator>
            </id>
            <property name="cname" column="cname" type="string"></property>

            <!-- 一对多:一个客户当前客户 拥有 多个订单 -->
            <set name="orders" batch-size="2">
                <key column="customer_id"></key>
                <one-to-many class="Order"/>
            </set>

            <!-- 配置局部的HQL语句 -->
            <query name="def"><![CDATA[from Order]]></query>
        </class>

        <!-- 配置全局的HQL语句 -->
        <query name="abc"><![CDATA[from Customer]]></query>
    </hibernate-mapping>

    测试代码如下:

    package com.itheima.c_hql;

    import java.util.List;

    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.utils.HibernateUtils;

    // 演示:HQL详解_03 命名查询

    // 思想:将HQL从java源码中,提取到配置文件中。
    // 分类:全局、局部
    @SuppressWarnings("unchecked")
    public class Demo3 {
        @Test
        // 找到全局配置的HQL语句
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 从com.itheima.domain.Customer 的 hbm文件中找
            Query query = session.getNamedQuery("abc");

            List<Object> list = query.list();
            System.out.println(list);

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

        @Test
        // 找到局部配置的HQL语句
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 从com.itheima.domain.Customer 的 hbm文件中找
            Query query = session.getNamedQuery("com.itheima.domain.Customer.def");

            List<Object> list = query.list();
            System.out.println(list);

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

    五、QBC【了解】

    5.1、QBC查询

      QBC:Query By Criteria 条件查询(面向对象的查询的方式),不支持连接查询,开发中不常用。

    5.2、QBC简单查询

      示例代码如下:(往下看)

    5.3、QBC分页查询

      示例代码如下:(往下看)

    5.4、QBC排序查询

      示例代码如下:(往下看)

    5.5、QBC条件查询

    5.2~5.5示例代码如下:

    package com.itheima.d_qbc;

    import java.util.List;

    import org.hibernate.Criteria;
    import org.hibernate.Session;
    import org.hibernate.criterion.Restrictions;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.domain.Order;
    import com.itheima.utils.HibernateUtils;

    // 演示:QBC详解
    @SuppressWarnings("unchecked")
    public class Demo1 {

        @Test
        // QBC简单查询
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

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

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

        @Test
        // QBC分页查询
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Criteria criteria = session.createCriteria(Order.class);
            criteria.setFirstResult(2);
            criteria.setMaxResults(2);

            List<Order> list = criteria.list();
            System.out.println(list);

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

        @Test
        // QBC排序查询
        public void fun3() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Criteria criteria = session.createCriteria(Customer.class);
            // criteria.addOrder(org.hibernate.criterion.Order.asc("cid")); // asc 升序:由小到大
            criteria.addOrder(org.hibernate.criterion.Order.desc("cid")); // desc 降序:由大到小

            List<Customer> list = criteria.list();
            System.out.println(list);

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

        @Test
        // QBC条件查询
        public void fun5() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 按名称查询
            // Criteria criteria = session.createCriteria(Customer.class);
            // criteria.add(Restrictions.eq("cname", "李晓艺")); 
            // List<Customer> list = criteria.list();
            // System.out.println(list); 

            // 模糊查询
            // Criteria criteria = session.createCriteria(Customer.class);
            // criteria.add(Restrictions.like("cname", "子%")); 
            // List<Customer> list = criteria.list();
            // System.out.println(list); 

            // 条件并列查询
            Criteria criteria = session.createCriteria(Customer.class);
            criteria.add(Restrictions.like("cname""子%"));
            criteria.add(Restrictions.ge("cid"4));
            List<Customer> list = criteria.list();
            System.out.println(list);

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

    5.6、离线查询(了解)

    • DetachedCriteria 离线查询对象
      不需要使用Session就可以拼凑查询条件。一般使用在web层或service层拼凑。将此对象传递给dao层,此时将与Session进行绑定,执行查询。
    • 离线查询条件与QBC一样的。

    示例代码如下:

    package com.itheima.d_qbc;

    import java.util.List;

    import org.hibernate.Criteria;
    import org.hibernate.Session;
    import org.hibernate.criterion.DetachedCriteria;
    import org.hibernate.criterion.Restrictions;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.utils.HibernateUtils;

    // 演示:QBC详解
    @SuppressWarnings("unchecked")
    public class Demo2 {

        @Test
        // QBC离线查询
        public void fun1() {
            // Web层或者Service层,DetachedCriteria 离线查询对象
            DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
            dc.add(Restrictions.eq("cid"2));

            // Dao层
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            // 离线 Criteria 与 Session 进行绑定,执行查询
            Criteria c = dc.getExecutableCriteria(session);
            List<Customer> list = c.list();
            System.out.println(list);

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

    六、连接池、事务、锁 相关常见设置

    6.1、整合c3p0(连接池) (了解)

    步骤如下:
    步骤一:导入c3p0 jar包,并添加至构建路径


    步骤二:hibernate.cfg.xml 进行配置,先在Hibernate.properties中找到对应的键和值,如下图所示:

    代码如下:
    hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider

    配置截图如下:


    步骤三:c3p0具体配置参数

    代码如下:
    ###########################
    ### C3P0 Connection Pool###
    ###########################

    #hibernate.c3p0.max_size 2
    #hibernate.c3p0.min_size 2
    #hibernate.c3p0.timeout 5000
    #hibernate.c3p0.max_statements 100
    #hibernate.c3p0.idle_test_period 3000
    #hibernate.c3p0.acquire_increment 2
    #hibernate.c3p0.validate false

    配置截图如下:


    示例代码如下:
    package com.itheima.e_api;

    import org.hibernate.Session;

    import org.junit.Test;

    import com.itheima.utils.HibernateUtils;

    public class Demo1 {
        @Test
        // 验证c3p0连接池
        public void fun1(){
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            System.out.println(session);

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

    控制台运行结果如下图所示:

    6.2、事务

    6.2.1、回顾之前学的知识

    • 事务:一组业务操作,要么全部成功,要么全部不成功。
    • 特性:ACID
      原子性:整体(被事务包裹的sql语句,看作是一个整体,要么都成功,要么都失败:同进退)
      一致性:数据(数据在事务的操作过程中,数据的总量是不能变化的:转账前和转账后的总金额不变)
      隔离性:并发(在并发(多线程)的情况下,事务与事务之间不受干扰)
      持久性:结果(指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响)
    • 隔离问题:
      脏读:一个事务读到另一个事务未提交的内容。(读到的是假的数据)
      不可重复读:一个事务读到另一个事务已提交的内容(update,每次读到的结果不一样)
      虚读(幻读):一个事务读到另一个事务已提交的内容(insert,读到的数据变多了)
    • 使用隔离级别,解决问题
      1、read uncommittd,读未提交。存在3个问题。赃读、不可重复读、虚读都有可能发生。
      2、read committed,读已提交。解决:脏读。存在2个问题。不可重复读、虚读都有可能发生。(oracle默认的)
      4、repeatable read,可重复读。解决:脏读、不可重复读。存在1个问题。虚读有可能发生。(mysql默认的)
      8、serializable,串行化。单事务。没有问题。

    6.2.2、hibernate设置隔离级别

    • 在hibernate.cfg.xml 配置
      hibernate.connection.isolation 4

    6.2.3、数据库中的锁

    悲观锁(数据库提供实现):默认认为别人一定会要修改我使用的数据,那我就可以为我读取的数据加锁。
      读锁/共享锁 => 读锁可被其他线程所共享,如果是读取的话大家都可以用这把锁读到数据。
        select * from t_Customer lock in share mode;(读锁、共享锁)
      写锁/排他锁 => 写锁不能共享,只要有人为数据加入了写锁,其他人就不能为数据加任何锁。
        select * from t_Customer for update; (写锁、排它锁)

    • 悲观锁:丢失更新肯定会发生。
      如下图所示:

    乐观锁(需要自己实现,但Hibernate帮我们实现了,我们只需配置一下就可以使用了):

    • 乐观锁:丢失更新肯定不会发生。
      在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。
      如下图所示:

    悲观锁测试代码如下:

    package com.itheima.e_api;

    import org.hibernate.LockOptions;
    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.utils.HibernateUtils;

    public class Demo2 {
        @Test
        // 悲观锁
        // 写锁
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class1, LockOptions.UPGRADE);
            System.out.println(c);

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

        @Test
        // 悲观锁
        // 读锁
        public void fun2() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class1, LockOptions.READ);
            System.out.println(c);

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

    乐观锁示例代码:
    在PO对象(JavaBean)中提供字段,表示版本字段。一般为Integer。


    在Custmoer.hbm.xml 文件配置

    配置截图如下:

    乐观锁测试代码如下:
    package com.itheima.e_api;

    import org.hibernate.Session;
    import org.junit.Test;

    import com.itheima.domain.Customer;
    import com.itheima.utils.HibernateUtils;

    public class Demo3 {
        @Test
        // 乐观锁
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();

            Customer c = (Customer) session.get(Customer.class1);
            c.setCname("lucy");
            System.out.println(c);

            session.getTransaction().commit();
            session.close();
        }
    }
  • 相关阅读:
    十年经验大牛浅谈自动化测试与测试用例的编写
    从功能测试到自动化测试,携程大牛总结一些工作经验分享
    阿里大牛谈软件测试面试的几个建议
    记一个Selenium自动化测试网页
    腾讯大牛教你简单的自动化测试模型(Python+Selenium)
    阿里大牛教你基于Python的 Selenium自动化测试示例解析
    携程大牛谈自动化测试里的数据驱动和关键字驱动思路的理解
    论:关于自动化测试的前期发展历史及未来发展趋势
    阿里大牛教你一分钟了解自动化测试
    绑定银行卡的一些细节
  • 原文地址:https://www.cnblogs.com/chenmingjun/p/9304548.html
Copyright © 2011-2022 走看看