25. srpna 2008

JSF - FacesTrace a MyFaces Orchestra

Teprve nedávno jsem začal používat JSF a musím se přiznat, že se v tom pořád tak nějak plácám. Jsem zvyklý, že při programování mám vždy vše pod kontrolou, ale tady z toho takový pocit nemám. Ale toto téma si nechám až na nějaký další článek.

V tomto článku bych chtěl zmínit dvě knihovny, které mi celkem zpříjemnily mojí práci s JSF.

FacesTrace

Pokud nastane nějaký problém s JSF, tak někdy je dost těžké vůbec zjistit příčinu problému - skoro žádné logování, skoro žádné ladící informace. Tato knihovna v tomto ohledu aspoň trochu pomůže.

Použití je velice jednoduché - přidá se knihovna k aplikaci a na stránce určené k ladění se použije tag <ft:trace />. Někdy je ještě nutné přidat mapování do web.xml.
Ukázku poskytnutých informací lze vidět zde.

MyFaces Orchestra

Při práci s JSF dost často nestačí ukládat proměnné jen do rozsahu requestu (pokud tedy chceme zachovat "rozumnou eleganci" vývoje s JSF). Potom musíme sáhnout po session, což není ideální, minimálně z pohledu nároků na paměť a škálovatelnosti (další důvody jsou uvedeny zde).
MyFaces Orchestra nabízí něco mezi - konverzaci. MyFaces Orchestra vyžaduje přítomnost Springu, protože využívá možnosti Springu si definovat vlastní rozsah (scope) pro uložení beanů resp. JSF managed beanů. Je možné využívát dva typy konverzací:
  • automatická - konverzace se automaticky vytvoří při přechodu na bean, který je definován pro rozsah konverzace. Při přechodu na bean s jiným rozsahem (request, session) se konverzace automaticky ukončuje.

  • manuální - řízení začátku a konce konverzace je plně na programátorovi pomocí dostupného API.

Kromě toho knihovna ještě nabízí "persistence in conversation". Tedy něco jako "session in view", ale zde pouze v rozsahu konverzací.

Knihovna je nezávislá na implementaci JSF API, tedy funguje nejen nad Apache MyFaces, tak i nad Sun RI. Knihovny jsem zkoušel s JSF 1.1.


Pokud máte nějaké další tipy na knihovny, které pomohou s vývojem v JSF, tak sem s nimi prosím :).

Hibernate - práce s kolekcemi, ManyToMany vazba

S Hibernatem dělám již celkem dlouho, ale i tak pořád narážím na nové a nové věci (to bude asi tím, že jsem manuál k Hibernate celý ještě nečetl a vždy se učím až za pochodu). Teď naposledy jsem řešil celkem intenzivně kolekce a asociace.

Není List jako List

Hibernate z pohledu kolekcí rozlišuje tři základní implementace:
Přesnou definici jednotlivých typů lze najít zde.

Když máme třídu, která obsahuje nějaký seznam (java.util.List), tak to ještě vůbec neznamená, že i Hibernate s tím bude pracovat jako s indexovanou kolekcí. Pro Hibernate se to stává indexovanou kolekcí teprve tehdy, když je definován i index pro jednotlivé položky (@IndexColumn), jinak List Hibernate interně zpracovává jako bags, což může mít nějaké nevýhody, viz další kapitola.

Kromě samotné podoby kolekce je ještě potřeba brát v potaz použitou asociaci mezi třídou s kolekcí a elementy kolekce. Ne všechny případy asociací mohou být z principu indexované (např. obousměrná (bidirectional) ManyToMany vazba). Obecně nelze mít indexované kolekce označené jako inverzní (nastavení inverse="true" v XML konfiguraci nebo použití mappedBy při vytváření asociace pomocí anotací). Přehled typů kolekcí vs. asociací je možné nalézt zde.

One shot delete

Máme kolekci elementů a přidáme nový jeden element. Pokud kolekce není interně interpretována jako indexovaná kolekce, tak pak se změna provádí takto - smažou se všechny elementy a poté se znovu přidají jeden po druhém včetně toho nového. Toto může mít některé negativní dopady, např. pokud potřebujete vést historii všech změn nebo když např. používáme triggery nad vazební tabulkou. Z pohledu kolekcí je nejlepší mít indexované kolekce (viz předchozí kapitola) - pak má Hibernate vše pod kontrolou a když je potřeba přidat jednu položku, tak přidá do databáze pouze jednu položku.

Na druhou stranu je ale někdy žádoucí, aby se smazaly všechny položky a poté znovu přidaly (one shot delete), např. z pohledu výkonnosti, když deset položek chci odebrat a jednu smazat. Toho lze docílit znovu inicializací originální kolekce. Jinak řečeno vytvořím novou kolekci na základě staré.
Nevýhoda indexovaných kolekcí je ta, že při odmazání elementu uprostřed seznamu je nutné upravit indexy všech elementů, které následují.
Více lze opět najít v dokumentaci k Hibernate v kapitole One shot delete.

Práce s obousměrnou ManyToMany vazbou

Mezi objekty ClassA a ClassB je oboustranný vztah ManyToMany, tj. ClassA obsahuje množinu objektů ClassB a naopak. Pokud chceme přidat objekt ClassA do množiny objektů ClassB, pak musíme přidat i objekt ClassB do množiny objektů ClassA.
Z pohledu ukládání obou objektů a jejich kolekcí je potřeba si uvědomit, že nestačí provést uložení pouze nad jedním objektem. Pouze ta strana resp. ten objekt, který je vlastníkem vazby, může uložit změny do databáze.
Podrobné info je v této kapitole.

Otázkou je, zda se lépe nepracuje pouze s jednosměrnou ManyToMany vazbou. Relaci mezi objekty mi drží pouze jeden objekt, takže z pohledu spravování to mám jednodušší. A pokud chci získat i opačný pohled, tak mohu použít tento dotaz (vysvětlení: AssetGroup obsahuje kolekci objektů Asset, vazba mezi oběma objekty je ManyToMany):

SELECT ag
FROM com..AssetGroup ag,
com..Asset a
WHERE a IN ELEMENTS(ag.assets) AND a = :asset

Pořadí práce nad objekty

V nějakém příspěvku jsem našel následující pořadí, v kterém Hibernate vykonává operace nad "svými" objekty:
  1. all entity insertions, in the same order the corresponding objects were saved using Session.save()
  2. all entity updates
  3. all collection deletions
  4. all collection element deletions, updates and insertions
  5. all collection insertions
  6. all entity deletions, in the same order the corresponding objects were deleted using Session.delete()


Tento článek jsem napsal hlavně kvůli sobě, protože zapomínám. Vzvláště s Hibernatem je to znát, protože ten intenzivně používám pouze na začátcích projektů a to zase není tak často.