Jak powinien wyglądać schemat aplikcji klient serwer z użyciem JPA

0

Chcę sobie dla ćwiczeń napisać prostą aplikację serwerową (EJB + JPA + baza danych (JNDI)) i aplikację kliencką która realizowałaby pewne działania na bazie właśnie przez aplikację serwerową (jej metody). Nie mam w tym żadnego doświadczenia dlatego chciałem zapytać o podstawowy schemat działania czegoś takiego.
Opiszę moje (2) propozycje tego procesu.
Założenia są takie że klient, za pośrednictwem serwera, ma mieć możliwość wyszukiwania rekordów z danej tabeli bazy (np. Produkty) podając pewne kryteria (np. dotyczące nazwy produktu (jakiś Select z warunkiem dla nazwy)). Tabela ma pole @Version do sprawdzania Optimistic Locka aby zapobiec równoległym updateom.
*pyt 1 (najważniejsze). Czy aplikacja EJB powinna Selectować obiekty encji czy same pola z rekordów? Załóżmy że rekordów jest bardzo dużo (setki) i zapytanie może dać wiele wyników więc nagle musiałoby powstać w Persistence Context wiele obiektów encji a potem trzeba by je przesłać do klienta. Wydaje się to bardzo czasochłonne. Tymbardziej że zaraz po zakończeniu tej selectujacej metody w EJB wszystkie te encje byłby automatycznie oderwane od Persistence Context *

Następnie serwer zwraca klientowi listę wybranych rekordów (pola lub obiekty encji). Z wyświetlonej listy klient wybiera sobie jakąś pozycję którą np. chce zmodyfikować.
Opcja 1
Jeżeli do klienta została wysłana kolekcja obiektów encji (co wydaje mi się nieekonomiczne) to dalsze działanie jest proste. Klient zmienia pola obiektu encji, wywołuje metodę zapisującą która wysyła wybrany obiekt encji do aplikacji EJB, ta przy mergowaniu sprawdza pole @Version i jeżeli jest poprawna wartość to na koniec spłukuje zmiany do bazy i commituje je.
Opcja 2
Jeżeli klient dostał od serwera listę danych z pól rekordów (wynik zapytania o wartości pól - krotki) to są one w postaci Object[]. załóżmy, że są one również wyświetlane po jego stronie jako lista rekordów. Klient wybiera jeden z nich do modyfikacji, modyfikuje dane i wywołuje metodę EJB z tymi danymi jako parametrami. Metoda EJB tworzy konstruktorem obiekt encji z otrzymanych danych, ustawia wartość pola Version którą też otrzymała od klienta (a on z kolei z pierwszego zapytania) i merguje obiekt encji. Jak nie ma konfliktu Optimistic Locka to zmiany zostają zapisane do bazy.

  • Która opcja jest bardziej poprawna?*

P.S.
Macie może linka z opisami przyjętych szablonów programowania podobnych aplikacji?

0

Jakieś sugestie?

0

kluczowe pytanie czy chcesz by klient widział całe obiekty czy tylko pojedyncze pola.

0

To nie ma dla mnie znaczenia bo użytkownik i tak nie rozróżni przez swoje GUI co dostał do obróbki. Chodzi mi o to co jest poprawniejsze od strony programistycznej i wydajnościowej.
W pierwszym przypadku musi powstać dużo obiektów encji które są wysyłane do klienta (a przy relacjach "eager fetch" to może się ich zrobić nawet kilka razy więcej). W drugim musi powstać dużo obiektów Object[] które też są wysyłane.
Nie mam doświadczenia żeby określić co jest lepsze. Chyba że jest jeszcze jakieś inne rozwiązanie?

0

Najłatwiej wysyłać obiekty. Możesz też odpowiednio je serializować do formatu np. JSON. Z tablicą Object[] będziesz miał o tyle problem, że musisz ją serializować/deserializować ręcznie co tylko komplikuje życie. To, że będziesz czasami musiał przesłać dużą ilość danych... no cóż. Jeżeli klient chce dużo danych to powinien je dostać.

0

Z tą serializacją to nie będę się musiał ręcznie babrać bo metoda interfejsu EJB wywołana przez klienta zwróci mu obiekt List z obiektami encji lub obiektami Object[] tak wiec wszystko zostanie obsłużone przez RMI.

0

ale piszesz, że chcesz pojedyncze pola z obiektów, a nie kompletne obiekty.

0

No tak, ale zapytanie JPQL o pola zwróci mi wynik w postaci listy zawierającej obiekty Object[] to najprościej taką listę zwrócić klientowi. No mogę to jeszcze przepakować po stronie EJB w listę List<Object> gdzie pojedyncze wyniki Object[] zamienia na kolejne obiekty listy List<Object> (np. nazwa_1, cena_1, waga_1, nazwa_2, cena_2, waga_2 ... itd) ale nie wiem czy ta zamiana da wiele.

0

Rzuć okiem jeszcze na takie coś co się Tuple nazywa. I jeszcze jedno czy klientowi pozwalasz na pisanie zapytań JPQL czy tylko na wyklikanie warunków? W tym drugim przypadku niezłym rozwiązaniem może okazać się użycie Criteria API, które jest silnie typowane.

0

Klient określa tylko warunki. Criteria Query to mnóstwo kodu w porównaniu z JPQL a w rezultacie otrzymujesz to samo tylko że typowane. Samo Tuple w swoim rdzeniu to pewnie (tak myślę) też jest zwykła tablica (jak w przypadku większości List i Collections) więc chyba nie poprawi wydajności przesyłanych danych.

0

Poprawa wydajności powinna raczej opierać się o silne typowanie ponieważ w ten sposób unikniesz narzutu konwertowania typów po stronie klienta. Jedyna drogą na rzeczywistą poprawę jest kompresja w locie, ale to już jest wyższa szkoła jazdy. W dodatku nikt nie daje gwarancji, że zyski będą zauważalne.

0

Ok.
Czyli ogólnie unikać rzutowania u klienta.
Jeżeli nie robi to większej różnicy to wyśle mu obiekty encji bo to łatwiejsze (a właściwe to listę tych obiektów).
Dzięki.

0

Mały OT:
W Javie rzutowanie != konwertowanie. Rzutowanie w Javie jest generalnie tanie, konwertowanie czy autoboxing już nie.

0

konwersja to zrzucenie np. int na byte lub char na long ??

0

A to w takim razie chodziło mi o konwersję danych: http://pl.wikipedia.org/wiki/Konwersja_danych

Generalnie w Javie jeżeli np:

  • rzutujesz String na Object to praktycznie nic się nie dzieje,
  • rzutujesz int na Integer - następuje wyciągnięcie odpowiedniej instancji z małej statycznej puli Integerów (chyba dla wartości -128..127), a jeśli tam nie ma to utworzenie nowego obiektu typu Integer z kopią wartości,
  • rzutujesz Integer na int - nastepuje chyba wywołanie metody intValue() lub jakiejś jej zoptymalizowanej formy,
  • rzutujesz int na long - następuje konwersja danych,

W Javie jest wymazywanie typów, więc jak mam np kolekcje typu List<String> i wywołuję metodę get(index) to następuje rzutowanie zwróconego obiektu jeżeli go gdzieś zapamiętuję lub przesyłam dalej. Tak więc genericsy w Javie raczej wydajności nie poprawiają, a tylko służą do pisania kodu, który znacznie łatwiej się refaktoryzuje i jest znacznie mniej zabugowany.

0

Dzięki.
A tak poza tym może pamiętacie jak jest po angielsku "rzutować". Otarłem się gdzieś o to ale nie pamiętam a muszę teraz coś napisać (po angielsku) i nie chcę wtopić pisząc "throw". No a w słowniku, w tym znaczeniu, nie ma co sprawdzać.

0

Przesyłanie Object[] to zły pomysł (nieczytelny kod, brak kontroli typów).

Zamiast tego można zastosować:

  1. Stronicowanie (jednocześnie wyświetlasz do 20 obiektów, użytkownik ma możliwość chodzenia pomiędzy stronami). Przy stronicowaniu liczba obiektów nie jest problemem.
  2. Jeżeli korzystasz z Hibernate: projekcje + AliasToBeanResultTransformer + przesyłanie dtosów (http://docs.jboss.org/hibernate/orm/3.3/api/org/hibernate/transform/AliasToBeanResultTransformer.html)
0

Jak przeprowadzić takie stronnicowanie?

Pierwsze wywołanie metody EJB przez klienta zwraca mu wynik 20 obiektów encji i metoda się kończy. Klient ma je wyświetlone, przegląda a gdy skończy to musi mieć jakiś przycisk "next" który wywołuje metodę EJB (inną metodę) która z puli wyników pierwszego zapytania wybiera kolejne 20 wyników i zwraca. Wyniki muszą być zapamiętane w EJB więc musi ono być stateful.

Czy tak?

Tylko jak jeszcze spowodować aby wyniki były zwracane w kolejności według numeru ID (PK) bo z tego co wiem zapytania zwracają listę wyników w przypadkowej kolejności. Może jest jakiś parametr żeby to uporządkować?. Albo przepakowanie z sortowaniem po stronie EJB ....

0

Zapytanie zawsze może zostać posortowane i mieć wyznaczony limit.

String sql = "SELECT c.name FROM Country c ORDER BY c.name";
Query query = em.createQuery(sql);
query.setFirstResult(firstPosition);
query.setMaxResults(numberOfRecords);
List result = query.getResultList(); 
0

No faktycznie "ORDER BY" rozwiązuje sprawę.
Dzięki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1