我们在对数据库的操作中,最常用的就是select,那么使用Hibernate如何进行select操作呢?本文就来徐徐道来。在Hibernate中提供了很多种的查询的方式,归纳起来可分为五种。
下面我就来一一介绍Hibernate中这5种检索方式。
哎妈啊!这种方式用老多遍了,我都懒得说了。所谓OID检索,就是Hibernate根据对象的OID(主键)进行检索。在Hibernate中,我们可通过get或load方法根据OID来查询指定的对象。
使用get方法
Customer customer = session.get(Customer.class, 1l);
使用load方法
Customer customer = session.load(Customer.class, 1l);
所谓的对象导航图检索方式就是通过在Hibernate中进行映射关系,然后在Hibernate操作时,可以通过导航方式得到其关联的持久化对象信息。说得更容易理解一点就是:对象导航图检索方式就是Hibernate根据一个已经查询到的对象,获得其关联对象的一种查询方式。
LinkMan linkMan = session.get(LinkMan.class, 1l);
Customer customer = linkMan.getCustomer();
或者
Customer customer = session.get(Customer.class, 2l);
Set<LinkMan> linkMans = customer.getLinkMans();
HQL是我们在Hibernate中最常用的一种检索方式。HQL(Hibernate Query Language)提供了更加丰富灵活、更为强大的查询能力,因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语句的查询方式,同时也提供了更加面向对象的封装。完整的HQL语句形式如下:
select/update/delete ...... from ...... where ...... group by ...... having ...... order by ...... asc/desc
其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。HQL检索的基本步骤:
session.createQuery(HQL语句)
创建一个Query对象;首先肯定是搭建好Hibernate的开发环境啦!笔者在此也不过多赘述,读者自行实践,聪明的读者要是有心的话,看过我的《Hibernate入门第十讲——Hibernate的一对多关联映射》这篇文章,自然就会知道如何搭建了。然后,我就来准备一些数据,不然查什么呢?我是这样做的:
怎么做到的呢?在src目录下新建一个com.meimeixia.hibernate.demo01包,在该包下编写一个HQLTest单元测试类,并在该包下编写如下方法:
package com.meimeixia.hibernate.demo01;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.meimeixia.hibernate.domain.Customer;
import com.meimeixia.hibernate.domain.LinkMan;
import com.meimeixia.hibernate.utils.HibernateUtils;
/**
* HQL查询方式的测试类
* @author liayun
*
*/
public class HQLTest {
@Test
public void demo01() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//创建一个客户
Customer customer = new Customer();
//customer.setCust_name("张小敬");
//customer.setCust_name("鸣太子");
customer.setCust_name("二柱子");
for (int i = 1; i <= 10; i++) {
LinkMan linkMan = new LinkMan();
//linkMan.setLkm_name("檀其" + i);
//linkMan.setLkm_name("自来也" + i);
linkMan.setLkm_name("大蛇丸" + i);
//设置双向的关联关系
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
session.save(linkMan);
}
session.save(customer);
tx.commit();
}
}
运行以上方法之后,我们所想要的数据就准备好了,下面就正式进入正题。
基本检索就是from 类名
,from是关键字,后面是类名,关键字是不区分大小写的,但是类名是区分的。以码明示,在HQLTest单元测试类中编写如下单元测试方法:
为了更加方便在Eclipse控制台看到打印的结果,我们最好在Customer类中重写toString()方法。
温馨提示:SQL中支持*
号的写法,例如select * from cst_customer;
,但是在HQL中是不支持这种写法的。
在写HQL查询语句的时候,还能给类起个别名,所以,你可以像下面这样写代码。
/**
* 别名查询
*/
@Test
public void demo03() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//别名查询
Query query = session.createQuery("from Customer c");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
现在我们的需求是查询客户时,根据客户的ID进行升序排序。以码明示,在HQLTest单元测试类中编写如下单元测试方法:
/**
* 排序查询
*/
@Test
public void demo04() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//排序查询
//默认升序排列
List<Customer> list = session.createQuery("from Customer order by cust_id").list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
温馨提示:HQL排序检索时,默认是升序排列,跟SQL里面的排序一样,升序使用的是ASC,降序使用的是DESC。所以,如果查询客户时,要根据客户的ID进行降序排序,那么就要这样写代码了。
/**
* 排序查询
*/
@Test
public void demo04() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//排序查询
//默认升序排列
//List list = session.createQuery("from Customer order by cust_id").list();
//设置降序排列,升序就使用asc,降序就使用desc,跟sql里面的排序是一样的
List<Customer> list = session.createQuery("from Customer order by cust_id desc").list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
我首先来讲根据位置来绑定HQL语句参数的条件检索。如果想要查询名称叫张小敬的客户,那么你有可能在HQLTest单元测试类中编写如下单元测试方法:
/**
* 条件查询
*/
@Test
public void demo05() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//条件查询
//1. 按位置绑定:根据参数的位置进行绑定
Query query = session.createQuery("from Customer where cust_name = ?");
//只设置一个条件
query.setParameter(0, "张小敬");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
在上面的代码中,也只是设置了一个条件,那如果要设置多个条件呢,该咋设置?例如,现在想要查询姓张并且客户信息来源是知乎的客户,那么你有可能会在HQLTest单元测试类中编写如下单元测试方法:
/**
* 条件查询
*/
@Test
public void demo05() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//条件查询
//1. 按位置绑定:根据参数的位置进行绑定
Query query = session.createQuery("from Customer where cust_source = ? and cust_name like ?");
//设置多个条件
query.setParameter(0, "知乎");
query.setParameter(1, "张%");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
接着我来讲讲根据名称来绑定HQL语句参数的条件检索。如果现在还是想要查询姓张并且客户信息来源是知乎的客户,那么以上demo05方法应修改为如下。
/**
* 条件查询
*/
@Test
public void demo05() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//条件查询
//2. 按名称绑定:
Query query = session.createQuery("from Customer where cust_source = :aaa and cust_name like :bbb");
//设置参数
query.setParameter("aaa", "知乎");
query.setParameter("bbb", "张%");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
假设现在我们的需求是查询联系人时,每页显示10条记录,但要得到第二页的数据,那么你有可能在HQLTest单元测试类中编写如下单元测试方法:
/**
* 分页查询
*/
@Test
public void demo06() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from LinkMan");
//每页显示10条记录,我们要得到第二页数据
query.setFirstResult(10);//从第几条开始,即开始位置,从0开始计数
query.setMaxResults(10);//每页显示多少条记录
List<LinkMan> list = query.list();
for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
tx.commit();
}
先看我们的第一个需求:统计一共有多少个客户。以码明示,在HQLTest单元测试类中编写如下单元测试方法:
再看我们的第二个需求:分组统计每一个客户信息来源有多少客户。此时,以上demo07的方法可能就要修改为:
如果查询增加一点难度,想要分组统计每一个客户信息来源有多少客户,并且客户人数至少要是2人,要想解决这个需求,那么以上demo07的方法可能就要修改为:
其他一些聚合函数,例如max()、min()、avg()以及sum()的使用方法也就和count()一样,在此并不过多赘述。
啥叫投影检索啊?投影检索就是查询对象的某个或某些属性,而不是全部属性。这儿我也只是主要讲解关于实体类中部分属性的查询,这样我们就可以使用投影检索将部分属性封装到对象中。下面我步步为营地讲解投影检索。
一开始,我们的需求可能是这样的:查询出所有客户的名称,故我们可能会在HQLTest单元测试类中编写如下单元测试方法:
/**
* 投影查询
*/
@Test
public void demo08() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//投影查询
//查询单个属性
List<Object> list = session.createQuery("select c.cust_name from Customer c").list();
System.out.println(list);
tx.commit();
}
运行以上方法,控制台打印出[张小敬, 鸣太子, 二柱子]
这样的字符串,那么可以得出结论:如果只是查询一个列,那么得到的结果便是List
。
接着,我们的需求又可能会变成这样:查询所有客户的名称和客户信息来源。故可将以上demo08方法改为:
/**
* 投影查询
*/
@Test
public void demo08() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//投影查询
//查询多个属性
List<Object[]> list = session.createQuery("select c.cust_name,c.cust_source from Customer c").list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
}
从以上方法中可得知,如果是查询多列,那么得到的结果是List
。
最后,我们可能想到使用投影查询将查询的结果封装到Customer对象中。故可将以上demo08方法改为:
温馨提示:我们必须在Customer类中提供对应属性的构造方法,也要有无参数构造。所以,Customer类中的内容就应该是下面这样的。
现在我就来讲讲命名检索,我们可以将HQL语句先定义出来,在使用时通过session.getNamedQuery(hqlName);
得到一个Query对象,然后再执行检索。那么问题就来了,这个HQL语句到底定义在什么位置呢?如果你有映射配置文件,那么当前的HQL操作是针对哪一个实体进行操作的,就在哪一个实体的映射配置文件中进行声明,声明就像下面这样。
当然了,如果你是使用注解来描述PO类的配置,那么你可以直接在PO类中使用@NamedQuery注解来声明。这又是另外一个话题了,因为我们得会Hibernate的注解开发,之前我们一直都是使用映射配置文件,这儿突然来使用注解进行声明,未免太过唐突,所以,这儿我就不讲解这种方式了。感兴趣的童鞋,可以自己实践实践!
此时我们是准备要查询所有客户的,所以,可以在HQLTest单元测试类中编写如下单元测试方法:
/**
* 命名查询
*/
@Test
public void demo09() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//命名查询
Query query = session.getNamedQuery("myHql");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
关于命名查询,我就讲到这里。
QBC(Query by Criteria),它是一种更加面向对象的检索方式。使用QBC检索的步骤如下:
有一个需求:查询所有的Customer对象。以码明示,在com.meimeixia.hibernate.demo01包下编写一个QBCTest单元测试类,并在该类中编写如下测试方法:
package com.meimeixia.hibernate.demo01;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.meimeixia.hibernate.domain.Customer;
import com.meimeixia.hibernate.utils.HibernateUtils;
/**
* 测试QBC的查询
* @author liayun
*
*/
public class QBCTest {
/**
* 简单的查询
*/
@Test
public void demo01() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//获得Criteria的对象
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
}
有这样一个需求:查询客户信息,并根据客户的ID进行排序。以码明示,在QBCTest单元测试类中编写如下测试方法:
/**
* 排序查询
*/
@Test
public void demo02() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//排序查询
Criteria criteria = session.createCriteria(Customer.class);
//criteria.addOrder(Order.asc("cust_id"));//asc是升序
criteria.addOrder(Order.desc("cust_id"));//desc是降序
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
有这样一个需求:查询客户信息来源为知乎的客户。以码明示,在QBCTest单元测试类中编写如下测试方法:
/**
* 条件查询
*/
@Test
public void demo03() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//条件查询
Criteria criteria = session.createCriteria(Customer.class);
//设置条件,只设置一个条件
/*
* = eq
* > gt
* >= ge
* < lt
* <= le
* <> ne
* like
* in
* and
* or
*/
//下面只设置一个条件
criteria.add(Restrictions.eq("cust_source", "知乎"));
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
现在又有了这样一个需求:查询姓张并且客户信息来源为知乎的客户。这时,可将QBCTest单元测试类中的demo03方法修改为:
/**
* 条件查询
*/
@Test
public void demo03() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//条件查询
Criteria criteria = session.createCriteria(Customer.class);
//设置条件
/*
* = eq
* > gt
* >= ge
* < lt
* <= le
* <> ne
* like
* in
* and
* or
*/
//下面设置的是多个条件,
criteria.add(Restrictions.eq("cust_source", "知乎"));
criteria.add(Restrictions.like("cust_name", "张%"));
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
有这样一个需求:查询联系人时,每页显示10条记录,但我们要得到第二页的数据,那么你有可能在QBCTest单元测试类中编写如下单元测试方法:
/**
* 分页查询
*/
@Test
public void demo04() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//分页查询
Criteria criteria = session.createCriteria(LinkMan.class);
criteria.setFirstResult(10);//从第几条开始,即开始位置,从0开始计数
criteria.setMaxResults(10);//每页显示多少条记录
List<LinkMan> list = criteria.list();
for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
tx.commit();
}
有这样一个需求:统计客户总数。以码明示,在QBCTest单元测试类中编写如下单元测试方法:
/**
* 统计查询
*/
@Test
public void demo05() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
/*
* Criteria对象的方法有:
* add:加的是普通的条件(where后面的条件)
* addOrder:排序
* setProjection:弄那些聚合函数和group by having
*/
criteria.setProjection(Projections.rowCount());//统计总行数,可以理解为count(*)
Long count = (Long) criteria.uniqueResult();
System.out.println(count);
tx.commit();
}
我想在实际开发中,像这样子的统计检索应该用的不多,使用最多的还要数HQL分组统计检索,可能这种方式比较接近SQL查询,可以直观地让人感受到Hibernate底层向数据库发送了什么样子的SQL查询语句。
Hibernate框架支持在运行时动态生成查询语句,DetachedCriteria对象可以脱离Session而使用。之前我们在三层架构之间传递用户提交的参数可能是这样的:
但使用离线条件对象进行检索时,情况有可能是这样的:
现在有这样一个需求:查询姓张的客户。以码明示,在QBCTest单元测试类中编写如下单元测试方法:
/**
* 离线条件查询
*/
@Test
public void demo06() {
//假设这个离线条件查询对象是从web层传递过来的
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.like("cust_name", "李%"));
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
//让离线条件查询对象跟Session绑定一下,就会得到一个Criteria对象,然后调用Criteria对象的方法
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
有这样一个需求:查询所有的Customer对象。以码明示,在com.meimeixia.hibernate.demo01包中编写一个SQLTest单元测试类,并在类中编写如下方法:
package com.meimeixia.hibernate.demo01;
import java.util.List;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.meimeixia.hibernate.domain.Customer;
import com.meimeixia.hibernate.utils.HibernateUtils;
/**
* SQL查询
* @author liayun
*
*/
public class SQLTest {
@Test
public void demo01() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
/*
SQLQuery query = session.createSQLQuery("select * from cst_customer");
List
SQLQuery query = session.createSQLQuery("select * from cst_customer");
query.addEntity(Customer.class);//如何能得到一个对象呢?即将查询结果封装到指定对象。
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
}
需要说明的是本地SQL查询也支持命名查询。那么问题就来了,这个本地SQL语句到底应定义在什么位置呢?如果你有映射配置文件,那么当前的SQL查询是针对哪一个实体进行操作的,就在哪一个实体的映射配置文件中进行声明,声明就像下面这样。
本地命名SQL还可使用注解来定义,但这里我就不讲了,感兴趣的童鞋,可以自己动手实践实践!
此时我们是准备要查询所有客户的,所以,可以在SQLTest单元测试类中编写如下单元测试方法:
@Test
public void demo02() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
SQLQuery query = (SQLQuery) session.getNamedQuery("mySQL");
query.addEntity(Customer.class);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}