Hibernate 5.3(八)
Criteria
條件查詢是更具面向物件特色的資料查詢方式。它是一種型別安全的查詢方式,用來替代HQL。它是如何保證型別安全的呢?它使用的是強型別這種方式去構造criteria 查詢的,利用的就是靜態元模型。
靜態元模型
這個東西,是個什麼東東,我在簡單說一下,當我們在查詢的時候,我們面向物件查詢的,所以要寫屬性,你必須要記住屬性名,這就很麻煩了,但是通過靜態元模型,會通過工具自動幫你去生成靜態元模型類,直接通過類去找屬性,就很爽。
配置靜態元模型生成
這裡是基於Myeclipse,其他配置自行百度。
在這裡首先需要注意Hibernate 5.3 靜態元模型,jdk 要在1.8 否則會編譯不通過。
右擊專案-屬性
注意這裡產生目錄,可以使用預設,也可以是自己設定。
經過上面兩步的設定,如果專案能正確的編譯,那麼恭喜你,沒問題了。
自動生成靜態元資料
我們這裡是基於Hibernate註解,還可以是基於persistence.xml。
@Entity//這裡註解不可以少,不然,無法為該實體類生成靜態元資料
public class Phone {
private Integer number;
private Integer number_id;
private String phone_name;
}
產生的靜態元資料類如下:
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor") @StaticMetamodel(Phone.class) public abstract class Phone_ { public static volatile SingularAttribute<Phone, Integer> number; public static volatile SingularAttribute<Phone, Integer> number_id; public static volatile SingularAttribute<Phone, String> phone_name; public static final String NUMBER = "number"; public static final String NUMBER_ID = "number_id"; public static final String PHONE_NAME = "phone_name"; }
注意該類是無法修改,他會隨著你實體類變化去自動生成的。
Criteria 查詢順序
- 獲得Hibermate的Session物件。
- 以Session物件建立CriteriaBuilder 物件。
- 通過CriteriaBuilder 的createQuery方法,通過泛型去設定查詢返回的例項型別。
- 通過CriteriaBuilder 的from方法,指定查詢參與的表(例項)
- 通過CriteriaBuilder 的select 去設定查詢返回屬性。
- 使用CriteriaBuilder 物件的方法建立查詢條件(可以是where 子句的條件,也可以是select 子句後面的聚集函式)。
- 執行CriteriaBuilder 的getResultList方法返回結果集。
先睹為快criteria查詢
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
//這裡查詢的屬性 是一個Person 實體,返回的是所有屬性
Root<Person> root = criteria.from(Person.class);
//criteria.select(root);這裡其實只是查詢整個實體,所以select 是不強制有的
criteria.select(root.get((Person_.ID)));//查詢指定的屬性
criteria.where(builder.equal(root.get(Person_.NAME),"laoqiang"));
java.util.List list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
Root
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createQuery(Tuple.class);
Root<Person> root = criteria.from(Person.class);
Root<Person> root1 = criteria.from(Person.class);
criteria.multiselect(root,root1);
java.util.List <Tuple> list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
這段程式碼兩個root 物件跨越兩張表,實際上底層操作,是將這兩個表進行了笛卡爾積運算。這裡可以理解root 控制查詢的表的內容,幾個表之間查詢。
Join
可以通過關聯的屬性,進行內連線查詢。
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
Join<Person,Phone> phoneJoin = root.join(Person_.PHONES);
criteria.select(root);
criteria.where(builder.equal(root.get(Person_.ID), 3));
java.util.List <Person> list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
Set<Phone> phone = list.get(0).getPhones();
Iterator<Phone> iterator = phone.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next().getPhone_name());
}
對應的資料庫語句:
select
person0_.person_id as person_i1_0_,
person0_.name_first as name_fir2_0_,
person0_.name_last as name_las3_0_,
person0_.person_name as person_n4_0_,
person0_.person_gender as person_g5_0_
from
PERSON person0_
inner join
PHONE phones1_
on person0_.person_id=phones1_.person_id
where
person0_.person_id=3
通過join 的關聯,無法去獲取phone 欄位,但是可以通過person 關聯欄位去獲取對應phone 欄位。
Fecth
可以通過關聯的屬性去獲取關聯實體的欄位資訊。
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
Fetch<Person,Phone> phoneJoin = root.fetch(Person_.PHONES);
criteria.select(root);
criteria.where(builder.equal(root.get(Person_.ID), 3));
java.util.List <Person> list = ss.createQuery(criteria).getResultList();
底層資料庫執行語句:
select
person0_.person_id as person_i1_0_0_,
phones1_.phone_id as phone_id1_1_1_,
person0_.name_first as name_fir2_0_0_,
person0_.name_last as name_las3_0_0_,
person0_.person_name as person_n4_0_0_,
person0_.person_gender as person_g5_0_0_,
phones1_.phone_number as phone_nu2_1_1_,
phones1_.phone_name as phone_na3_1_1_,
phones1_.person_id as person_i4_1_1_,
phones1_.person_id as person_i4_1_0__,
phones1_.phone_id as phone_id1_1_0__
from
PERSON person0_
inner join
PHONE phones1_
on person0_.person_id=phones1_.person_id
where
person0_.person_id=3
查詢返回多個屬性
兩種方式:
CriteriaBuilder builder = ss.getCriteriaBuilder();
//查詢多個屬性這裡一定要指定Object[] 泛型,多個屬性的型別不一致
CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class);
Root<Person> root = criteria.from(Person.class);
javax.persistence.criteria.Path<String> genderPath = root.get(Person_.GENDER);
javax.persistence.criteria.Path<String> namePath = root.get(Person_.NAME);
//path 後面泛型型別是參照你定義該屬性的型別
criteria.select(builder.array(genderPath,namePath));//指定查詢的多個屬性
criteria.where(builder.equal(root.get(Person_.NAME),"laoqiang"));
java.util.List list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
CriteriaBuilder builder = ss.getCriteriaBuilder();
//查詢多個屬性這裡一定要指定Object[] 泛型,多個屬性的型別不一致
CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class);
Root<Person> root = criteria.from(Person.class);
javax.persistence.criteria.Path<String> genderPath = root.get(Person_.GENDER);
javax.persistence.criteria.Path<String> namePath = root.get(Person_.NAME);
//path 後面泛型型別是參照你定義該屬性的型別
criteria.multiselect(genderPath,namePath);//指定查詢的多個屬性
criteria.where(builder.equal(root.get(Person_.NAME),"laoqiang"));
java.util.List list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
我們可以構造一個包裝類物件,將多個屬性存放其中,避免了使用Object陣列。
包裝類用來封裝查詢的多個物件
public class PersonWrapper {
private String name;
private String gender;
public PersonWrapper(String name, String gender) {
super();
this.name = name;
this.gender = gender;
}
}
CriteriaBuilder builder = ss.getCriteriaBuilder();
//我們將多個查詢屬性封裝在一個類,作為一個物件,這樣就不需要Object[]
CriteriaQuery<PersonWrapper> criteria = builder.createQuery(PersonWrapper.class);
Root<Person> root = criteria.from(Person.class);
javax.persistence.criteria.Path<String> genderPath = root.get(Person_.GENDER);
javax.persistence.criteria.Path<String> namePath = root.get(Person_.NAME);
//path 後面泛型型別是參照你定義該屬性的型別
criteria.select(builder.construct(PersonWrapper.class,namePath ,genderPath));//指定查詢的多個屬性,這裡構造的的屬性順序,全好和你定義包裝類的構造器一致,以免有問題。
criteria.where(builder.equal(root.get(Person_.NAME),"laoqiang"));
java.util.List list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
在上面我們通過將多個屬性包裝成一個物件類,這樣其實是有缺點,你還要單獨去建立該類,明顯就麻煩,下面我們通過Tuple,英文意思:元祖。你資料庫查詢的某一行是不是就是一元祖,它其實是把多個屬性封裝在元祖物件中,讓你直接用。
CriteriaBuilder builder = ss.getCriteriaBuilder();
//我們將多個查詢屬性封裝在一個類,作為一個物件,這樣就不需要Object[]
CriteriaQuery<Tuple> criteria = builder.createQuery(Tuple.class);
Root<Person> root = criteria.from(Person.class);
javax.persistence.criteria.Path<String> genderPath = root.get(Person_.GENDER);
javax.persistence.criteria.Path<String> namePath = root.get(Person_.NAME);
//path 後面泛型型別是參照你定義該屬性的型別
criteria.multiselect(namePath,genderPath);
criteria.where(builder.equal(root.get(Person_.NAME),"laoqiang"));
java.util.List <Tuple> list = ss.createQuery(criteria).getResultList();
for(Tuple tuple: list){
System.out.println(tuple.get(namePath));
//在獲取值的時候,可以通過path 物件名去獲取,也可以通過索引,索引是從0,和構造的順序一致
System.out.println(tuple.get(0));
System.out.println(tuple.get(genderPath));
System.out.println(tuple.get(1));
}
這種方式是推薦使用的。
查詢使用引數
在sql 中可以使用佔位符來表示引數,criteria 一樣可以,只不過換了一個形式,意思還是一樣一樣的。
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
criteria.select(root);
ParameterExpression<Integer> personidparameter = builder.parameter(Integer.class);//這裡是引數的型別
criteria.where(builder.equal(root.get(Person_.ID), personidparameter));
java.util.List <Person> list = ss.createQuery(criteria).setParameter(personidparameter,3).getResultList();//這裡設定引數,兩種方式,一個根據索引,一個可以引數名
System.out.println(list.size());
分組
CriteriaBuilder builder = ss.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createQuery(Tuple.class);
Root<Person> root = criteria.from(Person.class);
criteria.groupBy(root.get(Person_.ID));
criteria.multiselect(root.get(Person_.ID),builder.count(root));//這裡必須保證分組的欄位要在select 子句中有
//Tuple 這裡查詢必須要是該型別,否則會提示沒有該對應的構造器。
criteria.where(builder.equal(root.get(Person_.ID), 3));
java.util.List <Tuple> list = ss.createQuery(criteria).getResultList();
System.out.println(list.size());
本地sql 查詢
允許你手寫sql 語句,儲存過程等,去完成一系列的資料庫操作。在本地sql 中寫的就是實際資料庫的欄位和表。
java.util.List<Object[]> person = ss.createNativeQuery("select * from person").getResultList();//這樣查詢是整個實體屬性,因為實體屬性不同,所以存放在一個object 陣列中
for(Object[] o:person){
System.out.println(o[0]);//這裡獲取的索引就是和你查詢欄位位置一致
System.out.println(o[1]);
System.out.println(o[2]);
System.out.println(o[3]);
System.out.println(o[4]);
System.out.println(o[5]);
System.out.println(o[6]);
}
Hibernate將使用java.sql.ResultSetMetadata來推斷返回的標量值的實際順序和型別。
標量查詢
頻繁的使用ResultSetMetadata 去推測型別會耗費效能,為了減少對ResultSetMetadata依賴去猜測實際返回的查詢和型別,我們需要指定查詢返回欄位的型別。
java.util.List<Object[]> person = ss.createNativeQuery("select person_id,person_name from person")
.addScalar("person_id", IntegerType.INSTANCE).addScalar("person_name",StringType.INSTANCE).getResultList();
for(Object[] o:person){
System.out.println(o[0]);
System.out.println(o[1]);
}
實體查詢
hibernate 提供本地sql 查詢的結果,可以直接轉化為實體的屬性。但是使用,需要注意必須是查詢的所有屬性,select *。
java.util.List<Person> person = ss.createNativeQuery("select * from person").addEntity(Person.class)
.getResultList();
for(int i = 0;i<person.size();i++){
Person p = person.get(i);
System.out.println(p.getName()+p.getGender());
}
可以將查詢多個轉化成實體,還是注意是查詢兩個例項的所有,不是部分欄位。
java.util.List<Object[]> person = ss.createNativeQuery("select * from person p,phone e where p.person_id = e.person_id").addEntity(Person.class).addEntity(Phone.class)
.getResultList();
for(int i = 0;i<person.size();i++){
Object[] elemets = person.get(i);
Person p = (Person) elemets[0];
System.out.println(p.getName()+p.getGender());
Phone p1 = (Phone) elemets[1];
System.out.println(p1.getNumber()+p1.getPhone_name());
}