Hibernate 学习笔记(二)—— Hibernate HQL查询和 QBC 查询

在 Hibernate 中,查询方式有 HQL 和 Criteria 查询两种方式,HQL是Hibernate Query Language的缩写,语法类似于 SQL 语句,可以直接使用实体类名称及属性名称来查询,它提供更加丰富灵活、更为强大的查询能力。

Criteria 查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Language)查询提供了更加丰富的和灵活的查询特性,因此 Hibernate将 HQL 查询方式立为官方推荐的标准查询方式,HQL 查询在涵盖 Criteria 查询的所有功能的前提下,提供了类似标准 SQL 语句的查询方式,同时也提供了更加面向对象的封装。

一、Hibernate 的 HQL 查询

HQL 语法类似于 SQL,有 SQL 的关键词如 select 、from 、order by 、count()、where 等,完整的HQL语句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc。

Hibernate 的 HQL 查询中,通过 Session 对象的 createQuery(String HQL) 方法,获取 Query 对象, Query 中传入查询的 HQL 语句,之后通过 Query对象的 list() 方法,获取查询的结果集。

HQL 书写规范:

  1. 在查询语句中省略 select 关键字
  2. 使用类名称替代数据库中的表名称
  3. 使用实体类的属性名称代替数据库表中的列名称

HQL 查询步骤:

  1. Query query = session.createQuery(String HQL);:获取查询的 Query 对象
  2. query.setParameter(arg0,arg1);:设置查询条件参数,参数下标从0开始
  3. List list = query.list();:获取查询结果集

1.1、查询所有数据

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 查询数据库表中的所有数据
    @Test
    public void testHQL01() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        // 使用session的createQuery(String HQL)获取 Query 对象
        Query query = session.createQuery("from User");
        // 使用 query对象的list()方法,获取查询结果集
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.2、条件查询

在条件查询中,HQL 书写方式与 SQL 一样,使用 ? 作为参数的占位符,通过 setParameter(arg0, arg1) 来设定参数的值,其中:

  • arg0:表示参数的小标,从 0 开始;
  • arg1:表示参数的具体值;
package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 条件查询,查询昵称里面有蚂蚁的用户
    @Test
    public void testHQL02() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        // 使用session的createQuery(String HQL)获取 Query 对象
        Query query = session.createQuery("from User where nickname like ?");
        // 使用模糊查询
        query.setParameter(0, "%蚂蚁%");
        // 使用 query对象的list()方法,获取查询结果集
        List<User> list = query.list();
        
        for (User user : list) {
            System.out.println(user);
        }

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

1.3、排序查询

在排序查询中,HQL 书写方式与 SQL 一样,使用 order by 属性名称 desc/asc 来进行排序。

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {
    
    // 排序查询
    @Test
    public void testHQL03() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Query query = session.createQuery("from User order by uid desc");
        
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.4、统计查询

在 Hibernate 中,使用 HQL 语句,也可以使用聚合函数 (count()、avg()、sum()、max()、min())进行查询。使用聚合函数式, HQL 语句的写法如下:

Query query = session.createQuery("select count(*) from User");

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {
    
    // 统计查询
    @Test
    public void testHQL04() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Query query = session.createQuery("select count(*) from User");

        // 使用聚合函数中,除了使用 group
        // by进行分组的情况下,返回的都是唯一的结果,此时可以用query的uniqueResult()方法,接收唯一结果
        // 使用 Long 类型进行接收
        Long count = (Long) query.uniqueResult();
        System.out.println(count);
    }
}

1.5、分页查询

在 Hibernate 的分页查询中,通过 query.setFirstResult(0)query.setMaxResults(2) 方法分别设置查询的开始位置和每页显示的数量,从而达到分页查询的效果。

  1. query.setFirstResult(0):设置查询的开始位置,从0开始
  2. query.setMaxResults(2):设置每页的显示数据量
package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 分页查询
    @Test
    public void testHQL05() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        
        Query query = session.createQuery("from User");
        
        query.setFirstResult(0);
        query.setMaxResults(2);

        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.6、投影查询

在 Hibernate 框架中,当我们只需要查出某个对象中的个别属性的时候,如目前只需要查出用户的用户名和密码两个属性,如果使用 HQL 查询的方式,将会查找出该对象的全部属性,此时将会降低查询的速度和浪费系统的资源。

如果我们使用Query query = session.createQuery(select username , password from User); 此时通过 query.list(); 方法,我们得到的结果集是 List<Object[]> 类型的,不符合面向对象的特性,而且结果集不方便使用。

为了解决这一问题,Hibernate 也给出了解决方案投影查询,通过 Query query = session.createQuery("select new User(username,password ) from User"); 此时,通过 query.list() 方法得到的结果集是 list<User> 类型的,但是其中 User 对象除 usernamepassword 以外的属性均为 null

需要注意的是:在使用投影查询中的 HQL 语句中,出现了 new 关键字,这意味着,我们需要在普通Java类中添加所要查询属性的有参构造函数,此外,为了保证该普通 Java 类符合 JavaBean 的规范,我们需要声明其无参构造函数,不然程序运行时会报出 org.hibernate.hql.ast.QuerySyntaxException 异常。

User 实体类

package com.hibernate.domain;

import java.util.Date;

public class User {
    
    private Integer uid;
    private String username;
    private String password;
    private String nickname;
    private String realname;
    private Date birthday;
    
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public String getRealname() {
        return realname;
    }
    public void setRealname(String realname) {
        this.realname = realname;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    public User() {}
    
    @Override
    public String toString() {
        return "User [uid=" + uid + ", username=" + username + ", password=" + password + ", nickname=" + nickname
                + ", realname=" + realname + ", birthday=" + birthday + "]";
    }
    
}

投影查询:

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 投影查询:查询用户名和密码
    @Test
    public void testHQL06() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        
        Query query = session.createQuery("select new User(username, password) from User");
        // 所查询得到的结果集仍然是List<User> 类型
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

二、Hibernate 的 QBC 查询

Hibernate 中的 QBC 查询方式,全称为 Query By Criteria,通过 Session 对象的 createCriteria(Class clazz) 方法创建 Criteria 对象,之后通过 Criteria 对象的 list() 方法获取查询结果集。它是一种面向对象的查询方式,QBC 查询把生成语句的过程全部融入到方法中了。

2.1、基本查询

package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 基本查询
    @Test
    public void testQBC01() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        // 使用 createCriteria(Class clazz) 创建 Criteria 对象
        Criteria criteria = session.createCriteria(User.class);
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }
        
        tx.commit();
        session.close();
    }
}

2.2、条件查询

Hibernate QBC 查询进行条件查询时候,通过 Criteriaadd() 方法,添加查询条件。

package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 条件查询,通过 add()方法添加查询条件
    @Test
    public void testQBC02() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通过 Restrictions 添加具体的条件
        criteria.add(Restrictions.like("nickname", "%蚂蚁%"));
        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.3、排序查询

Hibernate QBC 查询进行排序查询时候,通过 CriteriaaddOrder() 方法,添加排序查询的条件。addOrder(Order.asc(属性名称))进行升序查询和 addOrder(Order.desc(属性名称)) 进行降序查询

package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 排序查询
    @Test
    public void testQBC03() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通过 addOrder() 添加排序条件
        criteria.addOrder(Order.desc("uid"));
        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.4、统计查询

QBC 查询中,Criteria 通过 criteria.setProjection(Projections.XXX) 方法来设定聚合函数。如使用 Projections.rowCount() 获取数据表中的总记录数,通过 Projections.count("实体类属性名称") 获取该属性有属性值的数据条记录数。

package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 统计查询
    @Test
    public void testQBC04() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 获取数据库表中具有uid属性值的数据条数
        criteria.setProjection(Projections.count("uid"));

        Long count = (Long) criteria.uniqueResult();
        System.out.println(count);
        
        tx.commit();
        session.close();    
    }
}

2.5、分页查询

QBC 和 HQL 中的分页查询所用的方法和方法的含义是一模一样的。通过 query.setFirstResult(0)query.setMaxResults(2) 方法分别设置查询的开始位置和每页显示的数量,从而达到分页查询的效果。

  1. query.setFirstResult(0):设置查询的开始位置,从0开始
  2. query.setMaxResults(2):设置每页的显示数据量
package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 分页查询
    @Test
    public void testQBC05() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 设置分页条件
        criteria.setFirstResult(0);
        criteria.setMaxResults(2);

        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.6、投影查询

package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 投影查询
    @Test
    public void testQBC06() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通过Projections.projectionList().add()添加所要查询的属性名
        criteria.setProjection(
Projections.projectionList().add(Property.forName("username")).add(Property.forName("password")));

        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.7、离线查询

在 JavaWeb 项目中,客户端查询实体总共经以下步骤:

  1. 客户端提交查询条件;
  2. 表现层 servlet 接收到客户端提交的查询条件,开启 SessionTransaction,创建 Criteria 对象,将查询条件封装到所创建的 criteria 对象中,并将 criteria 对象传递到业务层;
  3. 业务层将接收的 criteria 对象传递给持久层;
  4. 持久层接收 criteria 对象,查询数据库,并将查询结果依次传递给业务层、表现层,最后传递到客户端;

存在问题:

此时发现,在表现层中出现了开启 SessionTransaction,创建 Criteria 对象等属于持久层的操作,不符合 MVC 的程序设计规范,对后期程序的维护造成压力;

解决方式:使用 QBC 查询的离线查询,创建 DetachedCriteria 对象,该对象的获取不需要 Session,可以直接获得。

  1. 客户端提交查询条件,
  2. 表现层 servlet 接收到客户端提交的查询条件,创建 DetachedCriteria 对象,封装查询条件;并将 criteria 对象传递到业务层;
  3. 业务层将接收的 DetachedCriteria 对象传递给持久层;
  4. 持久层接收 DetachedCriteria 对象,将其转化成为 Criteria 对象,查询数据库,并将查询结果依次传递给业务层、表现层,最后传递到客户端;
package com.hibernate.test;

import java.util.List;

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

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 模拟表现层 —— Servlet
    @Test
    public void servletDetachedCriteria() {
        // 创建DetachedCriteria对象
        DetachedCriteria dc = DetachedCriteria.forClass(User.class);
        // 封装客户端提交的查询条件
        dc.add(Restrictions.like("nickname", "%蚂蚁%"));
        // 将封装好查询条件的DetachedCriteria对象传递给业务层
        List<User> users = seviceDetachedCriteria(dc);
        for (User user : users) {
            System.out.println(user);
        }
    }
    
    // 模拟业务层代码
    public List seviceDetachedCriteria(DetachedCriteria dc) {
        Session session = null;
        Transaction tx = null;
        try {
            // 获取与当前线程绑定的 Session 对象
            session = HibernateUtil.getCurrentSession();
            tx = session.beginTransaction();
            tx.begin();
            return daoDetachedCriteria(dc);
        } catch (Exception e) {
            tx.rollback();
        } finally {
            tx.commit();
        }
        return null;

    }

    // 模拟持久层代码
    public List daoDetachedCriteria(DetachedCriteria dc) {

        // 获取当前线程绑定的session
        Session session = HibernateUtil.getCurrentSession();
        // 将业务层传递的DetachedCriteria对象转化为Criteria对象
        Criteria criteria = dc.getExecutableCriteria(session);
        // 返回查询结果集
        return criteria.list();
    }
}

猜你喜欢

转载自www.cnblogs.com/martin0319/p/10857736.html
今日推荐