Witam!
Ostatnio wspominałem, że zacząłem swoją zabawę z Java Enterprise Edition. Aplikacja, którą piszę ma za zadanie komunikację z bazą danych i wyświetlać z niej wyniki. Nie było by problemu, gdybym w czasie tworzenia aplikacji korzystał z jednej bazy, a tak korzystam z trzech - lokalnej, na wirtualnym serwerze oraz znajdującej się na serwerach uczelnianych. Z początku po prostu zmieniałem parametry przy połączeniu via JDBC, ale takie rozwiązanie mnie nie satysfakcjonowało. Poczytałem trochę, popytałem i dowiedziałem się, że idealnym rozwiązaniem było by JNDI + DataSource. W sieci można znaleźć sporo informacji na ten temat (szczególnie w kontekście Springa, którym zajmę się dopiero za dłuższy czas), ale ja szukałem totalnego minimum i świadomości jak to działa. Z pomocą przyszedł Paweł Cegła, który pokazał mi praktyczne zastosowanie JNDI i DS w projekcie. Teraz krótko (za Pawła zgodą) opiszę jak najprościej i najszybciej skonfigurować naszą aplikację, aby uzyskać dostęp do bazy danych, a w przypadku zmian parametrów ograniczało się to zaledwie do edycji jednego pliku.
Na początek tworzymy nowy projekt: File -> New -> Dynamic Web Project. Ja nadałem projektowi nazwę "JNDI_and_DS" i moja konfiguracja wygląda następująco:
Po stworzeniu projektu ma on następującą strukturę:
Dane konfiguracyjne aplikacji webowej umieszczamy w katalogu WebContent -> META-INF. Należy w nim utworzyć plik XML i nazwać go dokładnie context.xml:
Konfiguracja kontekstu aplikacji (bo o nim mowa) w najprostszej postaci wygląda następująco:
Jak widać składa się na nią kilka elementów:
- całość jest zawarta między znacznikami Context z parametrem path określającym ścieżkę (tzw. context path) do naszej aplikacji (należy pamiętać o znaku '/'!)
- pojedynczy zasób jest określony w obrębie znaczników Resource
i posiada liczne atrybuty:
- name - na podstawie tej nazwy będziemy wyszukiwać naszą konfigurację w kontekście aplikacji
- auth - tu do wyboru mamy "Application" lub "Container". Ta druga opcja wskazuje, że w imieniu naszej aplikacji to kontener, w którym jest uruchamiana będzie zarządzał połączeniami z bazą danych i całym DataSource
- type - ty wpisujemy jakiego typu zasób będziemy pobierać z kontekstu aplikacji odwołując się do jego nazwy (my chcemy DataSource, więc wpisujemy javax.sql.DataSource)
- maxActive - maksymalna ilość połączeń w obrębie DataSource z bazą danych. Musi zgadzać się z konfiguracją samej bazy danych
- maxIdle - maksymalna ilość tzw. uśpionych połączeń. Temat omówię w dalszej części posta
- maxWait - ilość czasu (w ms), jaką maksymalnie aplikacja oczekiwać będzie na odpowiedź serwera baz dancyh
- username - nazwa użytkownika w systemie baz danych
- password - hasło wyżej wymienionego użytkownika
- driverClassName - pełna ścieżka do pliku sterownika, za pomocą którego będzie wykonywane połączenie (ja korzystam z biblioteki JDBC i bazy MySQL, więc sterownik ten znajduje się w com.mysql.jdbc.Driver)
- url - ścieżka dostępu do naszej bazy danych (wraz z jej nazwą)
Jeśli wypełnimy poprawnie wszystkie te pola, możemy przystąpić do napisania servletu, który za pomocą DataSource będzie komunikować się z naszą bazą danych. Ja przyjąłem wariant, w którym stworzyłem servlet, z którego będą dziedziczyć wszystkie servlety współpracujące z bazą danych, więc sam obiekt DataSource jest polem tejże klasy, a w metodzie init wyszukuję w kontekście aplikacji DataSource. Jak to mówią - raz a porządnie :)
Wygląda to mniej więcej tak:
public void init(ServletConfig config) throws ServletException {
super.init();
try {
InitialContext context = new InitialContext();
dataSource = (DataSource) context
.lookup("java:/comp/env/jdbc/JNDI_and_DS");
} catch (NamingException e) {
throw new ServletException("Problem z JNDI i DS");
}
}
Dziwne może wydawać się może, że szukamy referencji do DataSource (metoda lookup) pod inną nazwą niż ustawiliśmy w konfifuracji. Otóż na serwerze Tomcat 6, kontekst aplikacji znajduje się pod adresem "java:/comp/env/". Niestety jest to inna ścieżka na różnych serwerach aplikacji.
Do pełni szczęścia brakuje nam już tylko dodanie biblioteki MySQL Connector/J (wersja JDBC ze sterownikiem do baz danych MySQL) do katalogu lib w katalogu, w którym znajduje się serwer Tomcat 6 i już!
W przypadku aplikacji napisanej przeze mnie (link do projektu Eclipse zamieściłem na końcu posta) poprawna konfiguracja i uruchomienie aplikacji objawia się takim komunikatem:
Cały czas wspominam o DataSource, ale wciąż nie wytłumaczyłem co to tak naprawdę jest. Otóż jest to obiekt, który w naszym imieniu przechowuje pulę połączeń (tzw. connection pooling) oraz pomaga w procesie transakcji. W przypadku naszej prostej aplikacji daje nam to ten komfort, że jesteśmy w stanie określić wszystkie parametry związane z połączeniami oraz dochodzi element związany z wydajnością. W momencie kiedy kontener tworzy obiekt DataSource, przypisuje mu kilka tzw. uśpionych połączeń (idle connections). Dzięki temu, kiedy nasza aplikacja chce połączyć się z bazą danych, połączenie nie jest tworzone (a jest to proces dosyć czasochłonny, szczególnie w MySQL'u), a jedynie pobierane z puli i udostępniane. Kiedy kończymy pracę z danym połączeniem wraca ono do puli i czeka na ponowne użycie.
A teraz kilka linków:
- Projekt Eclipse (wraz z skryptem SQL do utworzenia bazy danych i zapełnienia jej danymi)
- Słów kilka o connection pooling'u
- Przykład z JNDI i DataSource na oficjalnej stronie serwera Tomcat
- Trochę informacji o kontekście aplikacji uruchamianej na serwerze Tomcat
- Sterownik JDBC dla bazy MySQL
Mam nadzieję, że wpis się komuś przyda :) Zachęcam do komentowania i do usłyszenia w kolejnym poście!
Po dyskusji z Mateuszem dokonałem małej zmiany w wyjaśnieniu czym jest DataSource. Trochę się zapędziłem i przekazałem to nie tak jak trzeba. Mam nadzieję, że tym razem jest wszystko dobrze.
OdpowiedzUsuńZastanawia mnie, dlaczego nie skorzystałeś z adnotacji @Resource, aby mieć dostęp do danych, zamiast go odszukiwać w JNDI?
OdpowiedzUsuńBo mało wiem o adnotacjach i nie wiedziałem, że tak można :) Widać wreszcie trzeba będzie zgłębić temat. Dzięki Jacku :)
OdpowiedzUsuń