SQL查询是通过SQLQuery接口来表示的。SQLQuery接口是Query接口的子接口,因此完全可以调用Query接口的方法。
执行SQL查询的步骤是:
- 获取Hibernate Session对象。
- 编写SQL语句。
- 以SQL语句作为参数,调用Session的createSQLQuery()方法创建查询对象。
- 调用SQLQuery对象的addScalar()或addEntity()方法将选出的结果与标量值或实体进行关联,分别用于进行标量查询或实体查询。
- 如果SQL语句包含参数,则调用Query的setXxx()方法为参数赋值。
- 调用Query的list()方法或uniqueResult()方法返回查询的结果集。
1.标量查询
明确指定返回值类型通过addScalar()方法来实现。
session.createSQLQuery("select * from student").addScalar("name",StandardBasicTypes.STRING).list();
上面查询指定了SQL查询的如下三个信息:
- SQL字符串。
- 查询返回的字段列表。
- 查询返回的各字段类型。
即使使用了*作为查询的字段列表(ResultSet返回多个字段),但Hibernate查询的结果也只是name字段组成的列表。
addScalar(String columnAlias)方法,这个方法只指定查询需要返回该字段,并不指定该字段的数据类型。
2.实体查询
如果查询返回了某个数据表的全部数据列,且该数据表有对应的持久化类映射,接下来就可把查询结果转换成实体。将查询结果转换成实体,可以使用SQLQuery提供的多个重载的addEntity()方法。
String sqlString = "select * from enrolment where year=?1";
List list = session.createSQLQuery(sqlString)
//指定将查询的记录行转换成Student实体
.addEntity(Enrolment.class)
.setInteger("1",2005)
.list();
使用原生SQL查询必须注意的是,程序必须选出所有数据列才可被转换成持久化实体。
如果要将查询结果转换成多个实体,则SQL字符串中应为不同的数据表指定不同的别名,并调用addEntity(String alias,Class entityClass)方法将不同的数据表转换成不同的实体。
Hibernate还可将查询结果转换成非持久化实体(即普通JavaBean),只要该JavaBean为这些数据列提供了对应的setter和getter方法。
Query接口提供了一个setResultTransformer()方法,该方法可接受一个Transformers对象,通过使用该对象即可把查询到的结果集转换成JavaBean集。
3.处理关联和继承
只要原生SQL查询选出了足够的数据列,则程序除了可以将指定数据列转换成持久化实体之外,还可以将实体的关联实体(通常以属性的形式存在)转换成查询结果。将关联实体转换成查询结果的方法时SQLQuery addJoin(String alias,String path),该方法的第一个参数是转换后的实体名,第二个参数是待转换的实体属性。
如果使用原生SQL查询的结果实体是继承树中的一部分,则查询的SQL字符串必须包含基类和所有子类的全部属性。
4.命名SQL查询
@NamedNativeQuery注解来定义命名的原生SQL查询,如果程序有多个命名的原生SQL查询需要定义,则可使用@NamedNativeQueries注解。
如果需要为@NamedNativeQuery注解指定了resultSetMapping属性,则还需要使用@SqlResultSetMapping定义SQL结果映射,@SqlResultSetMapping的作用是将查询得到的结果集转换为标量查询或实体查询—基本等同于SQLQuery对象的addScalar()或addEntity()方法的功能。
@ColumnResult注解的作用类似于SQLQuery的addScalar()方法的作用。
@EntityResult注解的作用类似于SQLQuery的addEntity()方法的作用。
@ConstructorResult注解的作用有点类似于Query的setResultTransformer()方法的作用。
@NamedNativeQuery(name="simpleQuery",query="select s.student_id,s.name from student s",resultClass=Student.class)
private void simpleQuery(){
List list = session.getNamedQuery("simpleQuery").list();
}
5.调用存储过程
对于函数,该函数必须返回一个结果集;对于存储过程,该存储过程的第一个参数必须是传出参数,且其数据类型是结果集。
//创建一个简单的存储过程
create procedure select_all_student()
select *
from student;
@NamedNativeQuery(name="classProcedure",query="{call select_all_student()}",resultSetMapping="secondMapping")
@SqlResultSetMapping(name="secondMapping",entities={
@EntityResult(entityClass=Student.class,fields={
@FieldResult(name="studentNumber",column="student_id"),
@FieldResult(name="name",column="name")
})
})
6.使用定制SQL
通过使用定制SQL可以完全控制Hibernate底层持久化所用的SQL语句。
Hibernate本身为定制SQL提供了如下注解:
- @SQLInsert:定制插入记录的SQL语句。
- @SQLUpdate:定制更新记录的SQL语句。
- @SQLDelete:定制删除记录的SQL语句。
- @SQLDeleteAll:定制删除所有记录的SQL语句。
使用上面4个注解时都需要指定一个sql属性。
一旦使用上面注解修饰了某个实体类,Hibernate将不再使用默认的SQL语句来执行CRUD操作,而是使用此处定制的SQL语句进行。
如果希望使用存储过程来执行CRUD等操作,则只需为@SQLInsert、@SQLUpdate、@SQLDelete或@SQLDeleteALL指定callable=true即可。
使用这种用法时参数的顺序很重要,调用存储过程的顺序必须和Hibernate所期待的顺序相同。
想看静态SQL的预计顺序时,先不要使用定制SQL功能,等记录了Hibernate静态SQL所期待的顺序后,再到持久化注解中配置定制SQL。
因为Hibernate会检查SQL语句是否执行成功(通过查看SQL语句执行结束后的返回值),所以应该让存储过程能返回该存储过程所影响的记录条数。Hibernate通常把CUD操作语句的第一个参数注册为数值型输出参数,所以应让存储过程的第一个传出参数记录该存储过程所影响的记录条数。