1. 程式人生 > >Hibernate 5.3(八)

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 查詢順序
  1. 獲得Hibermate的Session物件。
  2. 以Session物件建立CriteriaBuilder 物件。
  3. 通過CriteriaBuilder 的createQuery方法,通過泛型去設定查詢返回的例項型別。
  4. 通過CriteriaBuilder 的from方法,指定查詢參與的表(例項)
  5. 通過CriteriaBuilder 的select 去設定查詢返回屬性。
  6. 使用CriteriaBuilder 物件的方法建立查詢條件(可以是where 子句的條件,也可以是select 子句後面的聚集函式)。
  7. 執行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());
		  }