V minulém příspěvku jsem psal o tom, jakým způsobem nadefinovat základní DAO vrstvu. Zvolil jsem způsob, který se stal zavislý na Hibernate Session. Tento „vendor“ nabízí i jednu z věcí, kterou jsem si pomalu oblíbil, a tím jsou Criteria API.

Psaní základních SQL dotazů jsem nahrazil OQL dotazy. OQL je v podstatě velice podobný SQL s tím rozdílem, že daný dotaz se provádí nad entitami reprezentující data v DB. Díky tomu odpadá nutnost psát ruzné JOIN konstrukce, specifikovat vrácené sloupce a hlavně získávám nezávislost na použitém databázovém systému. Převod z MySQL na Oracle či MSSQL je triviální záležitost spočívající v přepsání pár řádku v persistence.xml či v přepsání JDBC zdroje v aplikačním serveru.

Ale zpět. I když je OQL velice elegantní způsob psaní, stále obsahuje velké omezení, které spočívá v tvorbě dynamických dotazů. Pokud aplikace přistupuje k datům tak, že je získává na základě uživatelského filtru, musím dotaz poskládat z daného Stringu, což je dost otravující záležitost.
K tomuto účelu se velice hodí Criteria API. Nebudu zde popisovat funkci Criteria API, k tomuto účelu slouží referenční příručka. U Criteria API se mi velice zalíbila možnost filtrovat data přes danou entitu. K tomuto účelu zde existuje objekt „Example“. Při použití tohoto přístupu mohu velice snadno získat data filtrovaná podle daných sloupců (atributů) v tabulce (entitě).

Jenže…

Pokud si například řeknu: „Chci vybrat všechny záznamy s podmínkou WHERE datum BETWEEN od AND do“, dostanu se opět do slepé uličky a musím přistoupit zpět k čistému Criteria API nebo dokonce k OQL.

Toto byl první důvod, proč jsem přistoupil k implementaci vlastního způsobu, který je řízen pomocí anotací. Ještě než přejdu k samotnému způsobu, musím zmínit druhou pozitivní věc.

Pokud začnu tvořit DAO vrstvu, začne se mi každý DAO objekt hemžit metodami jako: findByDatumPrijeti, findByXXX… Jistě není nic špatného na implementaci daných metod, až na to, že metody obsahují parametry, které již jasně říkají, jaká data mají být výsledkem (návratovou hodnotou).
Uvedu malý příklad (pro pozdější srovnání):

public List<zakazka> findForExample(String zakaznik, Date datumOd, Date datumDo) {

// implementace Criteria API, OQL ci suroveho SQL

}

Jak je z ukázky patrné, metoda vybíra zakázky, které spadají jistému zákazníkovi a jejich datum je v rozmezi datumOd-datumDo.

K odstranění psaní samotných dotazů použiji následující kroky:
  • vytvořím objekt pro filtraci
  • daný objekt anotuji příslušnými „metadaty“
  • zruším metodu findForExample a v DAO vytvořím obecnou metodu: findByCriteri­a(Object filtr)

Jediné, co mi zůstane je objekt pro filtrování. Sám se svými metadaty je schopen řici, co vlastně chci. Následující ukázka je ekvivalentní předchozímu způsobu:

@Criteria(entity = Zakazky.class)
public class ZakazkyForExam­pleFiltr {

    @Criterion(pro­perty=„zakaznik­.id“)
    private String zakaznik;

    @Criterion(pro­perty = „datumPrijeti“)
    @Between(idf = „dp“, property = BetweenDAO.MIN)
    private Date datumPrijetiOd;


    @Criterion(pro­perty = „datumPrijeti“)
    @Between(idf = „dp“, property = BetweenDAO.MAX)
    private Date datumPrijetiDo;

    // settry, gettry
}

// pouziti
ZakazkyForExam­pleFiltr filtr = new ZakazkyForExam­pleFiltr();
// naplneni filtru
List<zakazky> result = dao.findByCri­teria(filtr);
  

Pro lidi nesnášející anotace, bude tento způsob jistě nepoužitelný, pro lidi snažící se o co největší usnadnění a zpřehlednění své práce, naopak přínosem. Věřím, že každý si již ten svůj zpusob nalezl a nehodlá ho měnit, na druhou stranu při pohledu na takovýto kod si pokládám jednu otázku: „Není přece jen efektivnější popsat kod metadaty a nechat zbytek na daném frameworku?“.

Jelikož se mi článek rozrostl, rozhodl jsem se ho rozdělit do dvou částí. V první části jsem popsal důvody a uvedl malou ukázku. V druhé části uvedu možnosti a nabídnu danou funkčnost ke stažení. Tedy ten základní „motor“, který lze jednoduše „přilepit“ do své DAO vrstvy. Navíc uvedu další plány a položím otázky, na které si zatím nedokážu odpovědět.