Bazy danych - JDBC

Koziołek

budowa.gif

Strona w budowie
Ktoś pracuje nad tą stroną, jej zawartość może się wkrótce zmienić. Prosimy o cierpliwość!

1 Wstęp
     1.1 Wymagana wiedza
2 Przygotowania
     2.2 Wybór bazy danych
     2.3 Maven
          2.3.1 Najpopularniejsze RDBMS w repozytorium mavena
3 Tworzenie schematu
4 Tworzenie klas modelu
5 Połączenie z bazą
     5.4 Sterownik
     5.5 ConnectionURL
     5.6 Połączenie z bazą
6 Operacje CRUD
     6.7 Interfejs CrudDao
     6.8 Klasa AbstractDAO
7 Podsumowanie

Wstęp

Gdy tworzymy oprogramowanie dochodzimy do momentu, w którym musimy zadecydować w jaki sposób będziemy składować dane. W najprostszej wersji dane są zazwyczaj składowane w pojedynczym pliku, ładowanym przy starcie programu i obsługiwanym przez narzędzia dostarczone z programem.
Bardzo często jest jednak tak, ze źródło danych jest odległe np. znajduje się na serwerze lub musi być dzielony przez wielu użytkowników w ramach jednego programu. Wtedy naturalnym wyborem jest baza danych.
Bazy danych w javie mogą być obsługiwane na kilka różnych sposobów w zależności od tego jakiego typu są to bazy. W dokumentacji możemy odnaleźć dwa główne sposoby na łączenie się z bazami danych. Pierwszy z nich to JDO. Mechanizm ten jest rozwijany jako mechanizmy JPA oraz Hibernate. Ten mechanizm szerzej jest znany jako Mapowanie Obiektowo Relacyjne (ORM). Drugą metodą dostępu do bazy danych jest JDBC. Rozwiązanie to jest dedykowane przede wszystkim dla RDBMS ponieważ jest silnie powiązane z językiem SQL.
JDBC jest interfejsem, a zatem nie jest związane z konkretnym dostawcą RDBMS. Z jednej strony jest to bardzo dobre ponieważ migracja pomiędzy dostawcami jest przezroczysta dla kodu aplikacji. Z drugiej strony możliwość bezpośredniego użycia SQL powoduje, że kod może zostać zanieczyszczony rozwiązaniami charakterystycznymi dla konkretnych producentów RDBMS.

W poniższym artykule przedstawię w jaki sposób korzystać z JDBC oraz jak uniknąć pułapek związanych z użyciem niestandardowej składni SQL w kodzie.

Wymagana wiedza

Żeby nie było później niejasności, zakładam, że posiadasz wiedzę z zakresu:

  • Podstaw javy
  • SQL i umiesz administrować w podstawowym zakresie wybranym RDBMS (tworzenie i usuwanie baz danych, użytkowników, widoków itp.)
  • potrafisz posługiwać się mavenem

Przygotowania

Zanim rozpoczniemy naszą przygodę z JDBC musimy poczynić pewne przygotowania.

Wybór bazy danych

Na początek musimy wybrać najbardziej odpowiedni RDBMS. Na rynku jest multum różnych implementacji baz danych. Do najpopularniejszych, niekomercyjnych i najczęściej używanych należą:

  • Postresql - najbardziej zaawansowany, wolnodostępny RDBMS na świecie. Czasami nazywany "Oraclem ubogich". Udostępniony na licencji BSD.
  • Firebird - jest to kolejny zaawansowany RDBMS. Nie jest tak popularny jak mySQL czy Postresql. Dostępny na zmodyfikowanej licencji Mozilla Foundation.
    Oczywiście możemy wybrać któryś z produktów komercyjnych:
  • mySQL - najpopularniejszy RDBMS w internecie. Zawdzięcza to łatwej obsłudze i "naturalnemu" związkowi z PHP. Do zastosowań komercyjnych należy kupić licencję. Do zastosowań niekomercyjnych bezpłatny.
  • Oracle Database - najbardziej znany komercyjny RDBMS. Posiada bardzo dużo ciekawych funkcjonalności. Do zastosowań niekomercyjnych bezpłatny.
  • MS SQL - RDBMS od microsoftu. W wersji express do zastosowań niekomercyjnych jest bezpłatny.
    Istnieje też szereg mniej znanych RDBMS, które mogą być bardzo przydatne w niektórych warunkach:
  • HSQLDB - RDBMS napisany w całości w Javie. Jego największą zaletą jest brak serwera co powoduje, że można go używać w aplikacjach klienckich jako pomocnicze źródło danych z np. konfiguracją. Firefox 3 używa HSQLDB w mechanizmie mądrego wyszukiwania w historii. Licencja własna.
  • SQLite - RDBMS napisany w C. Podobnie jak HSQLDB nie wymaga serwera. Ma wsparcie ze strony m.n. Adobe. Licencja Public Domain (prawa autorskie wygasły).
  • Apache Derby - włączona do Javy 6 i znana pod pojęciem JavaDB. Jest dostarczana razem z JDK.
    Wybierając RDBMS należy kierować się przeznaczeniem i wymaganiami , a nie popularnością czy ilością rozwiązań. W tym artykule będę używał Derby ponieważ, każdy użytkownik JDK posiada ją na swoim komputerze. Uważam, że w wielu przypadkach wystarczy używać prostych narzędzi, a nie dążyć do maksymalnego zwiększania kosztów (rozbudowane systemy obsługi baz danych wymagają zasobów).

Maven

Kolejnym krokiem jest przygotowanie projektu w mavenie. Wygeneruj projekt i wprowadź poprawki do pom.xml:

  • kompilator wersja 5
  • JUnit co najmniej 4.0
    Poniżej zamieszczam też zbiór sygnatur mavena dla wymienionych wcześniej RDBMS.

Najpopularniejsze RDBMS w repozytorium mavena

<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.3-603.jdbc4</version>
  • Firebird - nie ma oficjalnego repozytorium mavena. Sterownik jaybird, należy ręcznie zainstalować w repozytorium.
  • mySQL
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
<groupId>com.oracle</groupId>
<artifactId>classes12</artifactId>
<version>10.2.0.2.0</version>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
<groupId>org.sqlite</groupId>
<artifactId>sqlitejdbc-nested</artifactId>
<version>3.6</version>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.4.1.3</version>

Niestety nie wszystkie te pliki są dostępne w głównym repozytorium. Warto jednak zaznaczyć, że wszyscy najważniejsi producenci RDBMS udostępniają swoje implementacje JDBC.

Mój plik pom.xml wygląda w następująco:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>eu.runelord.wiki</groupId>
        <artifactId>JDBCTutorial</artifactId>
	<packaging>jar</packaging>
	<version>1.0-Draft1</version>
	<name>JDBCTutorial</name>
	<a href="http://wiki.runelord.eu">http://wiki.runelord.eu</a>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>[4.0, )</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.derby</groupId>
			<artifactId>derby</artifactId>
			<version>10.4.1.3</version>
		</dependency>
	</dependencies>
</project>

Tworzenie schematu

Przykładowy projekt to najbardziej znane bazodanowe zagadnienia z czasów studenckich czyli biblioteka. Uprościmy go jednak ponieważ celem tego artykuły nie jest przedstawienie skomplikowanej struktury bazy danych, a możliwości JDBC. JDBC jest interfejsem niskiego poziomu pozwalającym na bezpośrednią komunikację z bazą danych. Możemy zatem wprost tworzyć i wykonywać skomplikowane zapytania SQL. Nasza baza będzie pozwalała na wykonanie pewnych bardziej skomplikowanych zapytań, a zatem musimy posiadać aż dwie tabele:

CREATE TABLE ksiazki ( id INT NOT NULL, tytul VARCHAR(50) NOT NULL, autor INT NOT NULL, PRIMARY KEY (id) );
CREATE TABLE autorzy ( id INT NOT NULL, imie VARCHAR(50) NOT NULL, PRIMARY KEY (id) );

Nie jest to nic wyrafinowanego, ale nie o komplikację tu chodzi. W kolejnych artykułach związanych z JPA i Hibernatem będziemy korzystali z takiego samego modelu danych.

Tworzenie klas modelu

Przejdźmy teraz do stworzenia klas javy, które będą odpowiadały naszemu modelowi danych.

package eu.runelord.wiki.jdbctutorial.model;

public class Autor {

	private long id;

	private String imie;

	public Autor() {
	}

	public Autor(long id, String imie) {
		this.id = id;
		this.imie = imie;
	}

	public Autor(String imie) {
		this.imie = imie;
	}

	public String getImie() {
		return imie;
	}

	public void setImie(String imie) {
		this.imie = imie;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

}

package eu.runelord.wiki.jdbctutorial.model;

public class Ksiazka {
	private long id;

	private String tytul;

	private int autor;

	public Ksiazka(String tytul, int autor) {
		super();
		this.tytul = tytul;
		this.autor = autor;
	}

	public Ksiazka(long id, String tytul, int autor) {
		super();
		this.id = id;
		this.tytul = tytul;
		this.autor = autor;
	}

	public Ksiazka() {
	}

	public int getAutor() {
		return autor;
	}

	public void setAutor(int autor) {
		this.autor = autor;
	}

	public String getTytul() {
		return tytul;
	}

	public void setTytul(String tytul) {
		this.tytul = tytul;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

}

W artykułach o JPA i Hibernate klasy te wyglądają inaczej i są "bardziej obiektowe". Korzystanie z JDBC ma tą wadę, że musimy pisać kod bardziej przypominający PHP o ile oczywiście nie chcemy pisać własnego narzędzia ORM. W artykule pominę też zagadnienia związane z Data Transfer Object.

Połączenie z bazą

Skoro mamy już przygotowane klasy modelu oraz skrypty tworzące bazę danych czas przejść do sedna. Jeżeli jeszcze nie stworzyłeś bazy danych stwórz ją teraz. Można to zrobić różnego typu narzędziami dostarczanymi wraz z bazą danych.

Sterownik

Pierwszym krokiem będzie stworzenie kody odpowiedzialnego za połączenia z bazą danych. W tym celu potrzebujemy dwóch rzeczy. Pierwsza z nich to odpowiedni sterownik. W tym celu należy zagłębić się w dokumentację API paczki sterownika. Poszukujemy klasy, która implementuje java.sql.Driver. Warto też przejrzeć poradniki producenta RDBMS, ponieważ zazwyczaj jest tam podana nazwa klasy sterownika. W przypadku Derby jest to klasa org.apache.derby.jdbc.EmbeddedDriver

ConnectionURL

Drugim elementem niezbędnym do prawidłowego połączenia się z bazą danych jest stworzenie odpowiednio spreparowanego adresu URL, który określa nie tylko to z jaką bazą się łączymy, ale też nazwę użytkownika oraz hasło. Całość znowu zależy od producenta. Warto przejrzeć dokumentację i poszukać różnych wariantów tego adresu.
W przypadku derby wygląda on następująco:

jdbc:derby:«nazwa bazy na lokalnym komputerze - ścieżka w drzewie katalogów/adres_serwera»;«opcje»

W naszym przypadku będzie to wyglądać następująco:

jdbc:derby:jdbctutorialDB;create=true

Połączenie z bazą

Czas nawiązać połączenie z bazą danych. W tym celu napiszemy klasę fabrykującą, która będzie udostępniała nam połączenia.

package eu.runelord.wiki.jdbctutorial.tools;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import eu.runelord.wiki.jdbctutorial.ConfigurationException;

public class DBConnectionFactory {

	private static String connectionURI;
	private static boolean isPrepared = false;
	private static String connectionClass;

	private static void prepare() throws ConfigurationException {
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream("db.configuration"));
		} catch (FileNotFoundException e) {
			throw new ConfigurationException(e);
		} catch (IOException e) {
			throw new ConfigurationException(e);
		}
		if (properties.containsKey("connection.uri")) {
			connectionURI = properties.getProperty("connection.uri");
		} else {
			throw new ConfigurationException("brak klucza connection.url");
		}
		if (properties.containsKey("connection.class")) {
			connectionClass = properties.getProperty("connection.class");
		} else {
			throw new ConfigurationException("brak klucza connection.class");
		}
		try {
			Class.forName(connectionClass);
		} catch (ClassNotFoundException e) {
			throw new ConfigurationException("brak sterownika " + connectionClass);
		}
		isPrepared = true;
	}

	public static Connection getConnection() throws ConfigurationException {
		if (!isPrepared)
			try {
				prepare();
			} catch (ConfigurationException e) {
				e.printStackTrace();
			}
		try {
			Connection connection = DriverManager.getConnection(connectionURI);
			return connection;
		} catch (SQLException e) {
			throw new ConfigurationException(e);
		}
	}

}

Jest to jedno z wielu różnych podejść do problemu zarządzania połączeniami. Jego zaletą jest prostota. Każda operacja będzie pracowała na własnym połączeniu z bazą danych. Trzeba tylko pamiętać o zamykaniu połączeń, ale to można uzyskać za pomocą metod finalize() w obiektach, które będą posiadały obiekty Connection.
Znacznie lepszym podejściem jest przesłonięcie klasy Connection własna klasą delegującą metody do klasy Connection i zarządzanie połączeniami za pomocą np. puli połączeń.
Niezależnie od wybranej metody schemat jest taki sam. Najpierw wywołujemy:

Class.forName(connectionClass);

sprawdzając tym samym czy istnieje klasa odpowiedzialna za obsługę połączeń. Bardzo częstym błędem jest niedołączanie pakietu .jar ze sterownikiem do projektu. Jako, że wykorzystujemy tu dynamiczne odwołanie do klasy to kompilator nie jest wstanie sprawdzić czy spełniona jest ta zależność. Tak samo nie jest ona sprawdzana w momencie uruchamiania programu i wczytywania definicji klas. Sprawdzenie następuje dopiero w momencie wywołania metody. Należy pamiętać o tym mechanizmie i w pierwszej kolejności sprawdzać czy są wszystkie potrzebne biblioteki.

Obsługę błędów obudowałem jedną klasą. Nie jest ona najistotniejszym elementem w naszym projekcie i takie uogólnienie pozwala zaoszczędzić czas. Dla porównania Hibernate też obudowuje błędy związane z bazą, ale używa do tego własnych wyjątków dziedziczących po RuntimeException.

Ostatnim krokiem będzie utworzenie tabel, o ile jeszcze tego nie zrobiliśmy, za pomocą kilku prostych operacji. Klasa Main takiego programu wygląda następująco;

package eu.runelord.wiki.jdbctutorial;

import java.sql.Connection;

import eu.runelord.wiki.jdbctutorial.tools.DBConnectionFactory;

public class Main {
	public static void main(String[] args) throws Exception {
		Connection conn = DBConnectionFactory.getConnection();
		String booksQuery = "CREATE TABLE ksiazki ( id INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), tytul  VARCHAR(50) NOT NULL, autor INT NOT NULL, PRIMARY KEY (id) )";
		String authQuery = "CREATE TABLE autorzy ( id INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), imie VARCHAR(50) NOT NULL, PRIMARY KEY (id) )";
		conn.createStatement().execute(booksQuery);
		conn.createStatement().execute(authQuery);
		conn.close();
	}
}

Poszczególne elementy tego programu omówię w następnym rozdziale.

Operacje CRUD

Podstawą pracy z bazą danych są operacje CRUD (od angielskich nazw operacji Create - tworzenia, Read - odczytywania, Update - aktualizacji, Delete - usuwania). Można implementować je na wiele różnych sposobów. Najprostszą z nich jest, tak jak przy tworzeniu tabel w poprzednim rozdziale, wykorzystanie bezpośrednio interfejsu JDBC. W takim podejściu po stworzeniu połączenia z bazą danych tworzymy komunikat za pomocą metody createStatement() i wykonujemy za jego pomocą zapytanie (metoda execute()).
Drugą metodą znaną z PHP jest napisanie specjalnego generatora zapytań, który na podstawie struktury obiektu tworzy odpowiednie zapytanie. Takie podejście jest stosowane w implementacjach ORM np. Hibernate. O ile w PHP jest ono dobre to w javie wymaga stworzenia mechanizmu mapującego. Można go wyposażyć w odczyt mapowań z adnotacji lub plików konfiguracyjnych. Nie jest to łatwe, a naszym celem jest stworzenie prostego przykładu.
Dlatego też wybierzemy drogę pośrednią. Stworzymy klasy, które będą zawierały wcześniej przygotowane zapytania i tylko będą je uzupełniać informacjami z dostarczonych im obiektów. Strategia ta sprawdza się tam, gdzie liczba klas modelu jest niewielka i nie ma pomiędzy nimi skomplikowanych zależności.

Interfejs CrudDao

Zanim jednak stworzymy nasze klasy zastanówmy się przez chwilę nad kwestią przenośności i rozszerzalności naszego kodu. Skoro tworzymy klasy ze sztywno zapisanymi zapytaniami to każda zmiana w modelu będzie wymagała przejrzenia kodu i wprowadzenia poprawek. Co stanie się jeżeli zmienimy całkowicie sposób zarządzania w komunikacji z bazą danych i na przykład zechcemy wykorzystać JPA? Przydało by się stworzyć uniwersalny mechanizm pozwalający na ukrycie implementacji. Taki mechanizm nazywa się CrudDao. Nie jest to jakaś dobrze zdefiniowana technologia, ani wzorzec projektowy. Jest to raczej wspólna nazwa na pewien sposób myślenia. Najlepiej zobrazuje to kod:

package eu.runelord.wiki.jdbctutorial.controller.dao;

import java.sql.SQLException;
import java.util.List;

/**
 * Interfejs CrudDao odpowiada za obsługę prostych operacji na bazie danych:
 * <ul>
 * <li>Tworzenia (ang. <b>C</b>reate)</li>
 * <li>Odczytywania (ang. <b>R</b>ead)</li>
 * <li>Aktualizacji (ang. <b>U</b>pdate)</li>
 * <li>Usuwania (ang. <b>D</b>elete)</li>
 * </ul>
 * Interfejs ten jest implementowany przez różnego rodzaju obiekty dostępu do
 * danych (DAO ang. akr. - <b>D</b>ata <b>A</b>ccess <b>O</b>bject)
 * 
 * @author koziolek
 * 
 */
public interface CrudDao {

	/**

	 * Dodawanie rekordu do bazy
	 * 
	 * @param <T>
	 * @param obj
	 * @throws SQLException
	 */
	public <T> void create(T obj) throws SQLException;

	/**
	 * Odczytywanie rekordy z bazy na podstawie spreparowanego obiektu.
	 * 
	 * @param <T>
	 * @param obj
	 * @return
	 * @throws SQLException
	 */
	public <T> List<T> read(T obj) throws SQLException;

	/**
	 * Aktualizowanie rekordu
	 * 
	 * @param <T>
	 * @param obj
	 * @return
	 * @throws SQLException
	 */
	public <T> T update(T obj) throws SQLException;

	/**
	 * Usuwanie rekordu
	 * 
	 * @param <T>
	 * @param obj
	 * @return
	 * @throws SQLException
	 */
	public <T> T delete(T obj) throws SQLException;

}

Jak widać nasz interfejs pozwala na wykonywanie podstawowych operacji na obiektach. Użyty jest typ generyczny w celu uogólnienia interfejsu i pozbycia się konieczności rzutowania.
Jeżeli jednak przyjrzymy się dokładnie to stwierdzimy, że brakuje nam tu możliwości wykonywania skomplikowanych zapytań. Biblioteki ORM stosują zazwyczaj skomplikowane algorytmy w celu generowania złożonych zapytań. My podejdziemy do tej sprawy po najmniejszej linii oporu i utworzymy metodę proccedCustomQuery(), która na użytkownika przeniesie odpowiedzialność za przetwarzanie zapytań. Dodatkowo zaimplementujemy obsługę operacji CRUD.
Na koniec słowo o tym czym jest DAO. Data Access Object (ang.), czyli obiekt dostępu do danych jest ogólną nazwą na wszystkie obiekty, które umożliwiają dostęp do danych. W pewnym sensie DAO są o klasę abstrakcji wyżej niż ORM ponieważ opakowuje dowolny sposób dostępu do danych.

Klasa AbstractDAO

Klasy abstrakcyjne są w Javie traktowane trochę po macoszemu. Wielu początkujących programistów zadaje pytanie po co ich używać. Klasy abstrakcyjne w połączeniu z interfejsami są doskonałymi budulcami dla wzorców strategii i wzorców metody szablonu. Klasy abstrakcyjne są też doskonałym miejscem do umieszczania domyślnych implementacji niektórych metod oraz metod narzędziowych. Nasza klasa Abstract DAO wygląda następująco:

package eu.runelord.wiki.jdbctutorial.controller.dao;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import eu.runelord.wiki.jdbctutorial.ConfigurationException;
import eu.runelord.wiki.jdbctutorial.tools.DBConnectionFactory;

public abstract class AbstractDAO implements CrudDao {

	private final Connection connection;

	private static Connection privConn;

	static {
		try {
			privConn = DBConnectionFactory.getConnection();
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
	}

	public AbstractDAO(Connection connection) {
		this.connection = connection;
	}

	public <T> void create(T obj) throws SQLException {
		proceed(getCreateSQLProcessor(), obj);
	}

	public <T> T delete(T obj) throws SQLException {
		return proceed(getDeleteSQLProcessor(), obj);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> read(T obj) throws SQLException {
		return (List<T>) proceed(getReadSQLProcessor(), obj);
	}

	public <T> T update(T obj) throws SQLException {
		return proceed(getUpdateSQLProcessor(), obj);
	}

	private <T> T proceed(SQLProcessor processor, T object) throws SQLException {
		ResultSet resultSet = null;
		Statement stm = connection.createStatement();
		stm.execute(processor.createSQL(object));
		resultSet = stm.getResultSet();
		return processor.proceedResultSet(resultSet);
	}

	/**
	 * Metoda pozwala na wykonanie złożonych zapytań. Zwracać wyniki "tak jak
	 * jest" i nie poddaje ich obróbce. Pracuje w ramach własnego połączenia.
	 * 
	 * @param customQuery
	 * @return
	 * @throws SQLException
	 * @throws ConfigurationException
	 */
	public static synchronized ResultSet proccedCustomQuery(String customQuery)
			throws SQLException, ConfigurationException {
		Statement stm = privConn.createStatement();
		stm.execute(customQuery);
		return stm.getResultSet();
	}

	protected abstract SQLProcesor getCreateSQLProcessor();

	protected abstract SQLProcesor getReadSQLProcessor();

	protected abstract SQLProcesor getUpdateSQLProcessor();

	protected abstract SQLProcesor getDeleteSQLProcessor();

	@Override
	protected void finalize() throws Throwable {
		connection.close();
	}
}

Na uwagę zasługuje tu kilka elementów. Po pierwsze metoda procced():

private <T> T procced(SQLProccesor proccesor, T object) throws SQLException {
	ResultSet resultSet = null;
	Statement stm = connection.createStatement();
	stm.execute(proccesor.createSQL(object));
	resultSet = stm.getResultSet();
	return proccesor.proccedResultSet(resultSet);
}

To właśnie ona jest prawdziwym sercem wszystkich naszych obiektów DAO. Jest to metoda szablonowa realizująca proces tworzenia komunikatu, wysyłania zapytania, pobierania wyników i ich obróbki. Pojawia się w niej też kolejny element naszej układanki jest to interfejs SQLPoccessor wygląda on w następujący sposób:

package eu.runelord.wiki.jdbctutorial.controller.dao;

import java.sql.ResultSet;
import java.util.List;

/**
 * Interfejs za pomocą, którego tworzymy zapytania sql i przetwarzamy wyniki.
 * 
 * @author koziolek
 * 
 */
public interface SQLProcessor<T> {

	/**
	 * Metoda tworzy zapytanie sql na podstawie obiektu.
	 * 
	 * @param obj
	 * @return
	 */
	public String createSQL(T obj);

	/**
	 * Metoda przetwarza wyniki zapytania.
	 * 
	 * @param resultSet
	 * @return
	 */
	public List<T> proceedResultSet(ResultSet resultSet);
}

W tym miejscu warto zauważyć pewną właściwość obiektów DAO. Nigdzie nie są ustanowione żadne połączenia z jakimkolwiek graficznym interfejsem użytkownika. Dość częstym błędem jest chęć wyświetlenia danych już w miejscu ich przetwarzania. Takie działanie jest sprzeczne ze wzorcem MVC.

Podsumowanie

User:Koziołek Koziołek

1 komentarz

No mam nadzieje że wkrótce będzie gotowe :)