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();
	}
}
posted @ 2019-05-13 17:11  Java路上小菜鸟  阅读(504)  评论(0编辑  收藏  举报