25 marca 2007

Wstęp do technologii Apache iBATIS Data Mapper

A więc zaczynamy. Mój pierwszy blog, mój pierwszy artykuł. Będzie o technologii trwałości danych Apache iBATIS Data Mapper, http://ibatis.apache.org, znanej także pod bardziej trafną nazwą iBATIS SQL Maps. A ściśle rzecz biorąc o jej edycji dla Javy, bo jest też wersja dla .NET i Ruby'ego. No więc co to jest? Mówiąc krótko prosty i miły w użyciu szkielet (ang. framework) dla warstwy trwałości danych (ang. persistence). Nakładka na JDBC, która pozwala zachować zalety starego dobrego SQL'a zwalniając nas jednocześnie z konieczności mozolnego dłubania. W dokumentacji dla programisty (ang. Developer Guide) autorzy piszą że dostarcza 80% możliwości JDBC przy redukcji nakładów na programowanie do 20%. Spotkałem się z określeniem, że jest to lekkie rozwiązanie do mapowania obiektowo-relacyjnego (ang. light object-relational mapping) ale nie jest to szczególnie trafne. Używając iBATIS’a mapujemy bowiem nie na relacje, tabele relacyjnej bazy danych, ale na polecenia SQL. Najłatwiej będzie zrozumieć patrząc na przykład. Sercem aplikacji korzystającej z iBATIS’a jest plik konfiguracyjny XML postaci:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN”
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig> (...) </sqlMapConfig>

przy czym element <sqlMapConfig> zawiera kolejne pod elementy, będące właściwą konfiguracją. iBATIS jest biblioteką która działa ponad JDBC, najistotniejszym elementem pliku konfiguracyjnego jest więc konfiguracja sterownika JDBC, w tym celu dodajemy pod element:

<transactionManager type="JDBC" >
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${driver}"/>
<property name="JDBC.ConnectionURL" value="${url}"/>
<property name="JDBC.Username" value="${uname}"/>
<property name="JDBC.Password" value="${password}"/>
</dataSource>
</transactionManager>

przy czym wartości atrybutów @value odnoszą się do zmiennych wczytanych z pliku konfiguracyjnego .properties. Naturalnie możemy również te wartości podać explicite, wprost. Aby wczytać wspomniany plik .properties należy do elementu głównego dodać pod element:

<properties resource="ibatis/JDBCConfig.properties" />

gdzie plik JDBCConfig.properties to zwykły plik .properties który mógłby wyglądać tak jak ten:

driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:testdb
uname=scott
password=tiger

no i wreszcie pod element odpowiadający za mapowanie naszych obiektów języka Java na polecenia SQL, a ściślej rzecz biorąc, pod element powodujący wczytanie kolejnego pliku XML który to mapowanie definiuje:

<sqlMap resource="examples/domain/Person.xml" />

Plik konfiguracyjny zawiera jeszcze ustawienia ogólne, czyli pod element <settings>. Ogólne czyli jakie? Przykład poniżej:

<settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5" />

Można by jeszcze sporo napisać o głównym pliku konfiguracyjnym, ale ja celowo nie wdaje się w szczegóły, póki co wystarczy, że poczujemy o co w tym chodzi. Przejdźmy zatem do pliku mapowania, czyli jak mógłby wyglądać wspomniany wyżej plik Person.xml. Jakoś tak:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN”
"http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Person"> (...) </sqlMap>

gdzie element <sqlMap> zawiera pod elementy definiujące właściwe mapowania. Są to, jak już pisałem, mapowania na polecenia SQL. Mamy cztery rodzaje poleceń SQL (ang. CRUD) a więc i cztery rodzaje pod elementów XML, np.:

<select id="getPerson" resultClass="examples.domain.Person">
SELECT
ID as id,
FIRST_NAME as firstName,
LAST_NAME as lastName
FROM PERSON
WHERE ID = #value#
</select>

<insert id="insertPerson" parameterClass="examples.domain.Person">
INSERT INTO PERSON (ID, FIRST_NAME, LAST_NAME)
VALUES (#id#, #firstName#, #lastName#)
</insert>

<update id="updatePerson" parameterClass="examples.domain.Person">
(...)
</update>

<delete id="deletePerson" parameterClass="examples.domain.Person">
(...)
</delete>

w ten oto sposób zdefiniowaliśmy cztery, nazwane tak jak wskazuje atrybut @id, polecenia SQL, wszystkie odnoszące się do klasy examples.domain.Person. Klasa ta to zwykłe POJO, JavaBean. To by było na tyle jeśli chodzi o konfiguracje, możemy przejść do kodowania. Zaczynamy od zbudowania obiektu klienta iBATIS’a na podstawie skonstruowanego uprzednio pliku konfiguracyjnego:

Reader reader = Resources.getResourceAsReader(“ibatis/config.xml”);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);

i to by było na tyle, możemy uruchamiać nasze zapytania zdefiniowane w plikach mapowań, w naszym przypadku w pliku examples/domain/Person.xml. Wyglądało by to jakoś tak:

Person newPerson = new Person(1, „Mariusz”, „Lipinski” );
sqlMap.insert(„insertPerson”, newPerson);

Integer personPk = new Integer(1);
Person person = (Person)sqlMap.queryForObject(“getPerson”, personPk);

person.setLastName(„Lipiński”);
sqlMap.update(“updatePerson”, person);

sqlMap.delete(“deletePerson”, person);

Cisną się na usta pytania? Są niejasności? I bardzo dobrze. Jeśli pobudziłem ciekawość to jest to właśnie to, o co chodziło. Niecierpliwych odsyłam do dokumentacji projektu. Jest tego trochę i to całkiem niezłej jakości. Tych zafrapowanych nie dość zapraszam do lektury moich kolejnych artykułów na ten temat, które mam nadzieje powstaną w przyszłości. Dla tych, którzy nie słyszeli poprzednio o iBATIS’ie i mieli zamiar potraktować go jako ciekawą osobliwość, ale li tylko osobliwość napiszę jeszcze tylko, że twórcy Springa potraktowali go całkiem poważnie. Myślę, że mieli powody. Kropka. Czekam na komentarze, no i na te pytania!

1 komentarz:

Anonimowy pisze...

Fajny art szkoda ze taki krótki ale w sumie omówiłeś wszystko co mi było potrzebne na starcie. PDzieki i pozdrawiam