Přeskočit na hlavní obsah

DTO a ORM

Pojem DTO jistě není třeba představovat. Jedná se o objekt reprezentující data, které je třeba přenést z jedné strany na druhou. Asi nejčastějším využitím jest výsledek dotazu z persistentní vrstvy.

Při použití ORM frameworku, jako je např. Hibernate, definuji data v DB pomocí objektů (entit). Tyto entity poté mohou být i výsledkem, tedy mohou představovat jak doménový model aplikace, tak i dané DTO. Jsou ovšem chvíle, kdy takové DTO je nepoužitelné či jeho použití může znamenat výrazný pokles výkonnosti aplikace.

Prvním příkladem, kde entita nemůže (neměla by) být reprezentována jako DTO:
"Vytvoř seznam zaměstnanců s celkovým počtem odpracovaných hodin."
V takovém případě je třeba výsledek z OQL přemapovat do vlastního objektu (DTO), který bude obsahovat číslo, jméno, příjmení, celkový počet hodin. Sice by někdo mohl namítnout, že daná hodnota (odpracované hodiny) se dá do entity, jako @Transient, přidat. Ale doménový model bych se neměl "špinit" vlastnostmi, které reprezentují pouze výsledek nějakého dotazu.

Druhým příkladem je distribuovaná Java. Jinými slovy řečeno, ve chvíli, kdy vzdáleně volám remote EJB, by výsledkem mělo být pouze to, co skutečně potřebuji. Nikoli anotovaná entita, která obsahuje mapované kolekce a dalších X vlastností, které mě v té chvíli nezajímají. Dodržení této zásady bude mít pozitivní vliv na výkon aplikace.

Důvody, proč je někdy lepší použit DTO namísto entity, jsem uvedl. Nyní ovšem přichází otázka, jak takové DTO přemapovávat z OQL dotazů a jak si co nejvíce ušetřit nudného psaní kódu.

První možností je použití klauzule new z JPA. Takové OQL by mohlo vypadat následovně:
public List getList() {
String oql = "SELECT new ZamPocetHodin(z.cislo, z.prijmeni, z.jmeno, x.vypocetHodin) FROM Zamestnanci z .....";
return entityManager.createQuery(oql).getResultList();
}


Jak je na první pohled patrné, je třeba, aby objekt "ZamPocetHodin" obsahoval konstruktor, který obsahuje dané parametry podle daného OQL.
Tento způsob má vesměs samé nevýhody. Osobně mi asi nejvíce vadí samotná absence jakéhokoli refactoringu. Myslím, že v dnešní době neexistuje IDE, které by dokázalo poznat OQL dotazy a validovat je. Dále mi vadí, že parametrem v konstruktoru DTO může být pouze omezený výčet typů. Není možné vracet jiné Entity, či je nějak dále přemapovat. Poslední věc, která se mi nelíbí, je fakt, že píši zbytečně mnoho kódu. Někde definuji DTO a někde ho musím plnit a přitom stále hlídat, zda se mi něco nezměnilo.

Druhou možností je plnění pomoci Hibernate Criteria API:
criteria.setProjection(Projections.sqlProjection(sql + " AS " + alias,
new String[]{alias},
new org.hibernate.type.Type[] {TypeWrapperImpl.getType(/* type */)}));
criteria.setResultTransformer(Transformers.aliasToBean(ZamPocetHodin.class));


Uvedl jsem jen malý kus kódu, který úplně nedpovídá všednímu použití, ale jde o to, že dost často je hodnota v DTO reprezentována nějakým poddotazem, či jiným způsobem, které SQL umožňuje. Je pravda, že v Hibernate Projections si vše mohu dobře nadefinovat, ale výsledkem je poté naprosto šílený kód, který je sice "dynamičtější" a "programovější" než je tomu u JPA, ale za to je složitější.

Jelikož mi nevyhovuje ani jeden z daných způsobů, snažil jsem se nalézt nějaký vhodný způsob, jak se zbavit toho nudného či složitého psaní na přemapování do DTO.
Po pečlivém rozmýšlení jsem se rozhodl, že by nebylo špatné nahlédnout na danou věc stejně jako v případě mé implementace "Irminsul Criteria". O co vlastně šlo, si můžete přečíst zde a zde.
Cílém mého snažení bylo, abych vytvořil DTO objekt, který pomocí anotací nad atributem bude obsahovat dané SQL příkazy, které naplní příslušnou hodnotu. V této chvíli se jedná stále o testování, ale výsledkem mého snažení je již celkem funkční základní část, tedy jednoduché přemapování.

Nyní uvedu malý příklad takového přemapování:
@DTO(entity = EntitaOdkudSeDataZiskavaji.class)
@Aliases(values={
@Alias(associationPath="strediska.provoz", alias="provoz"),
@Alias(associationPath="rj.testPolozky.polozka", alias="polozka"),
.....
})
public class TestDTO implements Serializable {
@ProjectionSQL("(({polozka}.faktura * {vicePraceMena}.aktualni_kurz) * (procento / 100))")
private BigDecimal opravy;

@ProjectionSQL("(({polozkaZakazky}.cena * {mena}.aktualni_kurz) * (procento / 100))")
private BigDecimal cenaVyrobku;

@Projection("pk.cislo")
private String cislo;

// settry, gettry, atd.
}
List result = factory.findForDTO(TestDTO.class);


Jak je z kódu patrné, jde pouze o klasické POJO, které je anotované, aby implementace pochopila, že jde o DTO, které je spouštěne nad danou entitou (@DTO) a obsahuje ty či ony aliasy, které usnadňuj psaní ProjectionSQL.
K tomuto účelu jsem využil implementace Hibernate Projections a ProjectionsSQL, kde podle jasných pravidel převádím dané anotace na projekci, která je již obeznámena s daným typem a tím, co vlastně daný atribut reprezentuje.

Opěvná píseň

Předem musím poznamenat, že toho dané DTO umí více, jednak je to možnost spojit toto DTO i s Filtr objektem, který jsem popisoval ve výše odkazovaných člancích. Výsledkem je pote jednoduche volání:
List result = factory.findByCriteriaDTO(filtr, TestDTO.class);


Již se tedy nemusím zabývat nutností definování filtru a vrácených dat, oba stavy jsou řízeny anotacemi.

Dalšími možnostmi jsou řazení, získaní hodnoty přes alias, či přes vlastní definici, která může být ve formě SQL či OQL zápisu.

Díky tomu odpadá několik věcí:

  • není třeba definovat nějaké metody v DAO

  • nemusí se psát žadné přemapování, stačí jen definice DTO

  • není třeba hledat, jak se dané DTO plní, je to jasné z jeho definice

  • není třeba se bát refactoringu jako v případě implementace přes JPA

  • není třeba určovat vrácený typ jako v případě Hibernate, typ je již jasný z definice atributu třídy DTO



Až bude daná funčnost plně funkční, nabídnu své řešení ke stažení. Zatím bych nerad někde publikoval zabugovaný nedodělek :)

Zajímal by mě Váš názor na takovouto implementaci DTO. Je dost možné, že někdo přijde s lepším nápadem či možností, která již dávno existuje a jen já o ni nevím :)

Komentáře

  1. Dovolim si poznamenat, že IntelliJ IDEA podporuje klauzule new uvnitř JPA dotazů, včetně kontroly typu předávaných parametrů a refaktoringu.

    Vašek

    OdpovědětVymazat

Okomentovat

Populární příspěvky z tohoto blogu

Jak si v IT vydělat hodně peněz?

Na začátek by bylo dobré, abych objasnil samotný titulek, který může na někoho působit jako červený hadr. Článek nebude o obecných pravidlech, ale bude vyprávět můj vlastní příběh, na kterém vám zkusím ukázat, jak se dá docílit úspěchu, či alespoň správně nastartovat svojí vlastní kariéru v IT.

I když se z názvu článku dá dedukovat, že se vše bude točit kolem peněz, není tomu tak. Alespoň ze dvou třetin určitě ne. Ale to už předbíhám, pojďme to raději vzít hezky popořadě...

Kdybychom měli mluvit o roce 2017 jako o přelomové době, nejspíše to nebude pravda. I když pro někoho to může být rok plný úspěchů a štěstí v podobě narození zdravých dětí, svatby či první velké lásky, tak z pohledu lidstva se jedná o rok, který jen kopíruje předešlé a v oblasti technologií nás posouvá stejným tempem jako rok předtím.

Jsem naprosto přesvědčen o tom, že i když se současná doba tak nenazývá, tak prožíváme dobu, která jednou bude označena za revoluční, a to zejména díky vynálezu internetu, který je st…

Jak by se firmy neměly chovat k programátorům?

Každý, kdo pracuje v IT oboru, se jistě již setkal s různými „geniálními nápady“, od kterých si firma slibovala zlepšení produktivity či snížení nákladů. Ať už je to zavedení agilních principů, striktní kontrola práce či zavedení nové a skvělé metodiky, o které si „šéf“ přečetl včera na internetu. Jsou z toho skutečně tak nadšení i samotní vývojáři? A bude nový nápad fungovat?
K napsání tohoto článku mě navedly různé programátorské diskuze, kde si lidé stěžovali na firmu, kde pracují. Příklady, které zde uvedu, jsou z reálné praxe. Ať už jsem je zažil jako řadový programátor, či jako šéf týmu.
I když je poptávka po programátorech tak vysoká, že Vás headhunteři nahánějí i ve chvílích, kdy o to opravdu nestojíte, tak i přes to je mnoho lidí, kteří se bojí opustit svoje současné zaměstnání.
Čeho se nejčastěji bojíme? Je to samozřejmě nejistota, kterou si často omlouváme větami jako: „Tady mám své pohodlí, co když to jinde mít nebudu?“ nebo „I když mě to v práci štve a nebaví, tak mě ale…

Jak jsem technologicky postavil startup

Tento příběh pojednává o technologiích, nástrojích a vůbec o všem, co jsem potřeboval k tomu, abych byl schopen, postavit startup na zelené louce.

Každý správný příběh začíná stejně: "Jednou jsem...."

Kapitola první: Nápad
Jednou jsem se setkal s člověkem, který měl nápad na produkt, který se v průmyslu zatím nevyskytuje. I přes prvotní skepsi, kdy jsem si říkal: "Tohle už přeci dávno v průmyslu existuje, ne?", jsem došel ke zjištění, že nikoli.

Tím jsem se dostal ke svému prvnímu poučení. Průmysl je technologicky dost zabržděný. Osobně se domnívám, že těch důvodů, proč tomu tak je, je několik. Za prvé je to fakt, že většina lidí, kteří se pohybují v tomto odvětví jsou často konzervativní a za správné považují pouze léty osvědčené věci. Druhým důvodem je to, že jakákoli změna znamená riziko. Ať už z pohledu finanční ztráty tak i z pohledu stability výroby. No a třetím a nejzásadnějším důvodem je to, že ač zde máme spousty technologických vymožeností, narážíme na to,…