Hibernate(三) 检索方式和策略

最近重新学习了一遍Hibernate框架,感觉收益颇多。学到最后不得不说下Hibernate的检索,为了提高检索速度,Hibernate提供了立即检索策略延迟检索策略左外连接检索策略三种策略。为了满足用户不同习惯和多样化搜索需求,Hibernate提供了五中检索数据的方式,这其中我主要记录了HQL检索方式

说到检索,为了实现高效的检索,我们先来说下Hibernate的检索策略。

一、Hibernate的检索策略

Hibernate的session在加载对象是可以将其关联的对象也加载到缓存中来,以便调用,但在有些情况下,我们不太需要加载太多无用的对象到缓存中来,一是浪费内存,二是消耗无谓的查询。所以为了合理使用缓存,Hibernate提供了立即检索策略延迟检索策略左外连接检索策略

1. 立即检索策略和延迟检索策略

立即检索策略就是立即将查询对象查询出来加进缓存,并且将其关联对象也加载出来,这样做能够及时加载自身数据和关联对象数据,但是比较占内存和执行过多的select语句。

延时检索策略在不涉及到连级操作单张表时,只适用 sessionload 方法,只有在用到对象除 id 以外的属性时才会去加载对象。在涉及到关联类操作时,只有在用到关联对象的时候才会去加载。优点: 由程序决定加载哪些内容,避免了大量无用的查询语句。缺点: 在session关闭后,就不能访问关联类的对象了,强行访问会发生懒加载异常

下面我们来通过例子说明下立即检索策略延时检索策略的区别。下面举例用到的是 Grade 类和 Student 类,分别对应数据库里面 hbm_one2many_gradehbm_one2many_student 两张表,两者是一对多关系。

① 当不涉及到关联操作,只有一张表时 getload 的区别

session.get(Grade.class, 1);

会立即发送SQL查询语句,将查询对象加进缓存

session.load(Grade.class, 1);

load 执行后并不发送SQL查询语句,只有在访问到该对象的除id外的其他属性时才会发送SQL查询语句。

② 在 集合标签里面设置 lazy 属性,值分别是 true / false / extra

...,使用懒加载

Grade grade = session.get(Grade.class, 1);

这个时候只会查询 Grade 对象,其关联的对象(set集合关联的对象)不会去查询,只有在用到关联对象的时候才会发送数据查询SQL语句。

...,不使用懒加载

Grade grade = session.get(Grade.class, 1);

不采用懒加载的方式, 查询 Grade 的时候会将其关联的对象(set集合关联的对象)全部查询出来。

...,Hibernate又对懒加载方式做了些优化

Grade grade = session.get(Grade.class, 1);

这个时候不会去查询set集合关联的对象

grade.getStudents().size();

这个时候也不会去查询set集合关联的对象, 而是发送条聚合查询 select count(id) from ... where grade_id =?

for (Student student : grade.getStudents()) {
    //System.out.println(student.toString());
}

这个时候才会发送SQL语句查询set集合关联的对象。

③ 在 标签里面设置 lazy 属性,值分别是 false / proxy / no-proxy

时,也是其默认

session.get(Student.class, 1);

这个查询会将会 many-to-one 关联的对象也给查询出来

时,对于many-to-one关联的对象查询使用懒加载的方式,用到是才会发送SQL语句查询。

注意: 当大环境是懒加载时,可以调用 initialize(...) 方法立即加载懒加载对象。

Grade grade = session.get(Grade.class, 1);
grade.getStudents(); //这时懒加载没有被查询
Hibernate.initialize(grade.getStudents()); //即使是懒加载, 也会被查询出来
3. 左外连接检索策略

采用左外连接检索,能够使用SQL的外连接查询将关联对象加进缓存中。
优点: 使用了外连接,select语句数目少。
缺点: 可能会加载应用程序不需要的对象进缓存,白白浪费内存,同时复杂的数据库表连接也会影响检索性能。

对应 fetch 属性,可设置在 标签和 标签中,对应三种值分别是 join / select / subselect

下面拿 标签使用 fetch 属性举例

时进行查询操作

session.get(Grade.class, 1);

执行上面代码发送查询的SQL语句

select
    ...
from
    hbm_one2many_grade grade0_ 
left outer join
    hbm_one2many_student students1_ 
        on grade0_.id=students1_.grade_id 
where
    grade0_.id=?

通过左外连接的方式将其 集合标签关联的对象也都全部查询出来,这时 标签里面的 lazy 属性无效。

时进行查询操作

这时会按照 lazy 设置方式加载关联对象,发送普通的 select SQL语句进行相关查询。

二、Hibernate的检索方式

Hibernate检索数据分为 5 中方式,分别是 OID检索、导航对象图检索、HQL检索、QBC检索、和本地SQL检索

1. OID检索方式

这种检索方式使我们最常用的,根据对象 id 查询对象,涉及到的方法 session.get("xx", id)session.load("xx", id)

2. 导航对象图检索方式

通过已加载的对象导航到其他关联对象,一般用在关系数据表对象查询中。

Student student = session.get(Student.class, 1);

Grade grade = student.getGrade();

后面根据 student 对象查询里面关联的 grade 对象

3. QBC检索方式

Hibenate的QBC查询个人认为是Hibernate的很大一个亮点,提供个丰富的查询API,但是QBC据我了解几乎没有程序员使用,跟使用起来麻烦有很大关系。下面我们通过例子了解下。

以前用QBC我们都用session.createCriteria()来开始查询,不过这种方式已经被废弃 @deprecated (since 5.2) for Session, use the JPA Criteria,很明显作者认为以前创建QBC查询方式不够好,现在改成用 JAP Criteria,通过下面的一个查询简单看下。

@Test
public void testQBCQuery() {
    //1. 创建CriteriaBuilder对象
    CriteriaBuilder builder = session.getCriteriaBuilder();
    //2. 获取 CriteriaQuery
    CriteriaQuery criteriaQuery = builder.createQuery(Grade.class);
    //3. 构建搜索语句         select * from grade
    Root root = criteriaQuery.from(Grade.class);
    //3.1 构建条件          + where id > 2
    criteriaQuery.where(builder.gt(root.get("id"), 2));
    //3.2 构建排列顺序    + order by id desc
    criteriaQuery.orderBy(builder.desc(root.get("id")));
    //4. 执行查询, 返回结果集
    List list = session.createQuery(criteriaQuery).getResultList();

    for (Grade student : list) {
        System.out.println(student);
    }
}
4. 本地SQL检索方式

一般我们都用Hibernate提供的 HQL检索方式,但是有时可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句,在这种情况下就需要用到Hibernate提供的原生SQL检索方式。
先前执行原生SQL语句都用 session.createSQLQuery(),不过这个方法已经过时了 @deprecated (since 5.2) use {@link #createNativeQuery(String)} instead,那么下面我们将用 createNativeQuery() 来展示用原生SQL语句进行查询和更新。

当我们没有指定查询结果集类型时,Hibernate将为我们返回字段值组成的数组

@Test
public void testSQLQuery() {
    NativeQuery nativeQuery = session.createNativeQuery("select * from contact where id>?");
    nativeQuery.setParameter(1, 2);
    List list = nativeQuery.getResultList();
    for (Object[] objects : list) {
        System.out.println(Arrays.toString(objects));
    }
}

当我们指定查询结果集类型时,Hibernate会帮我们把查询结果封装成对象

@Test
public void testSQLQuery2() {
    String sql = "select * from contact where id>? and id nativeQuery = session.createNativeQuery(sql, Contact.class);
    nativeQuery.setParameter(1, 1);
    nativeQuery.setParameter(2, 3);
    List list = nativeQuery.getResultList();
    for (Contact contact : list) {
        System.out.println(contact.toString());
    }
}

测试用Hibernate执行原生SQL更新语句,用到 executeUpdate() 方法,更新、删除和插入都用这个方法

@Test
public void testSQLUpdate() {
    String sql = "update contact set name=? where id=?";
    NativeQuery nativeQuery = session.createNativeQuery(sql);
    nativeQuery.setParameter(1, "修改后的名字");
    nativeQuery.setParameter(2, 2);
    int num = nativeQuery.executeUpdate();
    System.out.println("影响行数 " + num);
}
5. HQL检索方式

下面我们重点来说下HQL检索方式

三、HQL检索方式详叙

HQL (Hibernate Query Language) 是面向对象的查询语言,他和SQL查询语言有些相似,在Hibernate提供的各种检索方式中,HQL是使用最广泛的一中检索方式,他可以将自行的HQL语句翻译成对应数据库的SQL语句,更换数据库十分方便。

1. HQL简单查询列表
String hql = "from Contact"; 
List list = session.createQuery(hql).getResultList();
for (Object object : list) { System.out.println(object); }

注意:HQL语句中关键字不区分大小写,但是实体类和对象属性要区分大小写。没有指定列查询Hibernate内部会将返回数据封装成查询类的对象。

2. HQL 语句占位符
String hql = "from Contact where id>?";
List list = session.createQuery(hql).setParameter(0, 2).getResultList();
for (Object object : list) { System.out.println(object); }

注意:占位符的索引位置跟JDBC里面参数索引有些区别,Hibernate索引位置是从 0 开始的。

3. HQL 语句中使用形参充当占位符
String hql = "from Contact where id>:id";
List list = session.createQuery(hql)
                    .setParameter("id", 3)
                    .getResultList();
System.out.println(list);

这种用 :id 来代替 ? 当占位符要更常用,因为后面可以直接根据参数名字赋值,直接明了。

4. HQL 查询单条记录
String hql = "from Contact where id=?";
Object object = session.createQuery(hql)
                        .setParameter(0, 2)
                        .getResultList()
                        .get(0); 
System.out.println(object);

注意:这样查询出来的是单条记录,但是一般不推荐使用 .getResultList().get(0),因为当如果id=0的 Contact 对象不存在时,使用get(0)会抛出异常,我们通常使用下面这种方式

String hql = "from Contact where id=?";
Object object = session.createQuery(hql)
                        .setParameter(0, 2)
                        .uniqueResult();
System.out.println(object);

通过 uniqueResult() 方法返回一个Object对象,如果对象不存在则返回null,如果返回值不唯一,则抛出异常。

5. HQL 聚合查询
String hql = "select count(id) from Contact";
Object uniqueResult = session.createQuery(hql).uniqueResult();
System.out.println(uniqueResult);

同时还支持 avg()min()max()...聚合函数的查询。

6. HQL 查询单列字段
String hql = "select c.name from Contact c";
List list = session.createQuery(hql).getResultList();
for (Object object : list) { System.out.println(object); }

7. HQL 查询多列字段,以数组盛装单行查询字段结果集,然后将各行的结果集放进List数组里
String hql = "select c.name, c.birthday from Contact c";
List list = session.createQuery(hql).getResultList();
for (Object[] objects : list) { System.out.println(Arrays.toString(objects)); }
8. HQL 查询多列字段,以对象盛装单行查询字段结果集,然后将各行的结果集放进List数组里
String hql = "select new Contact(c.name, c.birthday) from Contact c";
List list = session.createQuery(hql).getResultList();
for (Object object : list) { System.out.println(object); }

注意:要求实体类必须有对应的构造方法

9. HQL 查询结果排序
String hql = "from Contact where id>? order by id desc";
List list = session.createQuery(hql).setParameter(0, 2).getResultList();
System.out.println(list);

按照查询结果 id 进行降序排序

10. HQL 分页查询
String hql = "from Contact";
List list = session.createQuery(hql)
                    .setFirstResult(1)
                    .setMaxResults(2)
                    .getResultList();
System.out.println(list);

注意:Hibernate会将语句翻译成SQL语句 limit 1, 2setFirstResult() 指记录开始的index(index从0开始表示第一条),setMaxResults() 设置从第 index+1 条开始往后取多少条。

你可能感兴趣的:(Hibernate(三) 检索方式和策略)