第20章 Hibernate的查询与更新技术
Hibernate框架虽然可以使用Session接口的get或load方法装载持久化对象,但这只是根据主键来检索数据,局限性非常大。为此,Hibernate框架提供了很多强大的查询技术,这些查询技术包括标准(Criteria)查询API和HQL,除此之外,Hibernate还支持直接使用SQL,这将使Hibernate在操作数据库方面更加灵活。HQL和SQL不仅可以查询记录,而且还可以对记录进行增、删、改操作。本章的主要内容如下:
标准查询API概述;
标准查询API的约束条件;
对标准查询API的查询结果进行分页; 对查询结果进行排序; 多个Criteria之间的关联; 聚合与分组;
用标准查询API聚合字段值; 使用QBE(Query By Example);
HQL的From、Select、Where、Update、Delete、Insert语句; 在HQL中使用命名参数; 使用HQL进行分页;
使用HQL进行排序和分组; 使用SQL。
20.1 标准(Criteria)查询API
在Hibernate框架中提供了3种获得数据的方法:标准查询API、HQL和SQL。其中标准查询API可以建立基于Java的嵌套的、结构化的查询表达式,并提供了编译时语法检查的功能,而这在使用HQL和SQL时是无法办到的。在Hibernate 3中,标准查询API还提供了投影(projection)、聚合(aggregation)和分组(group)的方法。
20.1.1 实例:一个简单的例子
要想使用标准查询API,就要用到org.hibernate.Criteria接口。通过Session的createCriteria方法可以创建Criteria对象。如果想查询表中的数据,可以使用Criteria接口的list方法。这个方法将以java.util.List对象的形式返回所有被装载的持久化对象(查询到的记录)。下面的例子使用list方法来查询MyMessage对象。在这个例子中使用Criteria
第20章 Hibernate的查询与更新技术
接口的setMaxResults方法来限制返回的最大持久化对象数。
在执行MyCriteria程序后,在控制台将输出如下的信息:
package chapter20; import hibernate.*;
import org.hibernate.*; import chapter17.entity.*; import java.util.*;
public class MyCriteria {
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession(); // 使用createCriteria方法创建一个Criteria对象
Criteria crit = session.createCriteria(MyMessage.class); // 限制只返回前两个持久化对象
crit.setMaxResults(2); // 开始查询持久化对象
List System.out.print(message.getId() + \" \" + message.getName()); System.out.println(); } session.close(); } } 从上面的输出结果可以看出,标准查询API最终也被翻译成SQL语句,并将生成的SQL语句交由相应的DBMS来执行,最后返回查询结果。在最后两行是查询到的两个MyMessage对象中的属性值。即使t_messages表中的记录多于两条,MyCriteria程序仍然会只输出查询结果集中前两个持久化对象的属性值。这是由于调用了Criteria接口的setMaxResult方法限制了返回的持久化对象数。该方法也可以用来对查询结果进行分页(将在后面的部分详细介绍)。 Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ limit ? 1 bike 2 car 20.1.2 查询的约束条件 在标准查询API中可以非常容易地使用Criteria接口的add方法为查询增加约束条件。每一个查询条件是一个SimpleExpression对象,由org.hibernate.criterion.Restrictions类的静态方法获得相应的SimpleExpression对象。如查询id属性等于2的MyMessage对象的代码如下: Criteria crit = session.createCriteria(MyMessage.class); // eq方法表示“等于” crit.add(Restrictions.eq(\"id\List ·613· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX 除了eq方法外,还可以使用如下的方法来表示其他的逻辑关系。 标准查询API不仅能搜索精确的结果,还能搜索模糊的结果。在SQL语句中使用like来查询模糊的结果,而在标准查询API中可以使用like或ilike方法来查询模糊的结果。like方法不区分大小写,而ilike方法区分大小写(实际上,所谓不区分大小写,只是通过MySQL的lower函数将字段值都转换成小写,然后再将要比较的值也转换成小写,最后再进行比较,读者可以将这两个方法生成的SQL语句显示出来进行对比)。在进行模糊的查询时可以使用如下两种方法。 1.使用通配符 public static SimpleExpression ne(String propertyName, Object value);// != public static SimpleExpression lt(String propertyName, Object value);// < public static SimpleExpression gt(String propertyName, Object value);// > public static SimpleExpression le(String propertyName, Object value);// <= public static SimpleExpression ge(String propertyName, Object value);// >= 这种方式和SQL语句的like子句的语法类似。使用%作为通配符,如下面的代码查询所有name属性包含msg的MyMessage对象。 // 创建一个Criteria对象 Criteria crit = session.createCriteria(MyMessage.class); crit.add(Restrictions.like(\"name\ 添加like模糊查询条件 List 2.使用MatchMode 这种方式通过MatchMode类的静态常量指定了如下所示的4个不同的匹配策略。 MatchMode.ANYWHERE:相当于%msg%。 MatchMode.START:相当于msg%。 MatchMode.END:相当于%msg。 MatchMode.EXACT:精确匹配,相当于msg。 如下面的代码也可以查询所有name属性中包含msg的MyMessage对象。 // 创建一个Criteria对象 Criteria crit = session.createCriteria(MyMessage.class); // 通过匹配模式添加like模糊查询条件 crit.add(Restrictions.like(\"name\List 如果对于一个Criteria对象,多次使用add方法加入约束条件,那么这些约束条件之间的关系的默认值是and。也可以使用LogicalExpression将它们的关系改为or,如下面的代码所示: // 创建一个Criteria对象 Criteria crit = session.createCriteria(MyMessage.class); SimpleExpression id = Restrictions.eq(\"id\设置id等于2 SimpleExpression name = Restrictions.like(\"name\ANYWHERE); // 将id和name的关系改为or LogicalExpression orExp = Restrictions.or(id, name); ·614 · 第20章 Hibernate的查询与更新技术 crit.add(orExp); // 添加“或”关系 List results = crit.list(); // 通过list方法获得查询结果 Hibernate将根据上面的代码生成如下的SQL语句。 虽然LogicalExpression类可以很好地生成or逻辑关系,但当关系比较复杂的话,使用LogicalExpression类就显得有些麻烦。Hibernate提供了一个更容易的解决方案来生成or逻辑关系,这就是Disjunction类(如果要生成and关系,可以使用Conjunction类)。下面的代码演示了如何使用Conjunction和Disjunction来生成and和or关系。 select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ where (this_.id=? or this_.name like ?) Criteria crit = session.createCriteria(MyMessage.class); // 创建一个Criteria对象 Criterion id1 = Restrictions.gt(\"id\ // 大于2 Criterion id2 = Restrictions.lt(\"id\ // 小于20 Criterion name = Restrictions.like(\"name\Conjunction conjunction = Restrictions.conjunction(); conjunction.add(id1); conjunction.add(id2); Disjunction disjunction = Restrictions.disjunction(); disjunction.add(conjunction); // 添加id1和id2的关系 // name和id1、id2是or关系 disjunction.add(name); crit.add(disjunction); // 向Criteria对象中添加关系 List results = crit.list(); 上面的代码生成的SQL语句如下: 除此之外,还可以使用Restrictions类的sqlRestriction方法直接写SQL语句,代码 如下: crit.add(Restrictions.sqlRestriction(\"{alias}.name like '%d%'\")); select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ where ((this_.id>? and this_.id) or this_.name like ?) 也可以在SQL语句中加入“?”作为参数,代码如下: 其中第1个参数中的{alias}是表名,Hibenate在生成SQL语句时,会自动将其替换成t_messages。第2个参数要和第1个参数中的“?”相对应。第3个参数表示在条件中使用的字段类型(也就是name字段的类型,在这里是String类型)。如果查询条件中有多个“?”,第2个和第3个参数要使用数组形式来传递多个参数值和类型。 crit.add(Restrictions.sqlRestriction(\"{alias}.name like ?\\"%d%\org. hibernate.type.StringType())); 20.1.3 对查询结果进行分页 分页是Web应用程序中经常要用到的技术。如在一个论坛程序中,可能会有成千上万的帖子,这些帖子肯定不能在一个页面中显示出来,因此就要将这些记录显示在若干页面 ·615· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX 上。达到这种效果的一般实现方法是单击某一页的链接后,在服务端再重新查询整个记录集,然后通过某些特殊的手段将要在当前页面显示的记录取出,并显示在客户端浏览器中。 实际上,只获得查询结果的部分内容对于不同的数据库来说难易程度也不同。如SQLServer 2000中的实现就比较复杂(要从一个大的结果集中取得满足要求的结果集,效率不是很高),在SQLServer 2005中由于提供新的功能,所以实现分页要比SQL Server 2000容易一些。而在MySQL中,笔者认为它的分页功能是最容易实现的,只需要简单地使用limit关键字指定开始记录的位置(从0开始)和要获取的记录数即可,如下面的代码只取查询结果中的从第2条记录开始的两条记录。 从上面的描述可以看出,对于不同的数据库来说,各种数据库的分页方法各不相同,有的效率高,有的效率并不高。为了能统一大多数常用数据库的分页方法,并尽可能地提高效率,Hibernate提供了一种非常简便的方式来实现这个目标。在Criteria接口中有两个方法:setFirstResult方法和setMaxResults方法。其中setFirstResult设置了记录的开始位置(0表示第1条记录),setMaxResults设置了返回的记录数。如下面的代码从t_message表中获得了从第2条记录开始的两条记录。 select * from t_message limit 1, 2 如果不使用setFirstResult方法设置记录的开始位置,默认值是0。Hibernate会根据不同的数据库采用不同的分页策略,由于本书使用的数据库是MySQL,因此,Hibernate会使用limit进行分页处理。其实,setFirstResult和setMaxResults方法设置的值就是limit关键字的第1个和第2个参数值。Hibernate根据上面的代码生成的SQL语句如下: 下面是查询t_message表中的第2条和第3条记录的完整实现代码。 Criteria crit = session.createCriteria(MyMessage.class); crit.setFirstResult(1); // 返回结果的开始位置(从第2条记录开始),第1行记录 的位置是0 crit.setMaxResults(2); // 返回的记录(持久化对象)数 List select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ limit ?, ? package chapter20; import hibernate.*; import org.hibernate.*; import chapter17.entity.*; import java.util.*; public class Pagination { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); // 根据MyMessage创建Criteria对象实例 Criteria crit = session.createCriteria(MyMessage.class); // 设置取记录位置 crit.setFirstResult(1); // 设置要返回的MyMessage对象数 crit.setMaxResults(2); // 返回指定数量的MyMessage对象 List 第20章 Hibernate的查询与更新技术 // 输出返回的MyMessage对象的id和name属性值 for (MyMessage message : messages) { System.out.print(message.getId() + \" \" + message.getName()); System.out.println(); } session.close(); } } 20.1.4 实例:实现Web分页功能 在本节给出一个实例来演示如何整合Struts 2和Hibernate(使用标准查询API)实现Web分页。在该实例中服务端使用了Action类,客户端在JSP页面中使用Struts 2标签来读取分页数据,并生成完整的 PaginationAction是处理分页请求的Action类。在该类中通过读取Action参数来确定查询哪个实体Bean,以及显示实体Bean的属性和每页显示的行数。PaginationAction类的实现代码如下: package chapter20.action; import hibernate.HibernateSessionFactory; import org.hibernate.Criteria; import org.hibernate.Session; import com.opensymphony.xwork2.*; import java.util.*; public class PaginationAction extends ActionSupport { private String entity; // 封装entity参数的属性 private String fields; // 封装fields参数的属性 private int rows; // 封装rows参数的属性 private int currentPage; // 封装currentPage请求参数的属性 private List records = new ArrayList(); // 返回成功装载的持久化对象 // 处理控制逻辑的execute方法 public String execute() throws Exception { Session session = HibernateSessionFactory.getSession(); // 获得Hibernate Session对象 // 根据entity属性值创建Criteria对象实例 Criteria crit = session.createCriteria(entity); crit.setFirstResult((currentPage - 1) * rows); // 设置返回记录的开始行 crit.setMaxResults(rows); // 设置返回的记录行数 List list = crit.list(); // 使用list方法查询结果 // 将装载的持久化对象返回给客户端的JSP页面 for (Object obj : list) { records.add(obj); } session.close(); // 关闭Hibernate Session对象 return SUCCESS; } // fields属性的getter方法 public String getFields() ·617· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX { return fields; } // fields属性的setter方法 public void setFields(String fields) { this.fields = fields; } // rows属性的getter方法 public int getRows() { return rows; } // rows属性的setter方法 public void setRows(int rows) { this.rows = rows; } // entity属性的getter方法 public String getEntity() { return entity; } // entity属性的setter方法 public void setEntity(String entity) { this.entity = entity; } // currentPage属性的getter方法 public int getCurrentPage() { return currentPage; } // currentPage属性的setter方法 public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } // records属性的getter方法 public List return records; } // records属性的setter方法 public void setRecords(List this.records = records; } // fieldList属性的getter方法 public String[] getFieldList() { return fields.split(\ } } PaginationAction类的配置代码如下: ·618 · 第20章 Hibernate的查询与更新技术 chapter17.entity.MyMessage id,name 4 pagination.jsp页面负责显示服务端返回的信息。该页面的代码如下: 在浏览器地址栏中输入如下的URL: 浏览器显示的效果如图20.1所示。 <%@ page pageEncoding=\"UTF-8\"%> <%@ taglib prefix=\"s\" uri=\"/struts-tags\"%> http://localhost:8080/webdemo/chapter20/pagination.action?currentPage=1 ·619· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX 图20.1 Web分页 单击图20.1所示的页码,就会显示相应页的信息。读者也可以通过配置 20.1.5 只获得一个持久化对象 在某些情况下,只需要获得一条记录或一个持久化对象。如在统计记录数时,只想返回一条只包含一个字段的记录,或是只想返回查询结果集的第1条记录。在这种情况下,可以使用Criteria接口的uniqueResult方法。这个方法返回了一个Object对象,而不是一个List对象。如果未查到记录,uniqueResult方法返回null。如果无法保证返回的结果集只有一条记录,可以使用setMaxResults方法将返回的结果集的记录数设为1,如下面的代码所示: public class UniqueResult { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); // 获得一个Hibernate Session 对象 Criteria crit = session.createCriteria(MyMessage.class); crit.setMaxResults(1); // 设置只返回一条记录 crit.add(Restrictions.eq(\"id\// 添加一个id属性等于1的条件 MyMessage message = (MyMessage)crit.uniqueResult(); // 装载第一个持久化对象 // 如果成功装载MyMessage对象,输出该对象的name属性值 if(message != null) System.out.println(message.getName()); session.close(); // 关闭Hibernate Session对象 } } 注意:在调用uniqueResult方法时必须保证查询结果集只有一条记录,否则uniqueResult 方法将抛出一个org.hibernate.NonUniqueResultException异常。 ·620 · 第20章 Hibernate的查询与更新技术 20.1.6 对查询结果进行排序 在标准查询API中可以使用org.hibernate.criterion.Order类对查询结果进行排序。Order类有两个静态方法:asc和desc。这两个方法都返回Order对象。使用这两个方法可以指定要排序的持久化对象的属性。下面的代码演示了如何使用Order对结果集进行排序。 在执行上面的代码后,Hibernate将生成如下的SQL语句。 public class SortResult { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); Criteria crit = session.createCriteria(MyMessage.class); crit.addOrder(org.hibernate.criterion.Order.desc(\"id\")); // 对查询结果进行排序 List // 输出当前Message对象的id和name属性值 System.out.print(message.getId() + \" \" + message.getName()); System.out.println(); } session.close(); // 关闭Hibernate Session对象 } } select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ order by this_.id desc 20.1.7 多个Criteria之间的关联 在标准查询API中也可以处理多个实体Bean关联的情况。如像Order和Customer对象这样的多对一关系,要想描述这种关系,需要使用Criteria接口的createCriteria方法创建另一个Criteria对象,这个createCriteria方法的参数是一个属性名。要建立Order和Customer对象的多对一关系,要先使用Session接口的createCriteria方法为Order建立一个Criteria对象,然后再使用这个Criteria对象的createCriteria方法为Order类的customer属性创建一个Criteria对象,代码如下: public class Associations { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); // 建立Order和Customer对象的多对一关系 Criteria crit1 = session.createCriteria(Order.class); Criteria crit2 = crit1.createCriteria(\"customer\"); // 添加约束条件 crit1.add(Restrictions.ne(\"number\ crit2.add(Restrictions.eq(\"name\ ·621· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX crit2.addOrder(org.hibernate.criterion.Order.desc(\"name\")); // 按name字段降序排序 List // 输出当前Order对象的number和name属性值 System.out.print(order.getNumber() + \" \" + order.getCustomer(). getName()); System.out.println(); } session.close(); } } 在上面的代码中除了建立了Order和Customer的多对一的关系外,还设置了查询条件(使用Criteria接口的add方法为crit1和crit2添加约束,详见20.1.2节的内容)。 20.1.8 聚合和分组 在标准查询API中可以使用org.hibernate.criterion.AggregateProjection类进行聚合操作,并且使用org.hibernate.criterion.Projections类的相应的静态方法来建立AggregateProjection对象,最后使用Criteria接口的setProjection方法设置相应的AggregateProjection对象。如获得表的记录数的代码如下: Criteria crit = session.createCriteria(MyMessage.class); // 通过Projections类的rowCount方法获得记录数 crit.setProjection(Projections.rowCount()); Integer value = (Integer)crit.uniqueResult(); System.out.println(value); 要注意的是,setProjection方法只在最后一次有效。也就是说,对一个Criteria对象多次使用setProjection方法后,后一次将覆盖前一次的聚合操作。如果要为Criteria对象设置多个聚合操作,可以使用org.hibernate.criterion.ProjectionList类。下面的代码是使用聚合方法的一个完整示例。 public class MyProjections { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); // 创建Hibernate Session对象 Criteria crit = session.createCriteria(MyMessage.class); // 创建Criteria对象 crit.setProjection(Projections.rowCount()); // 统计记录数 Integer value = (Integer)crit.uniqueResult(); // 使用uniqueResult方法统计记录的行数 System.out.println(value); crit.setProjection(Projections.max(\"id\")); // 求id属性的最大值 // 使用uniqueResult方法对id字段的最大值 value = (Integer)crit.uniqueResult(); System.out.println(value); crit.setProjection(Projections.sum(\"id\")); // 求id属性之和 value = (Integer)crit.uniqueResult(); // 使用uniqueResult方 法对id字段值之和 ·622 · 第20章 Hibernate的查询与更新技术 System.out.println(value); ProjectionList projList = Projections.projectionList(); // 创建ProjectionList对象 // 同时使用三个聚合方法 projList.add(Projections.rowCount()); projList.add(Projections.max(\"id\")); projList.add(Projections.sum(\"id\")); crit.setProjection(projList); List results = crit.list(); // 同时执行3个聚合操作 Object[] array = (Object[]) results.get(0); for(int i = 0; i < array.length; i++) { System.out.println(array[i]); } session.close(); // 关闭Hibernate Session对象 } } 如果Criteria对象有多个聚合操作,Hibernate仍返回一条记录,但这条记录并不是持久化对象,而是一个包含了每一个聚合值的Object数组(一个数组元素表示一个聚合值)。如执行上面的代码后将生成如下的4条SQL语句。 由于上面的SQL语句只有3个字段,因此,Hibernate生成了一个长度为3的Object数组。聚合与分组也可混合使用。下面代码是一个同时使用聚合与分组方法的完整的示例。 select count(*) as y0_ from t_message this_ select max(this_.id) as y0_ from t_message this_ select sum(this_.id) as y0_ from t_message this_ select count(*) as y0_, max(this_.id) as y1_, sum(this_.id) as y2_ from t_message this_ 在执行上面的代码后,Hibernate将生成如下的SQL语句。 public class ProjectionsGroup { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); Criteria crit = session.createCriteria(MyMessage.class); ProjectionList projList = Projections.projectionList(); projList.add(Projections.rowCount()); // 使用聚合方法 projList.add(Projections.groupProperty(\"name\")); // 使用分组方法 crit.setProjection(projList); List list = crit.list(); // 执行聚合、分组操作 Object[] array = null; // 输出查询结果 for (int i = 0; i < list.size(); i++) { array = (Object[]) list.get(i); // 将当前元素转换成对象数组 for(int j = 0; j < array.length; j++) System.out.print(array[j] + \" \"); System.out.println(); } session.close(); // 关闭Hibernate Session对象 } } select count(*) as y0_, this_.name as y1_ from t_message this_ group by ·623· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX this_.name 20.1.9 使用QBE(Query By Example) 除了使用标准查询API后,还可以用QBE来实现查询功能。QBE可以直接根据对象实例来指定查询条件。从前面的内容可以知道,在标准查询API中要使用Criterion对象来指定查询条件。而org.hibernate.criterion.Example类实现了Criterion接口,并且Example类可以使用create方法根据一个实体Bean对象来建立Criterion对象(实际上,QBE的意思就是使用Example类来生成Criterion对象,并进行查询)。下面的代码是一个使用Example查询数据的简单的例子。 在执行上面的代码后,Hibernate将生成如下的SQL语句。 Criteria crit = session.createCriteria(MyMessage.class); MyMessage message = new MyMessage(); message.setName(\"我的信息\"); crit.add(Example.create(message)); // 用Example类的create方法生成一个 Criterion对象 List 从上面SQL语句可以看出,Hibernate根据实体Bean对象生成了用于查询的SQL语句。但大家要注意一下,在默认情况下,Example会将实体Bean中的所有属性(不包括主键属性)作为查询条件放到where子句中,但会忽略值为null的属性,也就是说,在生成SQL语句时,值为null的属性不会出现在where子句中。 但有的属性类型是Java简单类型(如int),而不是复杂类型(如Integer)。对于Java简单类型,如int,在未赋值时的默认值是0。而Example并不会过滤值为0的属性。因此,这就需要使用Example类的excludeZeroes或excludeNone方法来忽略值为0的属性,也可以使用excludeProperty方法指定不参与查询的属性,代码如下: select this_.id as id0_0_, this_.name as name0_0_ from t_message this_ where (this_.name=?) 在执行上面的代码后,Hibernate将生成如下的SQL语句。 ·624 · Criteria crit = session.createCriteria(Person.class); Person person = new Person(); // 创建Person对象实例 java.util.Calendar cal = java.util.Calendar.getInstance(); cal.set(1955, 4, 7); // 设置指定日期 person.setBirthday(cal.getTime()); // 初始化birthday属性 Name name = new Name(); // 创建Name对象实例 name.setFirst(\"Gannon\"); // 初始化first属性 name.setLast(\"gates\"); // 初始化last属性 person.setName(name); Example example = Example.create(person); example.excludeZeroes(); // person对象中值为0的属性不值出现where 子句中 example.excludeProperty(\"birthday\"); // birthday不会出现在where子句中 crit.add(example); List select this_.id as id1_0_, this_.birthday as birthday1_0_, this_.first as 第20章 Hibernate的查询与更新技术 first1_0_, this_.last as last1_0_ from t_persons this_ where (this_.first=? and this_.last=?) 使用Example类的enableLike方法可以将所有String类型的属性都使用like,而不是用“=”进行查询,如下面的代码所示: Criteria crit = session.createCriteria(Person.class); Person person = new Person(); java.util.Calendar cal = java.util.Calendar.getInstance(); cal.set(1955, 4, 7); person.setBirthday(cal.getTime()); Name name = new Name(); name.setFirst(\"Gannon\"); name.setLast(\"gates\"); person.setName(name); Example example = Example.create(person); example.enableLike(); // 所有String类型的属性都使用like关键字进行查询 crit.add(example); List 在执行上面的代码后,Hibernate将生成如下的SQL语句。 在使用Example类生成查询条件时要注意,实体Bean中参与查询的属性之间的关系都是and。 select this_.id as id1_0_, this_.birthday as birthday1_0_, this_.first as first1_0_, this_.last as last1_0_ from t_persons this_ where (this_.birthday=? and this_.first like ? and this_.last like ?) 20.2 HQL和SQL技术 HQL(Hibernate Query Language)是Hibernate框架提供的另一种操作数据的方式。其在语法上非常接近SQL,但它们不同的是HQL是面向对象的,也就是说,HQL所操作的都是持久化对象,而不是表。使用HQL操作数据可以直接返回相应的持久化对象。另外,Hibernate也可以将SQL查询出来的数据转换为持久化对象。 20.2.1 实例:使用HQL的第一个例子 最简单的HQL查询是根据指定的实体Bean返回所有满足条件的持久化对象。在这个查询中只含有一个from关键字,from后面直接跟着实体Bean的类名,如下面的代码所示: from MyMessage 注意:HQL和SQL不同,在HQL语句中实体Bean的名称和属性名是区分大小写的, 而SQL语句中的表名和字段名不区分大小写,但HQL语句中的关键字不区分大小写,如from也可以写成From。 通过编程方式执行HQL语句首先要使用Session接口的createQuery方法建立一个 ·625· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX Query对象。然后使用Query类的list方法获得满足条件的持久化对象。createQuery方法的定义如下: 下面的代码是一个执行HQL语句的例子,在这个例子中返回了MyMessage类的所有持久化对象。 public Query createQuery(String queryString) throws HibernateException; 与标准查询API一样,Hibernate也会根据HQL语句来生成相应的SQL语句,如执行上面的代码后,Hibernate将生成如下的SQL语句的HQL语句。 package chapter20; import hibernate.*; import org.hibernate.*; import chapter17.entity.*; import java.util.*; public class FirstHQL { public static void main(String[] args) { Session session = HibernateSessionFactory.getSession(); String hql = \"from MyMessage\"; // 定义HQL语句 Query query = session.createQuery(hql); // 通过提定的SQL语句创 建Query对象 List // 输出当前MyMessage对象的id和name属性值 System.out.print(message.getId() + \" \" + message.getName()); System.out.println(); } session.close(); } } select mymessage0_.id as id0_, mymessage0_.name as name0_ from t_message mymessage0_ 20.2.2 From子句:简化实体Bean类名 在20.2.1节已经接触过from子句了。from后面可直接跟实体Bean的类名,但有时实体Bean的类名较长,使用不方便,因此,需要为较长的类名起一个别名,代码如下: 其中as关键字是可选的,因此,也可以使用如下的HQL语句为实体Bean起别名。 如果类名和当前类引用名中的类型冲突时,也可以使用package.classname的方式,代码如下: from chapter17.entity.MyMessage m ·626 · from MyMessage m from MyMessage as m 第20章 Hibernate的查询与更新技术 如果from后面有多个类名,中间用逗号(,)分隔,代码如下: from后面不但可以跟实体Bean,也可以跟实体Bean的父类,如下面的HQL语句 所示: 由于Object类是所有Java类的父类,因此,上面的HQL将返回所有在Hibernate映射文件中定义的实体Bean。Hibernate会根据上面的HQL语句生成如下的SQL语句。 from java.lang.Object from EntityBean1 as b1, EntityBean2 as b2 如果想让Hibernate生成查询相关实体Bean对象的SQL语句,可以建立一个父类,让这些实体Bean都继承这个父类,如有一个Father类,MyMessage和Employee都继承于该类,代码如下: public class Father { ... } public class MyMessage extends Father { ... } public class Employee extends Father { ... } select product0_.id as id7_, product0_.name as name7_ from t_products product0_ select productdet0_.id as id8_0_, productdet0_.detail as detail8_0_ from t_product_details productdet0_ where productdet0_.id=? select address0_.id as id6_, address0_.address as address6_ from t_addresses address0_ select productdet0_.id as id8_, productdet0_.detail as detail8_ from t_product_details productdet0_ select person0_.id as id2_, person0_.birthday as birthday2_, person0_.first as first2_, person0_.last as last2_ from t_persons person0_ select order0_.id as id4_, order0_.order_number as order2_4_, order0_.customer_id as customer3_4_ from t_orders order0_ select mymessage0_.id as id0_, mymessage0_.name as name0_ from t_message mymessage0_ select keys0_.key1 as key1_1_, keys0_.key2 as key2_1_, keys0_.data as data1_ from t_keys keys0_ select customer0_.id as id3_, customer0_.name as name3_ from t_customers customer0_ select employee0_.id as id5_, employee0_.name as name5_, employee0_. address_id as address3_5_ from t_employees employee0_ 可以使用如下的HQL生成查询MyMessage和Employee对象的SQL语句。 from chapter20.entity.Father 上面的HQL语句生成的SQL语句如下: select mymessage0_.id as id0_, mymessage0_.name as name0_ from t_message mymessage0_ select employee0_.id as id5_, employee0_.name as name5_, employee0_. ·627· Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+AJAX address_id as address3_5_ from t_employees employee0_ 注意:如果 from后面跟的是未在Hibernate映射文件中映射的实体Bean,必须使用package.classname的形式,如使用Father类时,必须使用chapter20.entity.Father,而不能只使用Father。 20.2.3 Select子句:选择返回属性 Select子句要比From子句提供了更多的控制。通过Select语句,可以选择返回的属性。如下面的HQL语句只返回MyMessage对象的name属性。 如果使用Select语句,Hibernate就不会返回相应的持久化对象,而是根据情况返回不同类型的List对象。如上面的HQL语句只返回了name属性,那么Hibernate就会返回List标签。
> getRecords() {
> records) {