Jak už jsem předeslal, základem dané filtrace nad daty, jsou anotace. Uvedu jejich výčet a popis.
@Criteria (class)
Jedná se o základní anotaci, která říká, že se jedná o "filtrační" objekt, který je schopen nabídnout své atributy k porovnání. Jediným parametrem je entita, která říká o jakou entitu se jedná.
@Criterion (atribut)
Popisuje atribut, který je automaticky brán jako jeden z prvků filtrace. Samotná anotace obsahuje moznosti, které označí, jak se k danému atributu bude přistupovat.
Zde je výčet některých z nich:
- property - název atributu podle dané entity, může se jednat i o vnořenou hodnotu (zakaznik.id)
- operator - definuje typ porovnávání mezi hodnotou atributu a názvem atributu, typ porovnávání je výčtový typ, který lze rozšířit
- excludeEmptyString - určuje, zda se bude brát v potaz prázdný String či se bude ignorovat
V budoucnu předpokládám rozšíření těchto vlastností. Ale držím se pravidla: Méně je někdy více. Přeci jen nechci tvořit kód, který bude obsahovat spoustu deprekovaných způsobů.
@Between (atribut)
Složení minimální a maximální hodnoty, která je prováděna na základě definovaného idf a property, což je výčtový typ, který může být buď MIN či MAX. Výhodou tohoto použití je také v tom, že pokud jedna z hodnot neexistuje, automaticky převádí BETWEEN na porovnání jedné hodnoty. Pokud například hodnota pro MAX bude empty a MIN bude existovat, bude generována následující podmínka: WHERE datum >= 'hodnota'.
@Conjunction (atribut)
Konjunkce funguje podobně jako v Criteria API. Na základě definovaného idf se spojí ty hodnoty, které k sobě patří. Zde ovšem existuje jedno omezení (stejně jako u disjunkce): Vnořené podmínky. Je to další krok, který budu muset vyřešit.
@Disjunction (atribut)
Opět podobný způsob jako v případě konjunkce. Zde se tedy generuje něco jako: WHERE (property [operator] value OR ....). Spojení daných atributu se opět provádí pomocí stejného názvu v idf.
@Alias (class)
Jelikož se často stává, že se potřebuji dotazovat na sloupec, který je v nějaké tabulce, která je v relaci s danou entitou, potřebuji aliasovat danou entitu, pro kterou bude platit nasledující alias.
Zde rovnou uvedu příklad:
Mám zaměstnance, který spadá do daného střediska, z kterého chci znát název, podle kterého filtruji. V OQL by mohl dotaz vypadat následovně: FROM Zamestnanec z WHERE z.stredisko.nazev = 'Vyroba'. V Criteria API se k danému sloupci dostanu přes alias, takže by to mohlo vypadat následovně: @Alias(associationPath = "stredisko", alias = "s").
Aby bylo možné definovat aliasů více, existuje anotace s nazvem Aliases, která může obsahovat pole daných aliasů.
@OrderBy (class)
Poslední popisovaná anotace slouží k definování řazení dat. Jelikož požadavek na řazení dat je častým jevem, existuje možnost nadefinovat filtračnímu objektu tuto vlastnost. Daná anotace obsahuje pole anotací s názvem @OrderValue, která má dvě vlastností a tím je název atributu pro řazení a typ řazení, který je výčtvový typ ASC nebo DESC.
Samotné použití je postaveno na DetachedCriteria. Ti, co znají Hibernate vědí, že se jedná o criteria, které neobsahují Session. Jinými slovy, takováto criteria si můžete vytvořit ještě v době, kdy o Hibernate Session nemá Váš kod ani ponětí. Jistě je to dobrý způsob jak zajistit určité odělení od zbytku vlastní implementace.
K přístupu slouží třída s názvem: "CriteriaDAOFactory". Jak už název napovídá, jedná se o factory, která vrací interface CriteriaDAO. Daný interface již obsahuje metodu vracející DetachedCriteria.
Dané použití může vypadat následovně:
MyFiltr filtr = new MyFiltr();
// naplneni filtru
CriteriaDAO d = CriteriaDAOFactory.getCriteria(filtr);
DetachedCriteria dc = d.getDetachedCriteria();
Nyní uvedu několik příkladů definování daného filtru a jeho ekvivalent v podobě SQL dotazu. U všech ukázek vynechám settry a gettry, které jsou nezbytnou součástí. Pokud někdo chce tvořit neměnný objekt, tak samozřejme settry může vynechat. Takže jedem:
@Criteria(entity = Zamestnanec.class)
public class ZamestnanecFiltr {
@Criterion(property="cislo")
private String id;
@Criterion(operator=RestrictionDAO.LIKE)
private String prijmeni;
}
SELECT * FROM zamestnanec
WHERE cislo = 'value' AND prijmeni LIKE '%value%'
@Criteria(entity = Zamestnanec.class)
@OrderBy(values={@OrderValue(name = "prijmeni")})
@Alias(associationPath = "stredisko", alias = "s")
public class ZamestnanecFiltr {
@Criterion(property="s.nazev")
private String nazev;
@Criterion(property="datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MIN)
private Date datumOd;
@Criterion(property="datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MAX)
private Date datumDo;
}
SELECT * FROM zamestnanec z
INNER JOIN stredisko s ON z.stredisko = s.cislo
WHERE s.nazev = 'value' AND z.datumPrijeti BETWEEN 'min' AND 'max'
ORDER BY z.prijmeni
Tím bych asi ukončil tento popis. Samozřejmě, lze dané věci dobře kombinovat. Je jasné, že se naleznou další potřebné funkce, které nechám spíše vyplynout postupem času. Pokud by někdo měl zájem, mohu se pokusit danou funčnost implementovat.
Poslední věcí, kterou chci zmínit je pár otázek, které bych rád nějak vyřešil. Jelikož vše má svá úskalí... Pokud bude mít někdo zájem, budu vítat jakoukoli pomoc či nakopnutí.
- řazení dat je definováno staticky, jak implementovat dynamický způsob
- specifikování získaných dat, jak omezení na určité sloupce, tak vlastní DTO či inicializace LAZY
- vnořené konjunkce a disjunkce, které závisí na předchozích podmínkách, a zda má vůbec cenu něco takového implementovat
- dynamické rozhodování zvoleného operátoru, uživatel například může mít k dispozici volbu mezi (LIKE, <, >, =)
Samotný projekt je psán v NetBeans, proto ho také jako NetBeans projekt vystavuji. Předem se omlouvám za absenci JUnit testů. Upřímně, zatím jsem neměl dostatek času vše otestovat a navíc testy mám komponovány přímo na danou specifikaci, ke které je třeba existence daného modelu a zdroje. Samotná implementace je závislá na Hibernate API a na log4j. Knihovna log4j je v adresáři lib, knihovny pro Hibernate neuvádím záměrně. Pro práci s Hibernate existuje mnohem větší závislost :)
Projekt ke stažení: IrminsulCriteria