Jesteś tutaj

NetBeans 6.1 Beta - pierwsze podejście do JSF

Karty podstawowe

Jednak nie było tak łatwo. Poniższy artykuł powstawał od momentu mojej ostatniej publikacji nt. NetBeansa 6.1, więc trochę się męczyłem.

Tworzenie aplikacji rozpocząłem od pobrania najnowszej wersji rozwojowej NetBeansa 6.1, idąc za radą Jacka. Część problemów znikła, jednak w zamian pojawiły się inne, zwłaszcza męczący java.lang.IllegalStateException, który dość często odwoływał się do błędu #17020. Jak widać nie wszyscy programiści NetBeansa zapoznali się z bazą zamkniętych już błędów i powtarzają je nadal. Dobrze, że wbudowany mechanizm NeBeansa pozwala wyłapać takie "kwiatki".

Kolejnym krokiem było dodanie obsługi MySQLa, która jest wbudowana w najnowszego NetBeansa i dawało mi nadzieję, że pozwoli to w dużym stopniu wykorzystać możliwości bazy danych. Lokalnie już zainstalowałem wcześniej MySQL w wersji 5.0.51a-community-nt. Instalacja jest dziecinnie prosta, wymagała tylko trochę klikania ;D

Przechodzimy na zakładkę Services i wybieramy opcję Register MySQL Server, podajemy dane do połączenia, użytkownika i hasło. Hasło jest ważne, gdyż w innym przypadku, w dalszym kroku dostaniemy MissingProperty, gdyż nie można na serwerze Glassfish ustawić pustej właściwości w Connection Pool, co by było wymagane dla pustego hasła. Więc podczas instalacji serwera MySQL należy podać jakieś hasło.

Register MySQL Server
Filling connection properties

Na koniec, nasz serwer MySQL pojawia się na liście baza danych:

Server registered!

Możemy przystąpić do tworzenia bazy danych, co jest również proste, jednak brakuje możliwości wybrania typu tworzonej bazy danych. MySQL wspiera kilka rodzajów baz danych jak np. MyISAM, InnoDB, etc. Brak możliwości wyboru muszę ocenić na minus. Co ciekawe można nadać od razu uprawnienia do bazy danych - plus.

Create database

Fill database name

Automatycznie też tworzone jest nowe połączenie dla utworzonej bazy danych, które możemy wykorzystać w dalszej pracy.

New database connection

Nasza baza danych i połączenie do niej

Database and connection

Przystępujemy do tworzenia tabel. Trzy tabele: Customer, Item i tabele łącząca CustomerItem z kluczami obcymi z tabelami Customer i Item. Pierwsze uwaga to nie ma sensu używać dużych liter czy też TrybuWielbłądowego do nazwa tabel i kolumn. MySQL i tak wszystko zamieni na małe litery. Kolejny problem to raczej bardzo ograniczone wsparcie dla wszystkich własności tabel i niestety część operacji trzeba będzie wykonać z palca, pisząc odpowiednie skrypty SQL. Najbardziej brakującą opcją jest brak podania własności AUTO_INCREMENT dla klucza głównego, co niestety będę musiał dodać ręcznie.

Create table

Tabela customer:

Create table

Tabela item:

Create table

Tabela customer_item:

Create table

Struktura baz po utworzeniu tabel, widzimy klucze główne, możemy tworzyć indeksy (jest odpowiednie okno dialogowe), jednak nie ma możliwości utworzenia kluczy obcych - minus. Nie do końca rozumiem wsparcie tylko dla części opcji MySQLa, a całkowicie pominięcie innych. Może zostanie to zmienione w wersji finalnej.

Database structure

Niestety teraz trzeba sięgnąć do pomocy MySQLa i napisać trzy proste linijki, aby dodać AUTO_INCREMENT

AUTO_INCREMENT

Kolejna rzecz, to dla tabeli customer_item należy utworzyć klucze obce, pomoże to nam w przyszłości przy generowaniu zależności między ziarnami danych. I znów musiałem sięgnąć do pomocy i napisać dwie linijki SQLa.

FOREGIN KEY

Klucze obce zapewnią naszej bazie spójność danych, bez potrzeby stosowania żadnych dodatkowych funkcji, ta własność MySQL pojawiła się dopiero od wersji 5 i brak wsparcia dla niej w NetBeans jest dla mnie nie zrozumiały - minus.

Klucze obce:

FOREGIN KEY

Mając już odpowiednią strukturę bazy danych, możemy przystąpić do tworzenia aplikacji. Jak widać na razie dużo rzeczy można wyklikać, mam nadzieję, że wsparcie dla większości funkcji MySQLa pojawi się w wersji finalnej NetBeansa.

Tworzymy zwykłą aplikację WWW z dodaną obsługą JavaServer Faces, pomijam możliwość zastosowania Visual Web JavaServer Faces, gdyż istniejące kreatory nie pozwalają generować obiektów innych niż standardowe strony i ziarna zarządzane JSF na bazie istniejących ziaren danych. Więc tworzę projekt o nazwie jsf-mysql-demo

Create project

Wybieram Glassfisha jako środowisko uruchomieniowe, wybieram Java EE 5 i podaję kontekst aplikacji:

New web application

Ostatni krok to zaznaczenie wsparcia dla rusztowania JSF i projekt mamy gotowy:

Framework support

Wygenerowany szkielet aplikacji raczej nie jest niczym szczególnym, dopiero teraz mogę zabrać się za generowanie klas na bazie istniejącej struktury bazy danych. Wybieram opcję New file i z sekcji Persistance wybieram opcję Entity Classes from Database

Entity classes from Database

Na potrzeby mojej aplikacji muszę utworzyć nowe źródło danych, co sprowadza się do wybrania opcji z menu, podania nazwy JNDI i wybrania już istniejącego połączenia, które wykorzystałem do utworzenia tabel w bazie danych:

New entity

New datasource

Na liście po lewej stronie zostają pokazane wszystkie tabele, szybkie kliknięcie na Add all i mogę przejść dalej

FOREGIN KEY

Kreator podpowiada nam, że w projekcie nie ma zdefiniowanej jednostki JPA, więc za pomocą kilku kliknięć tworzymy nową, zaznaczając opcję None przy Table Generation Strategy, gdyż nie chcemy, żeby JPA zmieniało nam strukturę bazy danych i niszczyło zawarte w niej dane.

Persistance Unit

Ostatnim krokiem tego kreatora jest podanie pakietu i jeśli mamy chęć to zmiana nazw generowanych klas. Muszę tu umieścić małą uwagę na temat nazwy tabeli Item. Gdy w dalszym kroku będziemy generować strony JSF dla podanych ziaren to niestety NetBeans użyje tej samej nazwy do reprezentacji ziarna zarządzanego jak i naszego ziarna danych. Będzie to powodować nadpisanie jednego obiektu drugim a co za tym idzie, generować wyjątki. Proste rozwiązanie tego problemu podałem w odpowiednim punkcie i również zgłosiłem to jako błąd.

New entity - finsh

Classes

Mając już wygenerowane klasy, możemy przystąpić do generowania stron JSF do obsługi naszych obiektów, jak i zależności między nimi. I znów wybieramy New file, z sekcji Persistance opcje JSF Pages from Entity Classes

New JSF Pages

Wybieramy wszystkie utworzone wcześniej ziarna danych:

Entity Classes

I pozostaje nam podać, gdzie mają być ulokowane wygenerowane pliki JSP oraz podać nazwę pakietu:

Entity classes - finish

I tak oto prezentuje się kompletna struktura naszego projektu, pozostaje jedynie kliknąć Start i sprawdzić w boju naszą aplikację:

Project structure

Po uruchomieniu naszym oczom ukazuje się strona startowa, tak dla przypomnienia, nawet jej nie dotknąłem, wszystko zrobił za nas NetBeans - duży plus!

Welcome page

Wybieramy opcję Show All Customer Items i przechodzimy do tworzenia nowego klienta:

New Customer

I tu pojawia się zdziwienie, pole CustomerId, przecież jest to wartość generowane przez bazę danych i nie chcemy podawać jej z palca, gdyż może to spowodować rozjechanie się naszych danych. Szybkie śledztwo i mamy winowajcę, niestety znów jest nim NetBeans. Przy tworzeniu naszych ziaren danych, co prawda utworzył adnotację @Id ale już nie dodał @GeneratedValue(strategy=GenerationType.IDENTITY).

Missing annotation

Szybko naprawiamy ten błąd, dodając do wszystkich naszych zairen danych w/w adnotację:

Added missing annotation

Teraz niestety musimy usunąć utworzone wcześniej strony JSF, ziarna zarządzane i jak również usunąć wpisy w faces-config.xml. Niestety NetBeans nie zadba oto za nas, a szkoda - minus.
Ponownie generujemy strony JSF i uruchamiamy naszą aplikację. Teraz już wszystko gra:

New Customer

Tworzymy naszego pierwszego klienta, Id zostaje wygenerowane przez bazę danych. Następnie przechodzę do tworzenia obiektu Item i tu otrzymuję wyjątek, co ciekawe przechwycony przez rusztowanie JSF, więc nie mam pojęcia co się dzieje!

New Item

Trochę czasu szperam po sieci za pomocą Google, jednak nie znajduję nic konkretnego na szybko. Moje podejrzenia padają na pole description ziarna danych Item. Zostało ono wygenerowane jako tablica byte[], gdzie pole w tabeli zostało zdefiniowane jako BLOB. Niby dobrze, jednak najwyraźniej JSF ma z tym problem. Nie pozostaje mi nic innego jak usunąć cały projekt, usnąć tabele i zacząć od początku. Tym razem zamiast BLOB użyję VARCHAR(400).
Mając wprawę, cała operacja zajmuje jakieś 20 minut i możemy wrócić do ewaluacji JSF. Powracam do tworzenia nowego obiektu Item i znów zostaje zaskoczony wyjątkiem PropertyNotFoundException:

PropertyNotFoundException

I tutaj spędziłem trochę czasu nim doszedłem, gdzie jest problem. Jak wspomniałem wyżej, NetBeans używa nazwy item dla elementów tablicy, którą wyświetla na stronie. Wystarczy tylko zmienić nazwę w znaczniku dataTable z item na np. itemVar i zmienić odwołania w ciele dataTable. Niestety trzeba wiedzieć, które elementy zmienić, gdyż wszędzie mamy same item:

item to itemVar change

I w końcu mogę się cieszyć w pełni funkcjonalną aplikacją:

Item list

Customer detail

Podsumowując mój wywód, muszę przyznać, że jak do tej pory NetBeans oferuje najbardziej rozbudowaną możliwość generowania aplikacji JSF, niestety występujące problemy i bardzo zawiły kod jaki zostaje wygenerowany dla ziaren zarządzanych raczej nie zachęcają do dalszej nauki. Również błędy w generowaniu ziaren danych mogą początkujących programistów JSF przyprawić o mocny ból głowy. Moim zdaniem strony JSF i ziarna powinny być bardziej uproszczone, gdyż z tych kreatorów będą korzystać głównie osoby stawiające dopiero pierwsze kroki z JavaServer Faces. Takie nagromadzenie kodu szybciej ich zniechęci niż da podstawy do dalszej nauki.

Dla mnie o tyle ciekawa była ta wycieczka w świat JSF, że chyba w końcu zrozumiałem jaka działa cała ta magia JSF i ziaren zarządzanych. I niestety nie jest to żadna odkrywcza technologia, w takim samym układzie działa np. Struts2, gdzie akcje można porównać z ziarnem, a specjalne tagi JSF z tagami Struts2. Jedyna różnica jest taka, że JSF to standard a Struts2 nie. Chodź jak pokazuje historia Struts1, może stać się nie formalnym standardem w przyszłości ;-)

Pozdrawiam
--
Łukasz

Odpowiedzi

Właśnie otrzymałem informację, że problem z brakującą adnotacją @GeneratedValue może pojawić się nie wcześniej niż w wersji 6.5, gdyż obecnie kod 6.1 został już zamrożony.

Portret użytkownika witold szczerba

Hej,
zastanawia mnie dlaczego tworzyłeś tabele w MySQL'u ręcznie i na ich podstawie generowałeś klasy @Entity? Wg mnie dużo łatwiej jest zrobić to w odwrotną stronę, tzn. utworzyć klasę Item oraz Customer, następnie korzystając z mechanizmów podpowiadania dla obiektów @Entity (które działają wyśmienicie w NetBeans od wersji 5.5) skonfigurować zależnąść między nimi.
Po utworzeniu jednostki JPA należy tylko zaznaczyć:
Table generation strategy "Create" (zamiast pokazanego powyżej "None") i przy pierwszym uruchomieniu aplikacji TopLink sam utworzy za nas wszystkie 3 tabele.
Po pierwszym utworzeniu można już przełączyć na "None", w celu szybszego instalowania aplikacji na serwerze (inaczej za każdym razem będzie próbował tworzyć istniejące już tabele). Niebezpieczna jest tylko opcja "Drop and create", chociaż też się przydaje na samym początku, gdy zauważymy jakiś błąd.

Się zduplikowało ;-)

Pozdrawiam
--
Łukasz

- chciałem sprawdzić jakie jest wsparcie dla MySQLa w NetBeans
- niektórzy programiści lubią samemu stworzyć strukturę tabel i od tego zacząć tworzenie aplikacji
- nie zawsze sami tworzymy strukturę bazy danych, tylko musimy użyć już istniejącej, dlatego użycie kreatorów jest szybsze, daje nam szkielet klasy, które można później poprawić, etc.
- chciałem użyć kreatorów do utworzenia ziaren danych i stron JSF, aby zobaczyć na ile "inteligentny" jest NetBeans (jak widać jeszcze trochę mu brakuje)

Pozdrawiam
--
Łukasz

Portret użytkownika witold szczerba

Osobiście nie do końca jestem przekonany czy faktycznie moce przerobowe ludzi od NetBeans'a powinny być wykorzystywane w kierunku duplikowania funkcjonalności narzędzi zrobionych specjalnie do obsługu MySQL'a.

Jeśli mamy już gotową bazę, do której musimy się dopasować to nie potrzebny jest nam NetBeans w celu utworzenia struktury tabel, a jedynie do wygenerowania obiektów @Entity i tutaj jest tylko jeden drobiazg źle działający (o którym wspominałeś - ręcznie trzeba dodać pole @GeneratedValue).

Jeśli natomiast musimy taką bazę utworzyć sami, wtedy i tak: albo potrzeba będzie utworzyć taką bazę za pomocą skryptów, bo przecież nie będziemy u każdego klienta i na każdym komputerze generować takiej bazy klikając, albo tak, jak wspominałem (i tutaj o wiele lepiej sprawdza się generowanie w drugą stronę -czyli baza na podstawie obiektów @Entity) uzupełnione ewentualnie skryptami aktualizacyjnymi dla już działających instalacji.

Do generowania skryptów SQL oraz do specjalistycznego mieszania w strukturze bazy najlepiej jest wg mnie korzystać z narzędzi specjalnie do tego przeznaczonych, natomiast programiści NetBeans'a powinni wg mnie dać tylko podstawowe wsparcie i zająć się sprawami bardziej istotnymi niż podejmowanie nierównej walki z wyspecjalizowanym oprogramowaniem. Przecież MySQL to tylko jedna z wielu wielu baz danych, do tego wiele z nich ma (a jak nie ma to powinna mieć) już gotowe i bardzo rozbudowane narzędzia do ich obsługi.

Jak dla mnie, NetBeans jest adresowany do początkujących programistów, zaawansowani też będą go używać ale w całkiem inny sposób. Stąd takie podejście do tematu, aby "wyklikać" aplikację ;-) Nie mówię, czy to jest dobre podejście, ale dla osób nie obeznanych z Javą, którzy stawiają pierwsze kroki, takie podejście pozwoli im szybko zobaczyć efekty swojego klikania. Nie mówię, że będą to od razu sprzedawać. W dużym projekcie podejście będzie całkiem inne, ale nie o tym tu piszę.

Nie wiem, czy programiści NB marnują czas dodając wsparcie dla MySQLa, ale to wsparcie jest już dodane, więc tylko wytykam czego mu brakuje. Nie jest to tylko sterownik JDBC, ale specjalna funkcjonalność, zresztą jest to obecnie produkt Suna więc jak najbardziej powinni w swoich narzędziach wspierać swoje produkty. To jak by zarzucać M$, że Visual Studio wspiera tylko MS SQL Server ;-)

Witam, jestem początkującym programistą JAVA. Bardzo podoba mi się ten artykuł. Pokazuje jak w prosty i szybki sposób stworzyć aplikację web-ową, która współpracuje z bazą danych.
Na podstawie artykułu stworzyłem moją aplikację, bardzo podobną, różni się tylko tabelami.
Mam jednak pytanie:
Zarówno w przykładzie powyżej jak i w mojej aplikacji zamiast użytkownika pokazuje się np. Customer_id=1. Jak zmodyfikować kod żeby w tabeli pokazywało się imię i nazwisko a nie Customer_id=1?

Problem leży w generowaniu Entities przez NetBeansa - nie wykrywa on automatycznie generowanych wartości pól tabeli. Trzeba zrobić to samemu, czyli po wygenerowaniu Entities (jednak przed generowaniem stron), dodać do każdego pola Id adnotację @GeneratedValue i wtedy można przystąpić do generowania stron JSF.

Tak, ten temat był opisywany powyżej. Poza tym najnowsza wersja NetBeansa ma już poprawiony ten problem. Moje pytanie dotyczyło tego: jak zmodyfikować kod programu ażeby w tabeli otrzymać np imię i nazwisko osoby (np SELECT imię, nazwisko FROM Osoby WHERE osoba_id=1). W moim programie w tabeli otrzymuję "1" a chciałbym otrzymać imię i nazwisko osoby której osoba_id=1.

Masz na myśli "Customer Details" ? Jeśli tak to sprawdź źródła tej stronki i dodaj odpowiednie wywołania (zamiast customer zrób customer.firstName + " " + customer.lastName) albo nadpisz metodę toString() w klasie Customer.