Prosinec 27th, 2007Anotace nahrazující SQL a OQL dotazy (důvody)
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: findByCriteria(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:
public class ZakazkyForExampleFiltr {
@Criterion(property=„zakaznik.id“)
private String zakaznik;
@Criterion(property = „datumPrijeti“)
@Between(idf = „dp“, property = BetweenDAO.MIN)
private Date datumPrijetiOd;
@Criterion(property = „datumPrijeti“)
@Between(idf = „dp“, property = BetweenDAO.MAX)
private Date datumPrijetiDo;
// settry, gettry
}
// pouziti
ZakazkyForExampleFiltr filtr = new ZakazkyForExampleFiltr();
// naplneni filtru
List<zakazky> result = dao.findByCriteria(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.
Leden 3rd, 2008 at 21.15
Criteria API je pěkné, je třeba však mít napaměti, že je určeno výhradně pro dynamické dotazy, tedy ty, kde bychom normalně lepily při každém volání jiné SQL. Tato věc má tu nevýhodu, že databáze si pak nedrží zkompilované query a musí je kompilovat pokaždé znovu, což ji nemusí udělat uplně dobře. Proto bych jen doplnil pro ORM nováčky, že HQL ci OQL by se stejně měli naučit
Leden 5th, 2008 at 12.34
Urcite souhlas. Na jednoduche dotazy je vzdy lepsi pouzit OQL.
Asi nejefektivnejsi je u JPA „NamedQuery“, coz je dotaz, ktery se anotuje k dane entite. EntityManager pri svem „nacteni“ udela prepared statement techto dotazu. Volani takoveho dotazu je jeste rychlejsi.
Leden 6th, 2008 at 0.35
Tak mi to nedalo a provedl jsem test. U Hibernate jsou jak na HQL, Criteria a Named Query (HQL) použity PreparedStatementy, tedy databáze si zkompilované query může držet (pokud se samozřejmě nemění kriteria (ne parametry)). Čímž si sypu popel na hlavu, že jsem v předchozím komentaři neměl pravdu :-/ Rychlosti HQL a Criteria byly srovnatelné na triviálním dotazu přes jednu entitu. Named Query byly nejrychlejší, jak píšeš, ale kvůli tomu, že si Hibernate pamatuje zkompilované HQL.
Leden 12th, 2008 at 22.48
Statementy si nemusi drzet jen databaze, ale treba i connection pool. Viz. napr. hojne pouzivany c3p0 pool,
Slava
Září 10th, 2010 at 14.18
< blockquote >< a href=„http://cheaptabletsonline.com/“>CheapTabletsOnline.com. Canadian Health&Care.Best quality drugs.Special Internet Prices.No prescription online pharmacy. Low price drugs. Order pills online< /a >…
Buy:Valtrex.Accutane.Zyban.Lumigan.Nexium.Actos.Prednisolone.Prevacid.Mega Hoodia.Zovirax.Human Growth Hormone.Arimidex.100% Pure Okinawan Coral Calcium.Retin-A.Petcam (Metacam) Oral Suspension.Synthroid…