命名SQL查询顾名思义就是将SQL语句从程序中抽出来,放在注解中管理,然后给每个SQL查询起一个名称,在程序中仅需要调用此名称即可,从而可以更好的提高程序的解耦。
hibernate允许使用@NamedNativeQuery注解来定义命名的原生SQL查询,如果有多个命名查询,则使用@NamedNativeQueries注解来管理。
下面@NamedNativeQuery注解支持的属性:
name:指定命名SQL查询的名称 必须具备属性
query 指定命名SQL查询的查询语句 必须具备属性
resultClass 在简单查询中将查询结果映射成指定实体类的实例,类似于addEntity()作用。 非必须具备属性
resultSetMapping 该属性指定一个SQL结果映射的名称(该结果映射需要使用@SqlResultSetMapping定义),
用于使用该结果映射来转换查询结果集。
简单查询时我们使用resultClass将查询结果转化为相应实体就可以了,但是如果是查询数据列较多,而且程序希望同时进行标量查询、实体查询,那就必须借助于resultSetMapping。
如果需要使用@NamedNativeQuery注解指定resultSetMapping属性(也就是需要复杂查询时)则还需要使用@SqlResultSetMapping定义SQL结果映射,@SqlResultSetMapping的作用就是将查询到的结果集转换为标量查询或者实体查询,类似于SQLQuery对象的addScalar()或者addEntity()方法的功能。
可能对于resultSetMapping和@SqlResultSetMapping两者容易混淆,搞不清楚到底是干什么的,通俗点讲就是 @SqlResultSetMapping是制定结果映射规则的,结果集是转化为标量查询还是实体查询我制定。 resultSetMapping是通过上面制定的映射名称引用这个结果映射的。
下面是@SqlResultSetMapping支持的属性
name 指定SQL结果映射的名称(上面说的,咱们通过resultSetMapping引用)必需属性
columns 该属性的值为@ColumnResult注解数组,每个@ColumnResult注解定义一个标量查询
entities 该属性的值为@EntityResult注解数组,每个@EntityResult注解定义一个实体查询
class 该属性的值为@ConstructorResult注解数组,每个@ConstructorResult负责将指定的多列转化为普通类对应的 属性
上面的@ColumnResult注解作用类似于SQLQuery对象的addScalar()作用,将结果集转换为标量查询
同样@EntityResult注解作用类似于SQLQuery对象的addEntity()作用,将结果集转换为标量查询
下面是实例:
一:先是通过resultClass就可以完成结果转换的简单查询
- // 定义一个命名SQL查询,其名称为simpleQuery
- @NamedNativeQuery(name="simpleQuery"
- // 指定命名SQL查询对应的SQL语句
- , query="select s.* from student_inf s"
- // 指定将查询结果转换为Student实体
- , resultClass=Student.class)
- @Entity
- @Table(name="student_inf")
- public class Student
- {
- // 代表学生学号的成员变量,将作为标识属性
- @Id @Column(name="student_id")
- private Integer studentNumber;
- // 代表学生姓名的成员变量
- private String name;
- // 该学生的所有选课记录对应的关联实体
- @OneToMany(targetEntity=Enrolment.class
- , mappedBy="student" , cascade=CascadeType.REMOVE)
- private Set<Enrolment> enrolments
- = new HashSet<>();
- // 无参数的构造器
- public Student()
- {
- }
- // 初始化全部成员变量的构造器
- public Student(Integer studentNumber , String name)
- {
- this.studentNumber = studentNumber;
- this.name = name;
- }
- // studentNumber的setter和getter方法
- public void setStudentNumber(Integer studentNumber)
- {
- this.studentNumber = studentNumber;
- }
- public Integer getStudentNumber()
- {
- return this.studentNumber;
- }
- // name的setter和getter方法
- public void setName(String name)
- {
- this.name = name;
- }
- public String getName()
- {
- return this.name;
- }
- // enrolments的setter和getter方法
- public void setEnrolments(Set<Enrolment> enrolments)
- {
- this.enrolments = enrolments;
- }
此例就是通过@NamedNativeQuery定义了一个simpleQuery的SQL查询,因为此查询较简单,所以通过 @NamedNativeQuery的resultClass属性将结果集映射Student实体就可以了,这样该命名查询查询得到的结果应该是集合元素Student的List集合
下面方法即可执行上面命名的SQL查询
- public class NamedSQLTest
- {
- public static void main(String[] args)
- {
- NamedSQLTest test = new NamedSQLTest();
- test.simpleQuery();
- HibernateUtil.sessionFactory.close();
- }
- // 执行简单的命名SQL查询
- private void simpleQuery()
- {
- // 打开Session和事务
- Session session = HibernateUtil.currentSession();
- Transaction tx = session.beginTransaction();
- // 调用命名查询,直接返回结果
- List<Student> list = session.getNamedQuery("simpleQuery")
- .list();
- tx.commit();
- HibernateUtil.closeSession();
- // 遍历结果集
- for(Studentele : list)
- {
- // 每个集合元素是Student对象
- System.out.println(s.getName() + "\t");
- }
- }
- }
二:基于标量查询、实体查询的复杂查询
首先我们定义一个复杂的命名SQL查询
- // 定义一个命名SQL查询,其名称为queryTest
- @NamedNativeQuery(name="queryTest"
- // 定义SQL语句
- , query="select s.*,e.*,c.* from student_inf s,enrolment_inf e,"
- + " course_inf c where s.student_id = e.student_id and"
- + " e.course_code = c.course_code and e.year=:targetYear"
- // 指定使用名为firstMapping的@SqlResultSetMapping完成结果映射
- , resultSetMapping = "firstMapping")
此查询语句复杂,查询数据列较多,因此我们使用firstMapping结果映射来负责结果集的转换。这里使用的是名为firstMapping结果映射,下面是 firstMapping结果映射的定义:
- @SqlResultSetMapping(name="firstMapping"
- ,entities={@EntityResult(entityClass=Student.class),
- @EntityResult(entityClass=Enrolment.class),
- @EntityResult(entityClass=Course.class,fields=
- {
- @FieldResult(name="id" , column="c.course_code"),
- @FieldResult(name="name" , column="c.name")
- }
- )}
- ,columns={@ColumnResult(name="s.name")}
- )
可以看出我们通过@SqlResultSetMapping定义了一个 firstMapping结果映射,另外通过entities指定了三个@EntityResult注解,通过columns指定了一个@ColumnResult,这就说明该SQL查询包含了三个实体查询,一个标量查询。
以下方法可以执行上面的命名SQL查询:
- public class NamedSQLTest
- {
- public static void main(String[] args)
- {
- NamedSQLTest test = new NamedSQLTest();
- test.query();
- HibernateUtil.sessionFactory.close();
- }
- // 执行命名SQL查询
- private void query()
- {
- // 打开Session和事务
- Session session = HibernateUtil.currentSession();
- Transaction tx = session.beginTransaction();
- // 调用命名查询,直接返回结果
- List list = session.getNamedQuery("queryTest")
- .setInteger("targetYear" , 2005)
- .list();
- tx.commit();
- HibernateUtil.closeSession();
- // 遍历结果集
- for(Object ele : list)
- {
- // 每个集合元素是Student、Enrolment
- // 和stuName三个元素的数组
- Object[] objs = (Object[])ele;
- Student s = (Student)objs[0];
- Enrolment e = (Enrolment)objs[1];
- Course c = (Course)objs[2];
- String stuName = (String)objs[3];
- System.out.println(s.getName() + "\t"
- + e.getYear() + "\t" + e.getSemester()
- + "\t=" + e.getCourse().getName() + "=\t" + stuName);
- }
- }
- }
这里可以看出返回的结果集合,每一个集合元素都是几个实体所组成的数组,这里前三个数组元素正是我们定义的三个实体查询,第四个数组元素使我们定义的标量查询。
另外:当我们需要定义多个@NamedNativeQuery时,我们使用@NamedNativeQueries({})来管理,
当我们需要定义多个@SqlResultSetMapping时,我们使用@SqlResultSetMappings({})管理