第一:關於cretiria的查詢
具有一個直觀的、可擴展的條件查詢API是Hibernate的特色。
15.1. 創建一個Criteria 實例
org.hibernate.Criteria接口表示特定持久類的一個查詢。Session是 Criteria實例的工廠。
- <span style="font-size: medium;">Criteria crit = sess.createCriteria(Cat.class);
- crit.setMaxResults(50);
- List cats = crit.list();</span>
15.2. 限制結果集內容
一個單獨的查詢條件是org.hibernate.criterion.Criterion 接口的一個實例。org.hibernate.criterion.Restrictions類 定義了獲得某些內置Criterion類型的工廠方法。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.like("name", "Fritz%") )
- .add( Restrictions.between("weight", minWeight, maxWeight) )
- .list();
- </span>
約束可以按邏輯分組。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.like("name", "Fritz%") )
- .add( Restrictions.or(
- Restrictions.eq( "age", new Integer(0) ),
- Restrictions.isNull("age")
- ) )
- .list();
- List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
- .add( Restrictions.disjunction()
- .add( Restrictions.isNull("age") )
- .add( Restrictions.eq("age", new Integer(0) ) )
- .add( Restrictions.eq("age", new Integer(1) ) )
- .add( Restrictions.eq("age", new Integer(2) ) )
- ) )
- .list();</span>
Hibernate提供了相當多的內置criterion類型(Restrictions 子類), 但是尤其有用的是可以允許你直接使用SQL。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
- .list();
- </span>
{alias}佔位符應當被替換爲被查詢實體的列別名。
Property實例是獲得一個條件的另外一種途徑。你可以通過調用Property.forName() 創建一個Property。
- <span style="font-size: medium;">Property age = Property.forName("age");
- List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.disjunction()
- .add( age.isNull() )
- .add( age.eq( new Integer(0) ) )
- .add( age.eq( new Integer(1) ) )
- .add( age.eq( new Integer(2) ) )
- ) )
- .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
- .list();</span>
15.3. 結果集排序
你可以使用org.hibernate.criterion.Order來爲查詢結果排序。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.like("name", "F%")
- .addOrder( Order.asc("name") )
- .addOrder( Order.desc("age") )
- .setMaxResults(50)
- .list();
- List cats = sess.createCriteria(Cat.class)
- .add( Property.forName("name").like("F%") )
- .addOrder( Property.forName("name").asc() )
- .addOrder( Property.forName("age").desc() )
- .setMaxResults(50)
- .list();
- </span>
15.4. 關聯
你可以使用createCriteria()非常容易的在互相關聯的實體間建立 約束。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.like("name", "F%")
- .createCriteria("kittens")
- .add( Restrictions.like("name", "F%")
- .list();
- </span>
注意第二個 createCriteria()返回一個新的 Criteria實例,該實例引用kittens 集合中的元素。
接下來,替換形態在某些情況下也是很有用的。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .createAlias("kittens", "kt")
- .createAlias("mate", "mt")
- .add( Restrictions.eqProperty("kt.name", "mt.name") )
- .list();
- </span>
(createAlias()並不創建一個新的 Criteria實例。)
Cat實例所保存的之前兩次查詢所返回的kittens集合是 沒有被條件預過濾的。如果你希望只獲得符合條件的kittens, 你必須使用returnMaps()。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .createCriteria("kittens", "kt")
- .add( Restrictions.eq("name", "F%") )
- .returnMaps()
- .list();
- Iterator iter = cats.iterator();
- while ( iter.hasNext() ) {
- Map map = (Map) iter.next();
- Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
- Cat kitten = (Cat) map.get("kt");
- }
- </span>
15.5. 動態關聯抓取
你可以使用setFetchMode()在運行時定義動態關聯抓取的語義。
- <span style="font-size: medium;">List cats = sess.createCriteria(Cat.class)
- .add( Restrictions.like("name", "Fritz%") )
- .setFetchMode("mate", FetchMode.EAGER)
- .setFetchMode("kittens", FetchMode.EAGER)
- .list();
- </span>
這個查詢可以通過外連接抓取mate和kittens。 查看第 19.1 節 “ 抓取策略(Fetching strategies) ”可以獲得更多信息。
15.6. 查詢示例
org.hibernate.criterion.Example類允許你通過一個給定實例 構建一個條件查詢。
- <span style="font-size: medium;">Cat cat = new Cat();
- cat.setSex('F');
- cat.setColor(Color.BLACK);
- List results = session.createCriteria(Cat.class)
- .add( Example.create(cat) )
- .list();</span>
版本屬性、標識符和關聯被忽略。默認情況下值爲null的屬性將被排除。
你可以自行調整Example使之更實用。
- <span style="font-size: medium;">Example example = Example.create(cat)
- .excludeZeroes() //exclude zero valued properties
- .excludeProperty("color") //exclude the property named "color"
- .ignoreCase() //perform case insensitive string comparisons
- .enableLike(); //use like for string comparisons
- List results = session.createCriteria(Cat.class)
- .add(example)
- .list();</span>
你甚至可以使用examples在關聯對象上放置條件。
- <span style="font-size: medium;">List results = session.createCriteria(Cat.class)
- .add( Example.create(cat) )
- .createCriteria("mate")
- .add( Example.create( cat.getMate() ) )
- .list();
- </span>
15.7. 投影(Projections)、聚合(aggregation)和分組(grouping)
org.hibernate.criterion.Projections是 Projection 的實例工廠。我們通過調用 setProjection()應用投影到一個查詢。
在一個條件查詢中沒有必要顯式的使用 "group by" 。某些投影類型就是被定義爲 分組投影,他們也出現在SQL的group by子句中。
你可以選擇把一個別名指派給一個投影,這樣可以使投影值被約束或排序所引用。下面是兩種不同的實現方式:
- <span style="font-size: medium;">List results = session.createCriteria(Cat.class)
- .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
- .addOrder( Order.asc("colr") )
- .list();
- List results = session.createCriteria(Cat.class)
- .setProjection( Projections.groupProperty("color").as("colr") )
- .addOrder( Order.asc("colr") )
- .list();
- </span>
alias()和as()方法簡便的將一個投影實例包裝到另外一個 別名的Projection實例中。簡而言之,當你添加一個投影到一個投影列表中時 你可以爲它指定一個別名:
- <span style="font-size: medium;">List results = session.createCriteria(Cat.class)
- .setProjection( Projections.projectionList()
- .add( Projections.rowCount(), "catCountByColor" )
- .add( Projections.avg("weight"), "avgWeight" )
- .add( Projections.max("weight"), "maxWeight" )
- .add( Projections.groupProperty("color"), "color" )
- )
- .addOrder( Order.desc("catCountByColor") )
- .addOrder( Order.desc("avgWeight") )
- .list();
- List results = session.createCriteria(Domestic.class, "cat")
- .createAlias("kittens", "kit")
- .setProjection( Projections.projectionList()
- .add( Projections.property("cat.name"), "catName" )
- .add( Projections.property("kit.name"), "kitName" )
- )
- .addOrder( Order.asc("catName") )
- .addOrder( Order.asc("kitName") )
- .list();</span>
你也可以使用Property.forName()來表示投影:
- <span style="font-size: medium;">List results = session.createCriteria(Cat.class)
- .setProjection( Property.forName("name") )
- .add( Property.forName("color").eq(Color.BLACK) )
- .list();
- List results = session.createCriteria(Cat.class)
- .setProjection( Projections.projectionList()
- .add( Projections.rowCount().as("catCountByColor") )
- .add( Property.forName("weight").avg().as("avgWeight") )
- .add( Property.forName("weight").max().as("maxWeight") )
- .add( Property.forName("color").group().as("color" )
- )
- .addOrder( Order.desc("catCountByColor") )
- .addOrder( Order.desc("avgWeight") )
- .list();</span>
15.8. 離線(detached)查詢和子查詢
DetachedCriteria類使你在一個session範圍之外創建一個查詢,並且可以使用任意的 Session來執行它。
- <span style="font-size: medium;">DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
- .add( Property.forName("sex").eq('F') );
- Session session = ....;
- Transaction txn = session.beginTransaction();
- List results = query.getExecutableCriteria(session).setMaxResults(100).list();
- txn.commit();
- session.close();
- DetachedCriteria也可以用以表示子查詢。條件實例包含子查詢可以通過 Subqueries或者Property獲得。
- DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
- .setProjection( Property.forName("weight").avg() );
- session.createCriteria(Cat.class)
- .add( Property.forName("weight).gt(avgWeight) )
- .list();
- DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
- .setProjection( Property.forName("weight") );
- session.createCriteria(Cat.class)
- .add( Subqueries.geAll("weight", weights) )
- .list();</span>
甚至相互關聯的子查詢也是有可能的:
- <span style="font-size: medium;">DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
- .setProjection( Property.forName("weight").avg() )
- .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
- session.createCriteria(Cat.class, "cat")
- .add( Property.forName("weight).gt(avgWeightForSex) )
- .list();</span>
第二:HQL查詢
在HQL中關鍵字不區分大小寫,但是屬性和類名區分大小寫,下面介紹各種類型的Hibernate的HQL查詢。
1、Hibernate HQL查詢:簡單屬性查詢
* 單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致
* 多個屬性查詢,返回的集合元素是對象數組,數組元素的類型和對應的屬性在實體類中的類型一致
數組的長度取決與select中屬性的個數
* 如果認爲返回數組不夠對象化,可以採用HQL動態實例化Student對象
2、Hibernate HQL查詢:實體對象查詢
* N + 1問題,在默認情況下,使用query.iterate查詢,有可以能出現N+1問題,所謂的N+1是在查詢的時候發出了N+1條sql語句,1: 首先發出一條查詢對象id列表的sql,N: 根據id列表到緩存中查詢,如果緩存中不存在與之匹配的數據,那麼會根據id發出相應的sql語句
* list和iterate的區別?
* list每次都會發出sql語句,list會向緩存中放入數據,而不利用緩存中的數據
* iterate:在默認情況下iterate利用緩存數據,但如果緩存中不存在數據有可以能出現N+1問題
3、Hibernate HQL查詢:條件查詢
* 可以採用拼字符串的方式傳遞參數
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery("select s.id, s.name from Student s where s.name like '%1%'").list(); </span>
* 可以採用 ?來傳遞參數(索引從0開始)
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery("select s.id, s.name from Student s where s.name like ?").setParameter(0, "%1%").list(); //可以使用?方式傳遞參數 //參數的索引從0開始 //傳遞的參數值,不用單引號引起來 //注意方法鏈編程 </span>
* 可以採用 :參數名 來傳遞參數
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery ("select s.id, s.name from Student s where s.name like :myname").setParameter("myname", "%1%").list(); </span>
* 如果傳遞多個參數,可以採用setParamterList方法
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery("select s.id, s.name from Student s where s.id in(:myids)").setParameterList("myids", new Object[]{1, 2, 3, 4, 5}).list();</span>
* 在HQL中可以使用數據庫的函數,如:date_format
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery("select s.id, s.name from Student s where date_format(s.createTime, '%Y-%m')=?").setParameter(0, "2008-02").list(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //查詢2008-01-10到2008-02-15創建的學生 List students = session.createQuery("select s.id, s.name from Student s where s.createTime between ? and ?") .setParameter(0, sdf.parse("2008-01-10 00:00:00")) .setParameter(1, sdf.parse("2008-02-15 23:59:59")) .list(); </span>
4、Hibernate HQL查詢:直接使用sql進行查詢
Java代碼:
- <span style="font-size: medium;">List students = session.createSQLQuery("select * from t_student").list(); </span>
不會返回對象,而是所有屬性!
5、Hibernate HQL查詢:分頁查詢
* setFirstResult(),從0開始
* setMaxResults,每頁顯示多少條數據
Java代碼:
- <span style="font-size: medium;">List students = session.createQuery("from Student") .setFirstResult(1) .setMaxResults(2) .list(); </span>
6、Hibernate HQL查詢:對象導航查詢,在HQL中採用 . 進行導航
7、Hibernate HQL查詢:連接查詢
* 內連
Sql代碼:
- <span style="font-size: medium;">SELECT s.name, c.name FROM Student s (inner) join s.classes c </span>
* 外連接(左連接/右連接)
Sql代碼:
- <span style="font-size: medium;">SELECT s.name, c.name FROM Student s left join s.classes c </span>
8、Hibernate HQL查詢:統計查詢
Java代碼:
- <span style="font-size: medium;">List students =session.createQuery("select c.name, count(s) from Student s join s.classes c " +"group by c.name order by c.name").list(); for (Iterator iter=students.iterator(); iter.hasNext();) { Object[] obj = (Object[])iter.next(); System.out.println(obj[0] + ", " + obj[1]); } </span>
9、DML風格的操作(儘量少用,因爲和緩存不同步)
Java代碼:
- <span style="font-size: medium;">session.createQuery ("update Student s set s.name=? where s.id < ?") .setParameter(0, "李四") .setParameter(1, 5) .executeUpdate(); </span>
應當儘量少用,因爲和緩存不同步,也就是說,假如在執行上面的語句之前,已經把student封裝成一個list曾經拿了出來,再執行上面的語句對 student中的表進行數據更新,然後再list Student表,則此時的list是從緩存中取的數據,而不是從表中找到的數據,也就是說,list拿到的是update前的數據,所以造成了這種不同步,所以這種風格儘量少用。
從這個方面也可以看得出Hibernate並不適用於聚集性,統計,大量批量的更新,刪除等操作。
第三,本地SQL查詢
使用SQLQuery
對原生SQL查詢執行的控制是通過SQLQuery接口進行的,通過執行Session.createSQLQuery()獲取這個接口。最簡單的情況下,我們可以採用以下形式:
- <span style="font-size: medium;">List cats = sess.createSQLQuery( " select * from cats " ).addEntity(Cat. class ).list(); </span>
這個查詢指定了:
SQL查詢字符串
查詢返回的實體
這裏,結果集字段名被假設爲與映射文件中指明的字段名相同。對於連接了多個表的查詢,這就可能造成問題,因爲可能在多個表中出現同樣名字的字段。下面的方法就可以避免字段名重複的問題:
- <span style="font-size: medium;">List cats = sess.createSQLQuery( " select {cat.*} from cats cat " ).addEntity( " cat " , Cat. class ).list(); </span>
這個查詢指定了:
SQL查詢語句,它帶一個佔位符,可以讓Hibernate使用字段的別名.
查詢返回的實體,和它的SQL表的別名.
addEntity()方法將SQL表的別名和實體類聯繫起來,並且確定查詢結果集的形態。
addJoin()方法可以被用於載入其他的實體和集合的關聯.
- <span style="font-size: medium;">List cats = sess.createSQLQuery(
- " select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " )
- .addEntity( " cat " , Cat. class )
- .addJoin( " kitten " , " cat.kittens " )
- .list(); </span>
原生的SQL查詢可能返回一個簡單的標量值或者一個標量和實體的結合體。
- <span style="font-size: medium;">Double max = (Double) sess.createSQLQuery( " select max(cat.weight) as maxWeight from cats cat " )
- .addScalar( " maxWeight " , Hibernate.DOUBLE);
- .uniqueResult(); </span>
除此之外,你還可以在你的hbm文件中描述結果集映射信息,在查詢中使用。
- <span style="font-size: medium;">List cats = sess.createSQLQuery(
- " select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " )
- .setResultSetMapping( " catAndKitten " )
- .list(); </span>
命名SQL查詢
可以在映射文檔中定義查詢的名字,然後就可以象調用一個命名的HQL查詢一樣直接調用命名SQL查詢.在這種情況下,我們不 需要調用addEntity()方法.
- <span style="font-size: medium;">< sql - query name = " persons " >
- < return alias = " person " class = " eg.Person " />
- Select person.NAME AS {person.name},person.AGE AS {person.age},person.SEX AS {person.sex} FROM PERSON person Where person.NAME LIKE :namePattern
- </ sql - query > </span>
- <span style="font-size: medium;">List people = sess.getNamedQuery( " persons " ).setString( " namePattern " , namePattern)
- .setMaxResults( 50 )
- .list(); </span>