Skip to content
tomaszskutnik edited this page Jun 27, 2012 · 17 revisions

Gdzie jest moja tabela?

Czyli jak OneWeb SQL przyspiesza programowanie przy wciąż zmieniającym się schemacie bazy danych

Część 0: czego potrzebujemy

Zainstaluj na swoim komputerze następujące programy.

  1. Java 1.6 lub wyższa. Ustaw zmienną JAVA_HOME odpowiednio.
  2. PowerDesigner 16. Wersję ewaluacyjną można pobrać ze strony http://response.sybase.com/forms/PDV16-EA
  3. Eclipse (http://www.eclipse.org/downloads/)
  4. PostgreSQL 9.1 (http://www.enterprisedb.com/products-services-training/pgdownload). Razem z PostgreSQL instaluje się program pgAdmin. Jako locale wybierz ’Polish’.
  5. Tomcat 7.0 (http://tomcat.apache.org/download-70.cgi)
  6. OneWebSQL PD2Java 1.0.0 (http://OneWebSQL.com/download)

W instrukcjach dla użytkowników używamy następujących skrótów.

  • <PROJECT> – główny katalog aplikacji demo
  • <ONEWEBSQL HOME> – katalog, w którym jest zainstalowany OneWebSQL
  • <TOMCAT HOME> – katalog, w którym jest zainstalowany serwer Tomcat

Część 1: uruchomienie aplikacji

  1. Rozpakuj plik ZIP z aplikacją demo.
  2. Skonfiguruj projekt. Utwórz nowy projekt w Eclipse’ie File > New > Java Project ... i podaj lokalizację źródeł. new project
    Sprawdź, że katalogi src, generated-sources są folderami źródłowymi (Source Folders). Załóż katalog WEB-INF\classes i ustaw go jako folder docelowy (Output Folder).
  3. Skopiuj plik <ONEWEBSQL HOME>\onewebsql-runtime-1.0.0.jar do katalogu <PROJECT>\WEB-INF\lib. Skopiowany plik dodaj do Build Path.
  4. Do Build Path dodaj ścieżkę do pliku <TOMCAT HOME>\lib\servlet-api.jar.
  5. Przygotuj bazę danych. Uruchom program pgAdmin (został zainstalowany razem z PostgreSQL-em). Uruchom konsolę SQL.
  • (a) Stwórz użytkownika ’onewebsql’ z hasłem ’onewebsql’. create user onewebsql password ’onewebsql’;
  • (b) W ten sam sposób załóż bazę ’onewebsql’. create database onewebsql owner onewebsql;
  • (c) Połącz się z bazą danych jako użytkownik onewebsql.
  1. Uruchom skrypt zakładający tabele (<PROJECT>\database\create-schema.sql), wstaw dane początkowe (skrypt <PROJECT>\database\initial-data.sql).
  2. Skonfiguruj serwer Tomcat.
  • (a) kopiuj plik <PROJECT>\lib\postgresql-9.1-902.jdbc3.jar do <TOMCAT HOME>\lib.
  • (b) Do katalogu <TOMCAT HOME>\conf\Catalina\localhost skopiuj plik <PROJECT>\META-INF\context.xml. Zmień jego nazwę na onewebsql.xml.
  • (c) W pliku onewebsql.xml w atrybucie docBase podaj ścieżkę do projektu w Eclipse’ie.
  1. Uruchom serwer Tomcat: plik <TOMCAT HOME>\bin\startup.bat.
  2. W przeglądarce otwórz stronę http://localhost:8080/onewebsql.

Część 2: wprowadzenie

Będziemy pracować w prostej aplikacji WWW. Uruchom program PowerDesigner i otwórz plik <PROJECT>\database\Bookshop.pdm. Plik zawiera początkowy schemat bazy danych.

Dla każdej tabeli OneWebSQL generuje 3 pliki Javy:

  1. prostego beana reprezentującego wiersze tabeli,
  2. interfejs DAO, dający dostęp do tabeli,
  3. implementację DAO.

Jak to w życiu bywa, schemat bazy danych będzie się zmieniał. Dlatego każde ćwiczenie rozwiązuj w osobnej metodzie. Wtedy zobaczysz, w jaki sposób OneWebSQL pomaga poprawić kod aplikacji po zmianie schematu bazy danych.

Generowanie kodu

Użyjemy OneWebSQL-a w wersji standalone (jest też plugin do Maven 2). W Eclipse’ie wybierz Run > External Tools > External Tools Configurations... Utwórz nową konfigurację typu Program.

  1. Podaj javę jako program do uruchomienia.
  2. Jako katalog uruchomienia podaj swój projekt w Eclipse’ie.
  3. Podaj argumenty generatora:
java -jar <ONEWEBSQL_HOME>\onewebsql-pd2java-1.0.0.jar
     -classpath <ONEWEBSQL_HOME>\onewebsql-runtime-1.0.0.jar
     -model_sets database\Bookshop.pdm
     -java_output_dir generated-sources
     -java_default_package com.onewebsql.demo.or
     -dictionaries_dir database
  1. Zapisz konfigurację pod nazwą OneWebSQL. Uruchom generację.
  2. Odśwież projekt w Eclipse’ie.

Część 3: podstawowe API

  1. Obejrzyj kod klasy DemoServlet. Klasa zawiera szkielet aplikacji WWW. Główną metodą jest metoda doGet.
  2. Wypisz listę wszystkich książek. W metodzie doGet odkomentuj wywołanie metody exercise1. Przeczytaj kod metody exercise1. Do wypisania listy książek użyto metody getBookList() w interfejsie BookDAO.
  3. Uporządkuj wypisywaną listę książek według tytułu
    • (a) rosnąco,
    • (b) malejąco.
  4. Wykorzystaj metody z interfejsu BookDAO: getBookList(AExp orderByColumn) oraz `getBookList(AExp OrderByColumn, boolean ascending). Jako orderByColumn podaj stałą BookDAO.TITLE``.
  5. Wypisz listę książek o id większym od 2. Użyj metody getBookList(LExp whereClause) z interfejsu BookDAO. Warunek where powinien mieć postać BookDAO.NAZWA KOLUMNY.gt(2).
  6. Wypisz listę książek, których tytuły zaczynają się na literę A. Podpowiedź: użyj metody like.

Część 4: obiektowy język zapytań

  1. Nie wszystkie zapytania można wygenerować za pomocą metod get*List() z klas typu DAO. Istnieje również obiektowy język zapytań, w którym zapytania SQL można tworzyć bezpośrednio. Zapytanie jest reprezentowane przez obiekt klasy SelectQuery. Obiekt tego typu można podać, np. do metody getBookList(SelectQuery query) w interfejsie BookDAO. Poniższy diagram pokazuje, które klasy odpowiadają różnym częściom zapytania SQL.
  2. Wypisz listę wszystkich książek używając obiektowego języka zapytań. Zapytanie, które pobiera dane z tabeli book tworzymy za pomocą new SelectQuery(BookDAO.TABLE EXPRESSION)`. Zapytanie wykonujemy metodą getBookList(SelectQuery)w interfejsieBookDAO. SelectQuery query = new SelectQuery(BookDAO.TABLE_EXPRESSION);bookDAO.getBookList(query);``
  3. Powtórz poprzednie zapytania, używając obiektowego języka zapytań:
    • (a) Wypisz listę książek, których tytuły zaczynają się na literę A.
    • (b) Wypisz listę książek, które wydano po 2005 roku.
    • (c) Uporządkuj listę książek według roku wydania. Wykorzystaj metodę orderBy w klasie SelectQuery.
  4. Wypisz listę pierwszych 2 książek o ponad 100 stronach, w porządku alfabetycznym według tytułów. Wykorzystaj metodę limit(int) w klasie SelectQuery.

Część 5: zmiany, zmiany, zmiany

Wszyscy wiemy, że zmiany schematu bazy danych są nieuniknione. Tym razem zmiana jest prosta: kolumna number of pages nie będzie nam potrzebna i chcemy ją usunąć. Na tym przykładzie pokażemy, jak wygląda proces dewelopmentu aplikacji z OneWebSQL-em.

  1. Zmień schemat bazy danych. W schemacie bazy danych w PowerDesignerze (plik \database\Bookshop.pdm) w ta- beli book usuń kolumnę number of pages. Zapisz zmiany w PowerDesignerze.
  2. Wygeneruj skrypt SQL. Wygeneruj skrypt zakładający bazę danych (Database > Generate Database...).
  3. Wprowadź zmiany w bazie danych.
    • (a) W programie pgAdmin usuń wszystkie tabele w bazie onewebsql. Kliknij prawym klawiszem myszy na tabeli i wybierz Drop Cascaded.... Powtórz czynność dla sekwencji (węzeł Sequences).
    • (b) Uruchom wygenerowany przez PowerDesignera skrypt zakładający bazę danych. Kliknij ikonę Wykonaj dowolne zapytanie SQL. Wskaż lokalizację skryptu, a następnie kliknij przycisk Wykonaj zapytanie.
    • (c) Wstaw dane początkowe. Możesz użyć przygotowanego skryptu <PROJECT>\database\initial-data-without-number-of-pages.sql.
  4. Wygeneruj klasy OneWebSQL-a. Uruchom generator, który skonfigurowaliśmy w części 2. Odśwież projekt w Eclipse’ie.
  5. Popraw kod aplikacji. Zauważ, że niekompatybilne zmiany są sygnalizowane w postaci błędów kompilacji. Nie musisz zastanawiać się, co trzeba poprawić. Popraw kilka metod, pozostałe możesz usunąć lub zakomentować.

Część 6: złączenia tabel

  1. Wypisz listę książek, które należą do kategorii, których nazwa zaczyna się na literę P. Potrzebne zapytanie wykorzystuje dwie tabele: book i category. Możesz użyć:
  • (a) konstruktora klasy SelectQuery, który bierze listę tabel. Podaj odpowiedni warunek złączenia w metodzie where.
  • (b) konstruktora klasy SelectQuery, który bierze obiekt typu com.onewebsql.query.Join. Użyj odpowiedniej metody statycznej z klasy Join.
  1. Wypisz listę książek i nazwę kategorii, do której dana książka należy. Rekomendowany sposób to utworzenie odpowiedniego widoku i wygenerowanie dla niego klas OneWebSQL-a.
  • (a) W PowerDesignerze w panelu Toolbox wybierz ikonę View z sekcji Physical Diagram i kliknij w diagram. Zakończ dodawanie widoku klikając w prawy przycisk.
  • (b) Zmień nazwę widoku. Kliknij dwukrotnie na ikonę widoku. W oknie dialogowym wpisz nazwę: book_with_category_view.
  • (c) W zakładkę SQL Query wpisz treść zapytania SQL:
SELECT
book.book_id,
book.category_code,
category.category_name,
book.title,
book.publication_year
FROM
book JOIN category ON book.category_code = category.category_code;
  1. Wciśnij OK a następnie zapisz model PowerDesignera. W PowerDesignerze wygeneruj skrypt zakładający bazę danych. Skasuj tabele i sekwencje z bazy danych analogicznie jak w części 5. Następnie uruchom wygenerowany skrypt zakładający bazę danych i skrypt <PROJECT>\initial-data-book-with-category-view.sql. Wygeneruj klasy OneWebSQL-a i odśwież projekt w Eclipse’ie.
  2. Tę samą listę można wypisać inaczej, nie tworząc widoku, lecz korzystając z obiektowego języka zapytań.
  • (a) Utwórz obiekt SelectQuery reprezentujący zapytanie SQL z punktu 2c.
  • (b) Metody klas DAO w rodzaju getBookList(SelectQuery) przekształcają wyniki na obiekty klasy Book. Jeśli chcemy zwrócić obiekt innej klasy, musimy posłużyć się metodą selectObjectList(SelectQuery,RowHandler) i podać jej odpowiedni RowHandler.
public interface RowHandler<T> {
   T getObject(ResultSet resultSet) throws SQLException; 
 }

Obiekty typu com.onewebsql.util.jdbc.RowHandler przekształcają wyniki bazy danych na obiekty Javy.

  • (c). Przykładowy kod istniejącego RowHandlera.
 public class ListRowHandler<T> implements RowHandler<List<T>> {

   @SuppressWarnings("unchecked")
   public List<T> getObject(ResultSet resultSet) throws SQLException {
      int count = resultSet.getMetaData().getColumnCount();
      List<T> result = new ArrayList<T>(count);

      for (int i = 1; i <= count; i++) {
         Object obj = resultSet.getObject(i);
         if (resultSet.wasNull()) {
             obj = null;
         }
         result.add((T) obj);
      }
      return result;
   }
 }
  • (d) Użyj obiektu ListRowHandler<Object>, aby pobrać i wypisać wyniki zapytania utworzonego w punkcie 3a.

Część 7: zaawansowane zapytania

W obiektowym języku zapytań można tworzyć praktycznie dowolne zapytania SQL. Poniżej kilka przykładów.

  1. Napisz zapytanie zwracające informacje, w którym roku wydano ile książek. Przykładowe zapytanie SQL, które to robi.
SELECT publication_year, count(*)
FROM book
GROUP BY publication_year;

Kolumnę count(*) tworzy się tak: AExp.fun("count", AExp.exp("*")). Używamy tu statycznej metody com.onewebsql.query.AExp.fun(String,AExp). Klauzulę GROUP BY dodajemy wywołując metodę groupBy na obiekcie SelectQuery.

  1. Powtórz poprzednie ćwiczenie dla książek wydanych po roku 2005.
SELECT publication_year, count(*)
FROM book
GROUP BY publication_year;
HAVING publication_year > 2000;

Klauzulę HAVING dodajemy wywołując metodę having na obiekcie SelectQuery.

  1. Znajdź autora, który napisał najwięcej książek. Przykładowe zapytanie SQL, które to robi:
SELECT author.name
FROM author, book_author
WHERE author.author_id=book_author.author_id
GROUP BY author.author_id, author.name
HAVING count(*) >= ALL(
                      SELECT count(*)
                      FROM author,book_author
                      WHERE author.author_id = book_author.author_id
                      GROUP by author.author_id);

  1. Wylosuj dwie książki z listy wszystkich książek znajdujących się w bazie danych. Można to zrobić pobierając listę wszystkich książek z bazy danych. To rozwiązanie jest mało efektywne, jeśli danych w bazie jest dużo. Dlatego zrobimy to inaczej. Najpierw pobierz z bazy listę identyfikatorów książek. Posłuż się metodą selectObjectList z klasy BookDAO, jako RowHandler daj obiekt klasy SingleColumnRowHandler<Integer>. Z otrzymanej listy wylosuj dwa identyfikatory. Pobierz wylosowane książki posługując się wylosowanymi identyfikatorami.

Część 8: edycja danych

  1. Oprogramuj dodawanie nowego autora. Wstaw do metody doGet wywołanie metody dispatchRequest, usuń wywołania metod napisanych w poprzednich ćwiczeniach. Na głównej stronie aplikacji pojawi się formularz dodawania autora oraz lista autorów. Obsługą formularza zajmuje się metoda addAuthor(). Wypełnij jej treść. Do wstawiania nowych obiektów służy metoda insert(Author) w interfejsie AuthorDAO. Aby odczytać wartość pola name w formularzu, wywołaj metodę getParameter("name") na obiekcie HttpServletRequest.
  2. Oprogramuj edycję danych autora. Formularz edycji jest już przygotowany. Aby się do niego dostać, kliknij Edit na liście autorów. Obsługą formularza zajmuje się metoda saveAuthor. Najpierw pobierz obiekt autora za pomocą metody getByPK(Integer id) w interfejsie AuthorDAO. Wprowadź zmiany w obiekcie, a następnie zapisz zmiany w bazie danych za pomocą metody update(Author).
  3. Oprogramuj usuwanie autora z bazy danych, tę operację obsługuje metoda deleteAuthor. Do usuwania obiektów w bazie danych służy metoda delete(Author) z interfejsu AuthorDAO. Pamiętaj także o usunięciu powiązań autora z książkami.

Część 9: słowniki

  1. Zmień tabelę category na słownik. W katalogu <PROJECT>\database utwórz plik o nazwie category.csv o takiej strukturze:
category_code;code;category_name
prog;prog;Programming
dev;dev;Software Development

Wygeneruj klasy OneWebSQL-a i schemat bazy danych. Zauważ, że powstała nowa klasa CategoryDICT. Obejrzyj jej zawartość.

  1. Wypisz listę książek, które należą do kategorii prog. Użyj stałej słownikowej w warunku where.
  2. Przygotuj formularz edycji książki, w którym kategorię książki można wybrać z listy rozwijanej, wypełnionej istniejącymi kategoriami. Zauważ, nie musisz pobierać listy kategorii z bazy danych.

FAQ

com.onewebsql.util.jdbc.RuntimeSQLException: SQLSTATE: 42501;
ERROR CODE: 0; org.postgresql.util.PSQLException:

Uruchomiliśmy skrypty z root-a bazy danych (postgres), a nie z użytkownika onewebsql.