27. srpna 2007

Hibernate Criteria API vs. HQL

Nedávno jsem dělal docela složitou stránku pro vyhledávání položek uložených v databázi, kde si uživatel mohl vybrat velké množství omezujících parametrů (přibližně asi 25 položek). Navíc jednotlivé objekty mají velké množství atributů a já potřeboval vypsat pouze nepatrné procento z nich.

Mám rád Hibernate Criteria API pro vytváření dynamických dotazů, protože nemusím skládat výsledný dotaz z řetězců, nemusím se starat o správné typy apod. Prostě celkově mi to přijde více "programovější" využívat dostupné Criteria API (pozn. celkem zajímavý článek o srovnání Criteria API a HQL vyšel na DevX). V tomto případě jsem ovšem narazil a musel jsem to nakonec celé udělat pomocí HQL dotazů :(.

Narazil jsem na následující problémy s Criteria API:

  1. nebyl jsem schopen efektivně omezit množinu atributů příbuzných objektů, které jsem potřeboval vrátit

  2. nemohl jsem se dotazovat na kolekci elementů

Nemožnost omezení atributů

Přes Criteria API se standardně načítají všechny atributy pro danou třídu včetně příbuzných tříd. Je možné toto změnit pomocí tzv. projekcí. Tyto projekce mají však svoje omezení - není možné provést projekci pouze na určitý atribut příbuzného objektu (=related object). Takže například když mám hlavní objekt Dokument s vazbou na lokalitu, tak nejsem schopen provést takovou projekci, aby se mi vrátil pouze název lokality.
    List results2 = session.createCriteria(Document.class)
.setProjection( Projections.projectionList()
.add( Property.forName("name") )
.add( Property.forName("location.name") )
)
.list();

Kolekce elementů

Na začátek bych měl asi nejdříve vysvětlit, co to je kolekce elementů. Vysvětlím to na příkladě. Mám dokument a k němu se mi vážou kódy lokalit. Tedy jeden dokument má na sebe vázanou kolekci kódů lokalit. Zde se nejedná o kolekci objektů, ale pouze elementů s jejich kódem. Určitě se zeptáte proč to není kolekce standardních objektů? Je to proto, že informace o jednotlivých lokalitách jsou uloženy v jiném systému a v našem systému ukládáme pouze identifikátory lokalit, abychom se na ně mohli později dotazovat. Konkrétně pro dokument konfigurace pomocí anotací vypadá následovně:
  @CollectionOfElements(fetch = FetchType.LAZY)
@JoinTable(name="document_pagis", joinColumns = @JoinColumn(name = "doc_id"))
@Column(name = "idob_pg")
private Set paGisCodes;
Je nutné poznamenat, že kolekce elementů není součástí standardního JPA, ale je to rozšíření Hibernate. Rád bych se k problematice kolekcí elementů ještě vrátit v některém dalším příspěvku.

Zpět k omezení Criteria API. V současné verzi Hibernate (3.2.3) nejsem schopen přistupovat k těmto elementům přes Criteria API. Tuto informaci jsem našel přímo na stránkách Hibernate, ale nyní bohužel nejsem schopen to znovu najít, abych uvedl přímo odkaz.

Z výše uvedených důvodů jsem vyhledávání implementoval přes HQL bez žádných větších problémů. Jen z toho nemám tak dobrý pocit, jako kdybych použil Criteria API :).

Žádné komentáře: