Hibernate 技术(三)

Hibernate的多对多关联关系

多对多关联关系在java对象中可以通过定义集合类型来实现关联关系。 

在关系数据模型中,无法直接表达表和表之间的多对多关联关系,而是需要创建一个中间表包含了两边的主键,来表达两张表的多对多关联关系。

   
   
  • 1
  • 2
  • 3
  • 4

我们用一个Student和Course(学生和课程)的例子来演示多对多关联关系。


    第一步:创建Student和Course类  

    public class Student {
            private Integer id;
            private String name; 
            //用一个集合包含该学生所选的课程对象
            private Set<Course> courses=new HashSet<Course>();
            public Integer getId() {
                return id;
            }
            public void setId(Integer id) {
                this.id = id;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public Set<Course> getCourses() {
                return courses;
            }
            public void setCourses(Set<Course> courses) {
                this.courses = courses;
            }

        }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
        public class Course {
                    private Integer id;
                    private String name; 
                    //用一个集合包含所有选择该课程的学生
                    private Set<Student> students=new HashSet<Student>();
                    public Integer getId() {
                        return id;
                    }
                    public void setId(Integer id) {
                        this.id = id;
                    }
                    public String getName() {
                        return name;
                    }
                    public void setName(String name) {
                        this.name = name;
                    }
                    public Set<Student> getStudents() {
                        return students;
                    }
                    public void setStudents(Set<Student> students) {
                        this.students = students;
                    }

            } 
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
     第二步:编写我们的映射文件  

        编写Course.hbm.xml
        <hibernate-mapping >
            <class name="com.cad.domain.Course" table="course">
                <id name="id" column="id">
                    <generator class="native"></generator>
                </id>
                <property name="name" column="name"></property>
                <!--table属性用来指定生成的中间表的表名称  inverse指定关联关系由Student维护-->
                <set name="students" table="student_course" inverse="true">
                    <key column="cid"></key>
                    <!--<many-to-many>元素中的column属性指定本表通过中间表中的sid外键关联到Student对象-->
                    <many-to-many class="com.cad.domain.Student" column="sid"></many-to-many>
                </set>
            </class>
         </hibernate-mapping>  


   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
         编写Student.hbm.xml 
            <hibernate-mapping >
                <class name="com.cad.domain.Student" table="student">
                    <id name="id" column="id">
                        <generator class="native"></generator>
                    </id>
                    <property name="name" column="name"></property>
                    <!-进行级联保存和更新操作-->
                    <set name="courses" table="student_course" cascade="save-update">
                        <key column="sid"></key>
                        <many-to-many class="com.cad.domain.Course" column="cid"></many-to-many>
                    </set>
                </class>
             </hibernate-mapping>  
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
         我们来测试一下 

            public class Demo {
                @Test
                public void fun(){
                    //读取配置文件
                    Configuration conf=new Configuration().configure();

                    //根据配置创建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    Session session = sessionfactory.openSession(); 
                     Transaction ts=session.beginTransaction(); 
                    //创建两个Student
                    Student s1=new Student();
                    s1.setName("tom");
                    Student s2=new Student();
                    s2.setName("jack");

                    //创建三个Course
                    Course c1=new Course();
                    c1.setName("语文");
                    Course c2=new Course();
                    c2.setName("数学");
                    Course c3=new Course();
                    c3.setName("英语");

                    //因为设置了关联关系由Student维护,所以不需要课程再来关联Student
                    s1.getCourses().add(c1);
                    s1.getCourses().add(c2);
                    s1.getCourses().add(c3); 

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

                    //由于设置了级联保存,所以只保存Student即可
                    session.save(s1);
                    session.save(s2);

                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }
            } 


    结果,数据库中生成了三张表。
    student和course
    还有中间表student_course
    里面的数据也相互对应。
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

Hibernate的检索策略

简介

Session的缓存中可以存放相互关联的对象。当Hibernate从数据库中加载对象时,如果同时自动加载与之关联的所有对象,那么这些关联的对象就浪费了很多的内存空间。而我们可以设置检索策略,来优化检索性能。

Hibernate提供了三种检索策略

        -立即检索策略:检索对象时立即加载对象以及关联的对象。 

        -延迟检索策略:使用时才会加载对象以及关联的对象。能避免多余加载不需要的对象。 

        -迫切左外连接检索策略 :利用SQL的外连接查询功能,能够减少select语句的数目。 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里写图片描述

类级别检索策略

定义:只加载某个类对象

类级别的检索策略包括立即检索和延迟检索。默认为延迟检索。

Hibernate中允许在映射文件中配置检索策略。
类级别检索策略,可以配置<class>元素中的lazy属性。属性可选值:true(延迟检索) false(立即检索)。默认值为true。 

类级别检索策略会影响Session的load方法。 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
    我们来做一个小例子,在load方法加断点
        public class Demo {
            private Session session;

            @Test
            public void test() {
                //读取配置文件
                Configuration conf=new Configuration().configure();

                //根据配置创建factory
                SessionFactory sessionfactory=conf.buildSessionFactory();
                session = sessionfactory.openSession(); 
                 Transaction ts=session.beginTransaction(); 

                 Customer c=session.load(Customer.class, 7);//此处加断点来观察效果
                 System.out.println(c.getName()); 

                ts.commit();
                session.close();
                sessionfactory.close();
            }

        }

        当lazy属性为true时,调用load方法什么都不输出,使用对象时才输出select语句。
        当lazy属性为false时,调用load方法会直接输出select语句。
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

原理:

当为延迟检索策略时,执行load方法时,是返回当前对象的代理对象,这个代理对象由Hibernate动态生成,代理对象继承了当前对象,仅仅初始化了OID属性,其他属性都为null,当程序访问代理类属性时,Hibernate才会初始化代理对象,初始化过程执行select语句,从数据库加载所有数据。

一对多关联级别的策略策略

关联级别检索策略:查询有关联关系的对象时,加载对象时是否需要将关联的对象加载。

在映射文件中,使用< set >元素来配置一对多或者多对多的关联关系。

< set >元素中有lazy和fetch属性决定加载策略。

lazy属性:和类级别加载策略一致。
         默认值为true。即使用延迟检索。 

         当lazyfalse时,使用立即检索。 

         当lazy为extra时,采用加强延迟检索策略,它尽可能的延迟集合被初始化的时机。例如程序访问集合的一些属性时,如访问size()、contains()、isEmpty()这些方法时,如果指定了extra,就不会加载集合,仅通过特定的语句查询必要的信息。 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
        例子 :
            public class Demo {
                private Session session;

                @Test
                public void test() {
                    //读取配置文件
                    Configuration conf=new Configuration().configure();

                    //根据配置创建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    session = sessionfactory.openSession(); 
                     Transaction ts=session.beginTransaction();  
                     //加载Customer对象
                     Customer c=(Customer)session.get(Customer.class, 7);
                     //加载Customer关联的Order对象
                     for(Order o:c.getOrders()){
                         System.out.println(o.getName());
                     }

                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }

            }

    当我们lazy属性为false时,在执行get方法时,会使用立即检索策略,会执行两个select语句,加载Customer和关联的Orders集合。 

    当我们lazy属性为true时,执行get方法,只会执行一句select语句,此时Customer的Orders属性引用一个没有被初始化的集合代理类,此时Orders集合中不存在任何Order对象,当我们使用order时,才会初始化集合代理类,才会到数据库中检索所有关联的Order对象

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
fetch属性:决定加载集合时查询语句的格式。取值为:select 、subselect、join 

          select:(默认值)使用普通的select语句进行查询。 

          subselect:使用子查询加载集合数据。当查找多个对象时才有用,例如查找多个用户,每个用户有多个订单 

          join:采用迫切左外连接检索策略进行查询。fetch属性设为join,lazy属性将被忽略,在查询对象时一定会加载与之关联的对象。     

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
        例子:
        public class Demo {

                    private Session session;

                    @Test
                    public void test() {
                        //读取配置文件
                        Configuration conf=new Configuration().configure();

                        //根据配置创建factory
                        SessionFactory sessionfactory=conf.buildSessionFactory();
                        session = sessionfactory.openSession(); 
                         Transaction ts=session.beginTransaction(); 

                        //加载Customer对象
                         Customer c=(Customer)session.get(Customer.class, 7);

                         //加载关联的Order对象
                         for(Order o:c.getOrders()){
                             System.out.println(o.getName());
                         }

                        ts.commit();
                        session.close();
                        sessionfactory.close();
                    }

                } 
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
            当fetch属性为默认值(select)时,打印的sql语句
                    Hibernate: 
                        select
                            customer0_.id as id1_0_0_,
                            customer0_.name as name2_0_0_ 
                        from
                            customer customer0_ 
                        where
                            customer0_.id=? 
                    Hibernate: 
                        select
                            orders0_.cid as cid3_1_0_,
                            orders0_.id as id1_1_0_,
                            orders0_.id as id1_1_1_,
                            orders0_.name as name2_1_1_,
                            orders0_.cid as cid3_1_1_ 
                        from
                            orders orders0_ 
                        where
                            orders0_.cid=?
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
            当fetch属性为join时,打印select语句,采用左外连接的查询语句,减少select语句
                    Hibernate: 
                        select
                            customer0_.id as id1_0_0_,
                            customer0_.name as name2_0_0_,
                            orders1_.cid as cid3_1_1_,
                            orders1_.id as id1_1_1_,
                            orders1_.id as id1_1_2_,
                            orders1_.name as name2_1_2_,
                            orders1_.cid as cid3_1_2_ 
                        from
                            customer customer0_ 
                        left outer join
                            orders orders1_ 
                                on customer0_.id=orders1_.cid 
                        where
                            customer0_.id=?

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
    演示fetch属性为subselect时,我们查询所有Customer,然后使用每一个Customer的Orders集合
            public class Demo {

                private Session session;
                @Test
                public void test() {
                    //读取配置文件
                    Configuration conf=new Configuration().configure();

                    //根据配置创建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    session = sessionfactory.openSession(); 
                     Transaction ts=session.beginTransaction(); 

                     //查找所有Customer
                    List<Customer> customerlist=session.createQuery("from Customer").list();  
                    //加载每个Customer关联的Orders集合
                    for(Customer c:customerlist){
                        System.out.println(c.getOrders().size());
                    }
                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }

            }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
            打印的语句,使用了子查询语句 
            当fetch属性为subselect时,Hibernate能够通过带有子查询的select语句,来整批量的初始化多个对象的集合中的实例。    


                Hibernate: 
                    select
                        customer0_.id as id1_0_,
                        customer0_.name as name2_0_ 
                    from
                        customer customer0_
                Hibernate: 
                    select
                        orders0_.cid as cid3_1_1_,
                        orders0_.id as id1_1_1_,
                        orders0_.id as id1_1_0_,
                        orders0_.name as name2_1_0_,
                        orders0_.cid as cid3_1_0_ 
                    from
                        orders orders0_ 
                    where
                        orders0_.cid in (
                            select
                                customer0_.id 
                            from
                                customer customer0_
                        )
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

fetch和lazy结合

这里写图片描述

多对一关联级别的策略策略

<set>元素一样,<many-to-one>元素中也有lazy和fetch属性。

    lazy的可选值有false(立即检索),proxy(延迟检索),no-proxy(无代理延迟检索)
    fetch属性可选值为selectjoin  

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
        例子:设置订单配置文件中fetch属性为join
            public class Demo {

                private Session session;

                @Test
                public void test() {
                    //读取配置文件
                    Configuration conf=new Configuration().configure();

                    //根据配置创建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    session = sessionfactory.openSession();  
                     Transaction ts=session.beginTransaction(); 
                    //加载订单对象
                     Order c=(Order)session.get(Order.class, 6);
                     //加载关联的Customer对象
                     System.out.println(c.getCustomer().getName());


                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }

            }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
        则在执行get方法时,会直接使用外连接查询。忽略lazy。
                    Hibernate: 
                        select
                            order0_.id as id1_1_0_,
                            order0_.name as name2_1_0_,
                            order0_.cid as cid3_1_0_,
                            customer1_.id as id1_0_1_,
                            customer1_.name as name2_0_1_ 
                        from
                            orders order0_ 
                        left outer join
                            customer customer1_ 
                                on order0_.cid=customer1_.id 
                        where
                            order0_.id=?
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

批量检索

< class >和< set >元素中包含batch-size属性。设定批量检索的数量。可选值为正整数,默认为1。
仅用于关联级别的立即检索和批量检索。

            我们在Customer配置文件中设置批量延迟检索
            <set name="orders" lazy="true" batch-size="3"> 

            public class Demo {

                private Session session;

                @Test
                public void test() {
                    //读取配置文件
                    Configuration conf=new Configuration().configure();

                    //根据配置创建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    session = sessionfactory.openSession(); 
                     Transaction ts=session.beginTransaction(); 
                     //加载所有Customer对象
                    List<Customer> customerlist=session.createQuery("from Customer").list();
                    for(Customer c:customerlist){
                        System.out.println(c.getOrders().size());
                    }
                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }

            }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
    我们使用延迟检索,当使用到每个Customer的orders时,才加载Orders,每执行一次循环,就打印一个select语句。 

    如果有很多Customer对象,那么就会打印很多的select语句。 

    所以我们可以使用批量检索。只需要将<set>的batch-size属性设置为3.  

    所以会批量初始化三个Orders集合,使用in语句。这样当用到第一个第二个第三个对象的集合时,就不会再发送语句。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Hibernate的查询方式

(1)OID检索方式

 按照对象的OID来查询对象。Session的get/load方法提供了这种功能。 

 如果应用程序中事先知道了OID,就可以使用这种查询对象的方式。 

   
   
  • 1
  • 2
  • 3
  • 4

(2)导航对象图检索方式

根据已经加载的对象,导航到其他对象.

例如,对于已经加载的Customer对象,可以导航到所有关联的Order对象 

   
   
  • 1
  • 2
  • 3
  • 4

(3)HQL检索方式

简介:

使用面向对象的HQL查询语言。Hibernate提供了Query接口,它是专门的HQL查询接口,能执行各种复杂的HQL语句。

HQL是面向对象的查询语言,和SQL查询语言有些类似,Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。HQL封装了JDBC的细节

HQL和SQL本质是不一样的: 

    -HQL查询语言面向对象,Hibernate负责解析HQL语句,然后根据映射文件,把HQL语句翻译成SQL语句。HQL查询语句中主体是类和类的属性。 

    -SQL查询语言是和关系数据库绑定在一起的,SQL查询语句中主体是数据库表和表的字段。  

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

HQL查询的步骤
-通过session的createQuery(HQL语句)方法创建一个Query对象
-调用Query的list()方法执行查询语句。该方法返回List集合

(1)================= 查找所有对象 ====================

    public class Demo {

                    private Session session;

                    @Test
                    public void test() {
                        //读取配置文件
                        Configuration conf=new Configuration().configure();

                        //根据配置创建factory
                        SessionFactory sessionfactory=conf.buildSessionFactory();
                        session = sessionfactory.openSession(); 
                         Transaction ts=session.beginTransaction(); 
                         //HQL语句是面向对象的,from 类名 
                        List<Customer> customerlist=session.createQuery("from Customer").list();
                        System.out.println(customerlist);
                        ts.commit();
                        session.close();
                        sessionfactory.close();
                    }

                } 
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(2)================= HQL语句使用别名 ====================

例如:List< Customer > customerlist=session.createQuery(“from Customer as c”).list();

as关键字用来设置别名,as也可以省略。 from Customer c即可。

(3)================= HQL语句选择查询 ====================

        选择查询是指仅查找某些属性。
        这时list()返回的集合中包括的是一个一个数组,每个数组中是一条记录。 

        List <Object[]> customerlist=session.createQuery("select id,name from Customer").list(); 
        for(Object[] obj:customerlist){
            System.out.println(Arrays.toString(obj));
        }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(4)================= HQL语句投影查询 ====================

        基于选择查询的基础上,把查询到的属性封装到对象中,该对象的其他属性为null。
        该对象必须有对应的构造方法。

        List <Customer> customerlist=session.createQuery("select new Customer(id,name) from Customer").list();
        for(Customer c:customerlist){
            System.out.println(c.toString());
        }

        Customer类中必须有如下构造方法     
        public Customer(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(5)================= HQL对查询结果排序 ====================

    HQL语言使用order by关键字对结果进行排序。
    asc为升序,desc为降序。 

    List <Customer> customerlist=session.createQuery("select new Customer(id,name) from Customer order by id desc").list(); 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5

(6)================= HQL分页查询 ====================

        -setFirstResult(int index):设定从第几个对象开始检索,起始值为0。
        -setMaxResult(int count):设定一次检索的对象数目。默认情况下检索所有的对象。

            Query query=session.createQuery("select id,name from Customer");
            //从第一个对象开始
            query.setFirstResult(0);
            //查找两个对象
            query.setMaxResults(2);
            List result=query.list();
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(7)================= HQL语句中绑定参数 ====================

对于实际应用中,经常需要用户输入一些查询条件,我们返回符合查询条件的数据。
我们可以使用from Customer where name='"+name+"';来实现
但是这种方式是非常不安全的,会受到SQL注入等非法攻击。 

   
   
  • 1
  • 2
  • 3
  • 4
        Hibernate中使用参数绑定来避免以上问题。有两种方式 

        (1)使用?占位符绑定
                        占位符的索引从0开始。
                        Query query=session.createQuery("from Customer where id=?");
                        query.setInteger(0, 7);

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

                        Query提供了各种绑定数据类型的参数的方法setXxx(),如果参数为字符串类型,就调用setString(),如果参数为整数类型,就调用setInteger()等等。
                        这些setXxx()方法第一个参数代表占位符的索引,第二个代表参数值。

        (2) 使用参数名字绑定
                        在HQL中使用命名参数,命名参数以":"开头。
                        Query query=session.createQuery("from Customer where id=:a");
                        query.setInteger("a", 7);

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

                        这些setXxx()方法第一个参数是命名参数的名字,第二个参数是值。
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(8)================= HQL查询单个对象 ====================

        -uniqueResult()方法:返回单个对象  

                Query query=session.createQuery("from Customer where id=1");
                Customer c=(Customer) query.uniqueResult();
                System.out.println(c.toString());

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(9)================= HQL聚合函数和分组查询 ====================

        (1)HQL调用聚合函数
                -查询customer表所有记录数目
                            Query query=session.createQuery("select count(*) from Customer"); 
                            //该语句返回long类型
                            Long c=(Long) query.uniqueResult();
                            System.out.println(c.intValue());

                -查询最大ID
                            Query query=session.createQuery("select Max(id) from Customer");
                            Integer c=(Integer) query.uniqueResult();
                            System.out.println(c.intValue());


        (2)分组查询
                group by子句用来分组查询。

                -根据id分组,统计相同id的数目
                            Query query=session.createQuery("select id,count(*) from Customer group by id");
                            List<Object[]> list=query.list();
                            for(Object[] obj:list){
                                System.out.println(Arrays.toString(obj));
                            }

                -having子句用于为分组查询加上条件
                            Query query=session.createQuery("select id,count(*) from Customer group by id having id>8");
                            List<Object[]> list=query.list();
                            for(Object[] obj:list){
                                System.out.println(Arrays.toString(obj));
                            }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

(10)================= HQL连接查询 ====================

        -交叉连接:返回被连接的两个表所有数据行的笛卡儿积
        如果A表有5行记录,B表有7行记录,返回的结果就有35行记录
        显然,会产生很多没有意义的数据。

                Query query=session.createQuery("from Customer,Order");
                List<Object[]> list=query.list();
                for(Object[] obj:list){
                    System.out.println(Arrays.toString(obj));
                }

        -隐式内连接
            在交叉连接基础上,通过条件来过滤一些无意义的数据,达到内连接的效果。
                Query query=session.createQuery("from Customer c,Order o where o.customer=c");
                List<Object[]> list=query.list();
                for(Object[] obj:list){
                    System.out.println(Arrays.toString(obj));
                }

        -显式内连接
            使用inner join关键字表示内连接。inner可以省略,只使用join。
            调用list()方法返回的集合中存放的是每个元素对应的记录,每个元素都是数组类型。

                Query query=session.createQuery("from Customer c inner join c.orders"); 
                List<Object[]> list=query.list();
                for(Object[] obj:list){
                    System.out.println(Arrays.toString(obj));
                }


        -迫切内连接
            使用inner join fetch关键字表示迫切内连接。
            调用list()方法返回的集合中存放的是Customer对象的引用,每个Customer对象的Orders集合都被初始化,存放所有关联的Order对象。

                Query query=session.createQuery("from Customer c inner join  c.orders");
                List <Object> list=query.list();
                for(Object obj:list){
                    System.out.println(obj);
                } 

        -左外连接
            在连接查询中,连接左端的表中的所有的行全部显示,并且能在右端的表中找到匹配的行,如果右端表中没能找到左端匹配的行,则对应NULL.
            使用left join关键字表示左外连接。
            返回的list集合中存放的是多个对象数组。   
            Query query=session.createQuery("from Customer c left join  c.orders");
            List<Object[]> list=query.list();
            for(Object[] obj:list){
                System.out.println(Arrays.toString(obj));
            }

        -迫切左外连接
            使用left join fetch关键字表示迫切左外连接。
            list()方法返回的集合中存放的是Customer对象的引用,每个Customer的Orders集合都被初始化,存放关联的Order对象。
                Query query=session.createQuery("from Customer c left join fetch c.orders");
                List<Object> list=query.list();
                for(Object obj:list){
                    System.out.println(obj);
                }

        -右外连接
            和左外连接一样,连接右端表中的行全部显示,连接左端找到匹配的行,如果未能找到匹配的行,则用NULL代替
            使用right join关键字表示右外连接。
            Query query=session.createQuery("from Customer c right join  c.orders");
            List<Object[]> list=query.list();
            for(Object[] obj:list){
                System.out.println(Arrays.toString(obj));
            }

        -迫切右外连接
            Query query=session.createQuery("from Customer c right join fetch  c.orders");
            List<Object> list=query.list();
            for(Object obj:list){
                System.out.println(obj);
            }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

(11)================= 在映射文件中定义命名查询语句 ====================

前面的例子中,HQL查询语句都写在程序代码中。Hibernate允许在映射文件中定义字符串形式的查询语句。

            <class name="com.cad.domain.Customer" table="customer" >
                <id name="id" column="id">
                    <generator class="native"></generator>
                </id>
                <property name="name" column="name"></property>
                <set name="orders" batch-size="3" fetch="subselect">
                    <key column="cid"/>
                    <one-to-many class="com.cad.domain.Order" />    
                </set>
                <!--定义局部命名查询语句-->
                <query name="bcd"><![CDATA[from Order]]></query>
            </class>
            <!--定义全局命名查询语句-->
            <query name="abc"><![CDATA[from Customer]]></query>

            //调用全局的命名查询语句
            Query query=session.getNamedQuery("abc");
            //调用局部的命名查询语句
            Query query=session.getNamedQuery("com.cad.domain.Customer.bcd");
            List<Object> list=query.list();
            for(Object obj:list){
                System.out.println(obj);
            }



   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

(4)QBC检索方式

使用QBC(Query By Criteria)API检索对象。封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口。 

QBC检索方式的步骤
        -调用Session的createCriteria()方法创建一个Criteria对象
        -设定查询条件
        -调用Criteria接口的list()方法执行查询语句。返回List集合。 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(1)================= 查询所有对象 ====================

            //传递类名.class即可
            Criteria criteria=session.createCriteria(Customer.class);
            List<Customer> l=criteria.list();
             for(Customer c:l){
                 System.out.println(c);
             }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(2)================= QBC对查询结果进行排序 ====================

        QBC使用org.hibernate.criterion.order类对查询结果排序。
        asc为升序,desc为降序。 

         Criteria criteria=session.createCriteria(Customer.class);
         criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
         List<Customer> l=criteria.list();
         for(Customer c:l){
             System.out.println(c);
         }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(3)================= QBC分页查询 ====================

    QBC的分页查询和HQL类似。Criteria接口也提供了方法。
            -setFirstResult(int index):设定从第几个对象开始检索,起始值为0。
            -setMaxResult(int count):设定一次检索的对象数目。默认情况下检索所有的对象。

                 Criteria criteria=session.createCriteria(Customer.class);
                 criteria.setFirstResult(0);
                 criteria.setMaxResults(2);
                 List<Customer> l=criteria.list();
                 for(Customer c:l){
                     System.out.println(c);
                 }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(4)================= QBC设定查询条件 ====================

    必须创建一个Criterion对象来设定查询条件。Restrictions类提供了创建Criterion的方法。
            -Restrictions.eq()  等于
            -Restrictions.ne() 不等于
            -Restrictions.gt()  大于
              ..........等等很多方法 

        Criteria criteria=session.createCriteria(Customer.class);
        //查询id为7的Customer
         Criterion c1=Restrictions.eq("id", 7);
         criteria.add(c1);
         Customer c=(Customer) criteria.uniqueResult();
         System.out.println(c);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(5)================= QBC检索单个对象 ====================

        uniqueResult():返回单个对象
   
   
  • 1

(5)本地SQL检索方式

    可以使用原生的SQL语句来进行查询。
    本地SQL检索也是使用Query接口,通过Session的createSQLQuery(SQL语句)方法来创建Query。

        例子:
            Query query =session.createSQLQuery("select * from customer");
             List<Object[]> list=query.list();
             for(Object[] o:list){


                 System.out.println(Arrays.toString(o));
             } 

            默认情况下,SQLQuery返回的list集合中存放的是关系数据。每个元素都是Object[]数组。

        -addEntity()方法能把查询结果中每一行的数据封装成对象

                Query query =session.createSQLQuery("select * from customer").addEntity(Customer.class);
                 List<Customer> list=query.list(); 
                 for(Customer o:list){
                     System.out.println(o.toString());
                 }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Hibernate高级配置

配置数据库连接池

                配置C3P0连接池。
                先导入c3p0包。
                然后在hibernate.cfg.xml文件中 ,使用下面代码配置连接池
                <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

                可以通过下面的属性来配置连接池的参数  
                #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  

                例如:  <property name="hibernate.c3p0.max_size">5</property>  

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

处理并发问题

Hibernate处理事务并发问题

事务并发问题以前都有讲到过,可以翻阅以前的笔记。
在Hibernate中设置事务的隔离级别。
<property name="hibernate.connection.isolation">2</property>

隔离级别代号。
1Read Uncommitted
2: Read Committed
4: Repeatable Read 
8: Serializable

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

数据库系统锁的基本原理

    为了避免各种并发问题,以保证数据的完整性和一致性,数据库系统采用锁来实现事务的隔离性。 

    锁的类型
    -共享锁:用于读数据操作。允许其他事务同时读取资源,但不允许其它事务更新。
    -独占锁:用于修改数据的场合。它锁定的资源,其他事务不能读取也不能修改。 

    锁的基本原理如下
        -当一个事务访问某种数据库资源时,如果执行select语句,必须先获得共享锁。
        如果执行updateinsertdelete语句,必须先获得独占锁。

        -当第二个事务也要访问相同的资源时,如果执行select语句,也必须获得共享锁。
        如果执行updateinsertdelete语句,也必须先获得独占锁。
        此时根据锁的类型。来决定第二个事务是应该等待第一个事务完成,还是可以立即获得锁。

    许多数据库系统都有自动管理锁的功能,能够根据事务执行的sql语句,自动为资源加上适当的锁。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这里写图片描述

悲观锁和乐观锁

悲观锁 :

指在应用程序显式的为数据资源加锁。悲观锁假定当前事务操纵数据资源时,一定会有其他事务同时访问资源。为了避免当前事务受到干扰,先锁定资源。悲观锁能够防止丢失更新和不可重复读等并发问题,但是会影响并发性能。需要谨慎使用。 悲观锁对事务处理很悲观,总是认为其他事务会占用资源。

当事务执行select语句时,默认使用共享锁来锁定查询的语句。也可以使用 select ..... lock in share mode来指定使用共享锁。 

使用select .... for update来指定采用独占锁来锁定查询的记录 

例如取款:select * from bank where id=1 for update 
那么这条记录就被锁定,其他事务如果要处理这条数据,必须停下来等待。直到我们的取款事务结束。 

Hibernate应用中,当通过Sessionget()和load()方法来加载一个对象时,可以采用以下方式使用悲观锁。
Session.get(Clsss,OID,锁的类型);

锁的类型
    LockMode.UPGRADE:使用悲观锁,也就是独占锁。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

乐观锁 :

乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源。
因此完全由数据库的隔离级别来控制,或者我们自己来实现。乐观锁对事务的态度总是很乐观。

   
   
  • 1
  • 2
  • 3

实现的原理

            在数据表中添加Version字段,初始化为0,事务提交的前提是我的数据Version字段必须比数据库中的Version字段小1。

            例如同时两个付款单,A付款事务的时候会先取出金额,然后付款,修改余额,然后将version字段加一
            B付款事务执行同样的操作,然后事务提交。
            如果A先提交事务,version变为1.B再提交的时候version也变为1,然后就会发现Version的值是一样的,这时就会抛出异常   


Hibernate帮我们实现了这种操作。
        我们需要在我们的实体类中添加一个version属性,并添加get/set方法。
        然后在实体类的映射文件中配置这个属性,紧跟在<id>元素后面有一个version元素,name值为version属性名。
        <version name="version"></version> 

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css">
            </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值