離線查詢--DetachedCriteria

原文鏈接

? ?在常規(guī)的Web編程中,有大量的動態(tài)條件查詢,即用戶在網(wǎng)頁上面自由選擇某些條件,程序根據(jù)用戶的選擇條件,動態(tài)生成SQL語句,進行查詢。

? ? 針對這種需求,對于分層應(yīng)用程序來說,Web層需要傳遞一個查詢的條件列表給業(yè)務(wù)層對象,業(yè)務(wù)層對象獲得這個條件列表之后,然后依次取出條件,構(gòu)造查詢語句。這里的一個難點是條件列表用什么來構(gòu)造?傳統(tǒng)上使用Map,但是這種方式缺陷很大,Map可以傳遞的信息非常有限,只能傳遞name和value,無法傳遞究竟要做怎樣的條件運算,究竟是大于,小于,like,還是其它的什么,業(yè)務(wù)層對象必須確切掌握每條entry的隱含條件。因此一旦隱含條件改變,業(yè)務(wù)層對象的查詢構(gòu)造算法必須相應(yīng)修改,但是這種查詢條件的改變是隱式約定的,而不是程序代碼約束的,因此非常容易出錯。

? ? DetachedCriteria可以解決這個問題,即在web層,程序員使用DetachedCriteria來構(gòu)造查詢條件,然后將這個 DetachedCriteria作為方法調(diào)用參數(shù)傳遞給業(yè)務(wù)層對象。而業(yè)務(wù)層對象獲得DetachedCriteria之后,可以在session范圍內(nèi)直接構(gòu)造Criteria,進行查詢。就此,查詢語句的構(gòu)造完全被搬離到 web 層實現(xiàn),而業(yè)務(wù)層則只負(fù)責(zé)完成持久化和查詢的封裝即可,與查詢條件構(gòu)造完全解耦,非常完美!

? ?Criteria 和 DetachedCriteria 的主要區(qū)別在于創(chuàng)建的形式不一樣, Criteria 是在線的,所以它是由 Hibernate Session 進行創(chuàng)建的;而 DetachedCriteria 是離線的,創(chuàng)建時無需Session ,DetachedCriteria 提供了 2 個靜態(tài)方法 forClass(Class) 或 forEntityName(Name)進行DetachedCriteria 實例的創(chuàng)建。
? ? Spring 的框架提供 getHibernateTemplate().findByCriteria(detachedCriteria) 方法可以很方便地根據(jù)DetachedCriteria 來返回查詢結(jié)果。

-Criteria:
查詢所有user表的資料

Criteria criteria = session.createCriteria(User.class);
// 查詢user所有欄位
List users = criteria.list();
Iterator iterator =  users.iterator();
while(iterator.hasNext()) {
    User user = (User) iterator.next();
    System.out.println(user.getId() + user.getName());           
}

Hibernate實際上使用以下的SQL來查詢:
select * from user

1.Restrictions

? ?Restrictions : 限制條件

= Restrictions.eq() 等于equal
<> Restrictions.ne() 不等于 not equal
> Restrictions.gt() 大于greater than
= Restrictions.ge() 大于等于 greater than or equal
< Restrictions.lt() 小于less than
<= Restrictions.le() 小 于 等 于 less than or equal
is null Restrictions.isnull() 等于空值
is not null Restrictions.isNotNull() 非空值
like Restrictions.like() 字符串模式匹配
and Restrictions.and() 邏輯與
and Restrictions.conjunction() 邏輯與
or Restrictions.or() 邏輯或
or Restrictions.disjunction() 邏輯或
not Restrictions.not() 邏輯非
in(列表) Restrictions.in() 等于列表中的某一個值
not in(列表) Restrictions.not(Restrictions.in()) 不等于列表中任意一個值
between x and y Restrictions.between() 閉區(qū)間 xy中的任意值
not between x and y Restrictions.not(Restrictions..between()) 小于值X 或者大于值y

下面進行舉例演示

//查詢user表的所有信息 
Criteria criteria = session.createCriteria(User.class);
    List users = criteria.list();
    Iterator iterator =  users.iterator();
    while(iterator.hasNext()) {
        User user = (User) iterator.next();
        System.out.println(user.getId() + user.getName());           
    }

Criteria只是個容器,如果想要設(shè)定查詢條件,只要使用add()方法加入Restrictions的條件限制,例如查詢age大于20且小于40的資料。雖然我們的SQL語句也是可以完成的,但是為了更好的封裝,更多的復(fù)用代碼,最好還是不要直接的書寫我們的SQL語句,看到了公司封裝的代碼,才感覺到前輩的強大無比,復(fù)用代碼的靈活性非常的高! 這里,我們可以傳遞無限制的Restrictions進行封裝起來,非常方便的使用!
需要注意的是 條件都是實體類中的字段,而不是數(shù)據(jù)庫的字段,所以一定要注意大小寫

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.gt("age", new Integer(20)));
criteria.add(Restrictions.lt("age", new Integer(40)));
List users = criteria.list();

您也可以使用邏輯組合來進行查詢,例如結(jié)合age等于(eq)20或(or)age為空(isNull)的條件:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.or(
                   Restrictions.eq("age", new Integer(20)),
                   Restrictions.isNull("age")
               ));
List users = criteria.list();
//一個單獨的查詢條件是org.hibernate.criterion.Criterion 接口的一個實例。
//org.hibernate.criterion.Restrictions類 定義了獲得某些內(nèi)置Criterion類型的工廠方法。

List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.like("name", "Fritz%") )

    .add( Restrictions.between("weight", minWeight, maxWeight) )

    .list();
  • DetachedCriteria的關(guān)聯(lián)查詢
    假設(shè)要通過學(xué)生的名字(stuName)查詢一個學(xué)生(Student)記錄,可以如下:
  DetachedCriteria dc = DetachedCriteria.forClass(Student.class);
    dc.add(Restrictions.like("stuName", stuName, MatchMode.ANYWHERE));
//MatchMode.ANYWHERE  like '%stuName%' 模糊查詢

如果要通過學(xué)生(Student)的小組(Team)的小組名字(teamName)查詢Student記錄,很多人都會這么寫:

DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
dc.add(Restrictions.like("team.teamName", teamName, MatchMode.ANYWHERE)); 

遺憾的是程序會報錯,說是在Student中找不到team.teamName屬性,這是可以理解的。那么如何通過teamName查找Student呢?
可以這么寫:

DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.createAlias("team", "t"); 
    dc.add(Restrictions.like("t.teamName", teamName, MatchMode.ANYWHERE)); 

沒錯,就是要先建立team的引用,才能用team導(dǎo)航到teamName 。
這里有一個特殊情況,如果是對引用對象的id查詢,則可以不用建立引用,也就是可以不調(diào)用createAlias()語句,如下所示:

DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("team.id", teamId, MatchMode.ANYWHERE)); 

據(jù)我個人的經(jīng)驗,team后只能跟其主鍵屬性,比較其他屬性要用別名。此主鍵屬性可以用“id”字符來指代,也可以用team的主鍵屬性來指代。換句話 說,我的Student類的類主鍵“stuId”,不管是在HQL還是在QBC中,都可以用stu.id來指代stu.stuId。在這里可以看出 “id”字符的特殊性。上述是個人觀點,并未得到確實的證實。

例子1:

如果每個美女都有自己的客戶資源(不要想歪了?。?,那么需要查詢擁有客戶Gates的美女怎么辦?

兩種方法:


DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createCriteria("customers");
beautyCriteria.add(Restrictions.eq("name", "Gates"));
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createAlias("customers", "c");
beautyCriteria.add(Restrictions.eq("c.name", "Gates")):

select * from beauty b,(select * from customers where name ='gates') c where
select b.* from beauty b,customers c where c.name='gates' and

關(guān)于Criteria更詳細的資料,Hibernate的源代碼和測試是最好的文檔。

例子2:
Department和Employee是一對多關(guān)聯(lián),查詢條件為:
  名稱是“department”開發(fā)部門;
  部門里面的雇員年齡大于20歲;

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class);
detachedCriteria.add(Restrictions.eq("name", "department"))
                 .createAlias("employees", "e")
                 .add(Restrictions.gt(("e.age"), new Integer(20)));
List  list = this.getHibernateTemplate().findByCriteria(detachedCriteria);

detachedCriteria.add(Expression.like("citycode", markuplayer8.getCitycode(),MatchMode.ANYWHERE));

if(startdate!=null && !startdate.equals("") && enddate!=null && !enddate.equals("")){  
            StringBuffer sb = new StringBuffer();  
            sb.append("enddate >='" + enddate + "' and '"+startdate+"' <= startdate");  
            detachedCriteria.add(Expression.sql(sb.toString()));  
        }  
        List<Markuplayer8> markuplayerList = this.getHibernateTemplate().findByCriteria(detachedCriteria);  

[參考資料]

  1. 創(chuàng)建一個Criteria 實例
//org.hibernate.Criteria接口表示特定持久類的一個查詢。Session是 Criteria實例的工廠。
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
  1. 限制結(jié)果集內(nèi)容
    一個單獨的查詢條件是org.hibernate.criterion.Criterion 接口的一個實例。
    org.hibernate.criterion.Restrictions類 定義了獲得某些內(nèi)置Criterion類型的工廠方法。
List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.like("name", "Fritz%") )

    .add( Restrictions.between("weight", minWeight, maxWeight) )

    .list();

約束可以按邏輯分組。


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();

Hibernate提供了相當(dāng)多的內(nèi)置criterion類型(Restrictions 子類), 但是尤其有用的是可以允許

你直接使用SQL。

List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%",

Hibernate.STRING) )

    .list();

{alias}占位符應(yīng)當(dāng)被替換為被查詢實體的列別名。

Property實例是獲得一個條件的另外一種途徑。你可以通過調(diào)用Property.forName() 創(chuàng)建一個

Property。

 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();
  1. 結(jié)果集排序

你可以使用org.hibernate.criterion.Order來為查詢結(jié)果排序

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();
  1. 關(guān)聯(lián)

你可以使用createCriteria()非常容易的在互相關(guān)聯(lián)的實體間建立 約束。

List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.like("name", "F%")

    .createCriteria("kittens")

        .add( Restrictions.like("name", "F%")

    .list();

注意第二個 createCriteria()返回一個新的 Criteria實例,該實例引用kittens 集合中的元素。

接下來,替換形態(tài)在某些情況下也是很有用的。

List cats = sess.createCriteria(Cat.class)

    .createAlias("kittens", "kt")

    .createAlias("mate", "mt")

    .add( Restrictions.eqProperty("kt.name", "mt.name") )

    .list();

(createAlias()并不創(chuàng)建一個新的 Criteria實例。)

Cat實例所保存的之前兩次查詢所返回的kittens集合是 沒有被條件預(yù)過濾的。如果你希望只獲得

符合條件的kittens, 你必須使用returnMaps()。

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");

}
  1. 動態(tài)關(guān)聯(lián)抓取

你可以使用setFetchMode()在運行時定義動態(tài)關(guān)聯(lián)抓取的語義。

List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.like("name", "Fritz%") )

    .setFetchMode("mate", FetchMode.EAGER)

    .setFetchMode("kittens", FetchMode.EAGER)

    .list();

這個查詢可以通過外連接抓取mate和kittens。

  1. 查詢示例

org.hibernate.criterion.Example類允許你通過一個給定實例 構(gòu)建一個條件查詢。

Cat cat = new Cat();

cat.setSex('F');

cat.setColor(Color.BLACK);

List results = session.createCriteria(Cat.class)

    .add( Example.create(cat) )

    .list();

版本屬性、標(biāo)識符和關(guān)聯(lián)被忽略。默認(rèn)情況下值為null的屬性將被排除。

可以自行調(diào)整Example使之更實用。

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();

甚至可以使用examples在關(guān)聯(lián)對象上放置條件。

List results = session.createCriteria(Cat.class)

    .add( Example.create(cat) )

    .createCriteria("mate")

        .add( Example.create( cat.getMate() ) )

    .list();
  1. 投影(Projections)、聚合(aggregation)和分組(grouping)

org.hibernate.criterion.Projections是 Projection 的實例工廠。我們通過調(diào)用

setProjection()應(yīng)用投影到一個查詢。

List results = session.createCriteria(Cat.class)

    .setProjection( Projections.rowCount() )

    .add( Restrictions.eq("color", Color.BLACK) )

    .list();

  

List results = session.createCriteria(Cat.class)

    .setProjection( Projections.projectionList()

        .add( Projections.rowCount() )

        .add( Projections.avg("weight") )

        .add( Projections.max("weight") )

        .add( Projections.groupProperty("color") )

    )

    .list();

在一個條件查詢中沒有必要顯式的使用 "group by" 。某些投影類型就是被定義為 分組投影,他

們也出現(xiàn)在SQL的group by子句中。

可以選擇把一個別名指派給一個投影,這樣可以使投影值被約束或排序所引用。下面是兩種不同的

實現(xiàn)方式:

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();

alias()和as()方法簡便的將一個投影實例包裝到另外一個 別名的Projection實例中。簡而言之,

當(dāng)你添加一個投影到一個投影列表中時 你可以為它指定一個別名:

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();

也可以使用Property.forName()來表示投影:

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();
  1. 離線(detached)查詢和子查詢

DetachedCriteria類使你在一個session范圍之外創(chuàng)建一個查詢,并且可以使用任意的 Session來

執(zhí)行它。

DetachedCriteria query = DetachedCriteria.forClass(Cat.class)

    .add( Property.forName("sex").eq('F') );

//創(chuàng)建一個Session

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();

相互關(guān)聯(lián)的子查詢也是有可能的:

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();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容