命名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()作用,將結果集轉換爲標量查詢,有幾個@ColumnResult註解就相當於調用幾次SQLQuery的addScalar()方法。
同樣@EntityResult註解作用類似於SQLQuery對象的addEntity()作用,將結果集轉換爲標量查詢,有幾個@EntityResult註解就相當於調用幾次SQLQuery的addEntity()方法。
下面是實例:
一:先是通過resultClass就可以完成結果轉換的簡單查詢
// 定義一個命名SQL查詢,其名稱爲simpleQuery
@NamedNativeQuery(name="simpleQuery"
// 指定命名SQL查詢對應的SQL語句
, query="select s.student_id , s.name 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 list = session.getNamedQuery("simpleQuery")
.list();
tx.commit();
HibernateUtil.closeSession();
// 遍歷結果集
for(Object ele : list)
{
// 每個集合元素是Student對象
Student s = (Student)ele;
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" ,type=String.class)}
)
可以看出我們通過@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({})管理