文章目錄
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 p.*,e.* 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());
}
p.* 這種是代表某個表中的所有字段。
處理本地關聯查詢
java.util.List<Object[]> list = ss.createNativeQuery("select * from phone e join person p where p.person_id = e.person_id").addEntity("phone",Phone.class).addJoin("pr111","phone.person")
.getResultList();
for(int i = 0;i<list.size();i++){
Object[] elemets = list.get(i);
Person p = (Person) elemets[1];//經過上述的實體轉換,我們在獲取的時候需要注意,如果多表查詢,例如,本例,如果在獲取的時候Person p = (Person) elemets[0],就會發生錯誤,實體phone 無法轉成person,因爲在sql 你是通過phone 去調用addjoin ,所以先是phone ,再是person。
System.out.println(p.getName()+p.getGender());
Phone p1 = (Phone) elemets[0];
System.out.println(p1.getNumber()+p1.getPhone_name());
}
在phone 實體中一個Person person 對象引用,Many to one 的關係。在這裏使用addEntity和上面的不一樣,這裏設置的實體,應該是你的主表,而不是關聯表。因爲轉換實體的時候,如果設置關聯表,字段會出現重複可能,所以你需要給個名字表示轉換後實體名,另外這裏雖然涉及到兩張表,但是addJoin會通過phone關聯屬性,自動轉換爲person 對應的實體,所以不需要手動addEntity person了。 addJoin這個方法是添加關聯的屬性,第一個參數,是轉換後的實體名(這個命名隨意),後面的是關聯的屬性,這裏命名就不是隨意,必須是你前面實體名.對應關聯類的屬性,他會根據addEntity的對應實體名,去對應的類找關聯的屬性。
上面關係是雙向的,所以,你也可以通過person 去訪問phone。
java.util.List<Object[]> list = ss.createNativeQuery("select * from phone e join person p where p.person_id = e.person_id").addEntity("person",Person.class).addJoin("phone","person.phones")//可以看到該數組第一個是person,第二個纔是phone 對象。
.getResultList();
for(int i = 0;i<list.size();i++){
Object[] elemets = list.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());
}
本地sql 查詢設置參數
java.util.List<Object[]> list = ss.createNativeQuery("select person_name from person where person_name like :name1 ").setParameter("name1","lao")
.getResultList();
注意雖然用法很像hql,但是又本質區別,它裏面可不是字段和對象,而是底層數據庫的表、字段。
按照SQL標準的解釋,在SQL環境下Catalog和Schema都屬於抽象概念,主要用來解決命名衝突問題。
從概念上說,一個數據庫系統包含多個Catalog,每個Catalog又包含多個Schema,而每個Schema又包含多個數據庫對象(表、視圖、序列等),反過來講一個數據庫對象必然屬於一個Schema,而該Schema又必然屬於一個Catalog,這樣我們就可以得到該數據庫對象的完全限定名稱從而解決命名衝突的問題了