2010.06_Biblioteka Dozer Proste mapowanie obiektów_[Biblioteka Miesiaca].pdf
(
1065 KB
)
Pobierz
441090793 UNPDF
BIBLIOTEKA MIESIĄCA
Biblioteka Dozer
Proste mapowanie obiektów
Ręcznie pisanym translatorom można zarzucić wiele –
są czasochłonne, błędogenne, a ich przejrzystość zależy
w dużej mierze od umiejętności programistycznych.
Może czas odrzucić dotychczasowe przyzwyczajenia?
Interesującym rozwiązaniem wydaje się być biblioteka Dozer.
Dowiesz się:
• W jaki sposób zainicjalizować Dozer'a;
• W jaki sposób tworzyć proste i zaawansowane pliki konigura-
cyjne wykorzystywane przez Dozer'a;
• W jaki sposób mapować obiekty typu JAXB przy pomocy Dozer'a.
Powinieneś wiedzieć:
• Jak programować w języku Java;
• Podstawowa znajomość XML'a.
pomiędzy obiektami z różnych modeli danych.
W przypadku systemów rozproszonych,
obiekty przekazywane są pomiędzy różnymi systemami.
Sytuacje, w których zewnętrzny model danych wykorzy-
stywany jest wewnątrz naszej aplikacji, są niepożądane.
Zdarza się, że dostawca usług nagle zmienia w zna-
czący sposób model danych. Następstwem tego są
zmiany w całej naszej aplikacji. Dlatego lepszym roz-
wiązaniem byłoby mapowanie obiektów zewnętrznych
na nasz wewnętrzny model danych, na najniższej war-
stwie. W dużej mierze uniezależniłoby to nasz system
od zmian pochodzących z zewnątrz.
Powszechnym rozwiązaniem powyższego problemu
jest pisanie ręcznych translatorów. Jednak implementa-
cja ich jest zbyt czasochłonna, a programista jest zmu-
szony do ręcznego odwzorowania wszystkich zależ-
ności. Nawet gdy klasy różnią się między sobą w nie-
wielkim stopniu. Tak zaimplementowany kod jest mało
przejrzysty i trudny w szybkiej analizie. Dodatkowo sto-
sunkowo łatwo o pomyłkę.
W niniejszym artykule przedstawię, w jaki sposób roz-
wiązać typowe problemy mapowania przy pomocy biblio-
teki Dozer. Biblioteka wprowadza jednolity sposób zapi-
sywania relacji pomiędzy obiektami w plikach XML. Nie
wymaga ona żadnej modyfikacji istniejących klas (np.
Szybki start w Eclipse
Koniguracja projektu w Eclipse:
1. Stworzyć w Eclipse nowy projekt Java;
2. Pobrać binarną dystrybucję biblioteki Dozer (patrz: ramka W sieci);
3. W zakładce do konigurowania bibliotek zewnętrznych (
Project -> Properties -> Java Build Path -> Libraries
) dodać bibliotekę
dozer.osgi-5.2.0.jar
za pomocą przycisku
Add external JARs
;
4. Powtórzyć kroki 2 i 3 dla bibliotek JAXB, Apache Commons Beanutils, Apache Commons Collections, Apache Commons Lang
i Apache Commons Logging.
Dodatkowo w przypadku biblioteki JAXB, po ściągnięciu jej z Internetu należy ją rozpakować, stosując się do instrukcji umieszczo-
nych na stronie domowej biblioteki. Spośród wypakowanych plików, do build path'a należy dodać plik o nazwie
jaxb-api.jar.
Z przyczyn technicznych na niektórych Listingach nie znalazły się deinicje setter'ów i getter'ów, co zostało oznaczone stoso-
wanym komentarzem. Jednak w łatwy sposób Czytelnik może wygenerować wspomniane funkcje za pomocą narzędzi wbu-
dowanych w Eclipse'a. W kodzie źródłowym klasy wywołujemy menu kontekstowe i klikamy kolejno
Source -> Generate getters
and setters
.
6
6/2010
M
apowanie obiektów jest to kopiowanie danych
Biblioteka Dozer
dodanie adnotacji). Dzięki temu jest to w pełni przeźro-
czyste rozwiązanie. Pierwsza wersja biblioteki Dozer zo-
stała wydana w 2005 roku. Od tego czasu jest intensyw-
nie rozwijana i optymalizowana. Ponadto cechuje się
przemyślanymi rozwiązaniami i dużą stabilnością.
Struktura niniejszego artykułu jest następująca: na
początek przedstawię sposób wykorzystania API Do-
zer'a oraz podstawową koncepcję zapisywania rela-
cji w plikach konfiguracyjnych. Następnie zaprezentu-
ję rozbudowane mechanizmy sterowania mapowaniem
i omówię szczegółowo zastosowane techniki. W dal-
szej kolejności zmierzę się z rzeczywistym problemem
– mapowaniem obiektów typu JAXB. Na koniec porów-
nam wydajność pomiędzy Dozer'em a ręcznie pisany-
mi translatorami.
wpisać i skompilować klasy znajdujące się na Listin-
gach od 1 do 5. Przykład uruchamiamy, wywołując kla-
sę
DozerExample
.
Przeanalizujmy krok po kroku kod programu znajdu-
jący się na Listingu 1. Na początku dołączamy niezbęd-
ne nagłówki. Następnie inicjalizujemy bibliotekę Dozer,
za co odpowiedzialna jest instrukcja:
DozerBeanMapper mapper = (DozerBeanMapper) DozerBeanMapp
erSingletonWrapper.getInstance();
Wykorzystaliśmy tutaj klasę
DozerBeanMapperSingleton
Wrapper
, ponieważ twórcy biblioteki zalecają, aby w ca-
łej naszej aplikacji występowała tylko jedna instancja
klasy
DozerBeanMapper
(tzw. singleton). Dalej inicjalizu-
jemy klasę
JabberPerson
testowymi wartościami:
Zaczynamy
Pierwszą czynnością, którą należy wykonać, jest konfi-
guracja środowiska pracy. Osoby korzystające z Eclip-
se'a zapraszam do przestudiowania zawartości ramki
Szybki start w Eclipse
. Czytelników chcących wykorzy-
stać bibliotekę wraz ze Spring Framework odsyłam do
ramki
Szybki start w Spring
. Warto także wspomnieć,
że biblioteka Dozer jest dostępna w centralnym repozy-
torium Maven'a. W ramce
W sieci
umieściłem bardziej
szczegółowe informacje z tym związane.
Gdy środowisko pracy jest już gotowe, możemy roz-
począć przygodę z Dozer'em. Na potrzeby artykułu wy-
obraźmy sobie, że jesteśmy programistami multiko-
munikatora internetowego, Pidgin. Aplikacja jest zmu-
szona do mapowania obiektów konkretnego protoko-
łu na swój model danych. Tak więc należy utworzyć,
JabberPerson source = new JabberPerson("Jan",
"Kowalski", 40, new
JabberAddress("Wojska Poskiego",
"3"));
Ostatecznie zlecamy bibliotece przemapowanie obiek-
tu typu
JabberPerson
na obiekt typu
PidginPerson
za
pomocą instrukcji:
PidginPerson destination = mapper.map(source,
PidginPerson.class);
Listing 1.
Najprostsze użycie biblioteki Dozer (plik
DozerExample.java)
Szybki start w Spring
Spójrzmy na prosty przykład, tego, jak wykorzystać Do-
zer'a w istniejącym serwisie zarządzanym przez Sprin-
g'a. W tym celu, w dowolnym serwisie, deiniujemy inter-
fejs
org.dozer.Mapper
w formie właściwości komponen-
tu JavaBean. Niech właściwość będzie miała nazwę map-
per. Następnie, w plikach XML deiniujących kontekst apli-
kacji, określamy, że właściwość
mapper
powinna być usta-
wiona na egzemplarz klasy
org.dozer.DozerBeanMapper
(który deiniujemy jako element bean). Teraz w serwisie mo-
żemy korzystać z dobrodziejstw biblioteki Dozer, wykorzy-
stując zmienną
mapper
.
import java.util.*;
import org.dozer.*;
public
class
DozerExample
{
public
static
void
main
(
String
...
args
)
{
DozerBeanMapper
mapper
=
(
DozerBeanMapper
)
DozerBeanMapperSingletonWrapper
.
getInstance
()
;
mapper
.
setMappingFiles
(
new
ArrayList
<
String
>
())
;
JabberPerson
source
=
new
JabberPerson
(
"Jan"
,
"Kowalski"
,
40
,
new
JabberAddress
(
"Wojska Poskiego"
,
"3"
))
;
PidginPerson
destination
=
mapper
.
map
(
source
,
PidginPerson
.
class
)
;
System
.
out
.
println
(
"JabberPerson-
>PidginPerson: "
+
destination
)
;
Plugin do Eclipse'a
W ramach projektu Dozer rozwijana jest również wtyczka
do Eclipse'a, usprawniająca edycję plików koniguracyjnych.
Możliwości, jakie oferuje wtyczka:
• podpowiadanie nazw klas i pól w trakcie pisania;
• walidacja nazw klas i pól;
• graiczne przedstawienie treści zawartej w plikach koni-
guracyjnych.
}
}
www.sdjournal.org
7
BIBLIOTEKA MIESIĄCA
Wynikiem działania naszego programu jest:
Listing 2.
Źródłowe DTO (plik JabberPerson.java)
JabberPerson->PidginPerson: PidginPerson@c8769b[name=<nu
ll>,lastName=Kowalski,age=40,address
=PidginAddress@dc9766[road=<null>,nu
mber=3]]
import org.apache.commons.lang.builder.ToStringBui
lder;
public
class
JabberPerson
{
private
String
irstName
;
private
String
lastName
;
private
int
age
;
private
JabberAddress
address
;
Zatrzymajmy się w tym miejscu na moment, aby dokład-
nie przeanalizować mapowanie zrealizowane przez Do-
zer'a. Porównując klasy z Listingu 2 i 4, widzimy, że róż-
nią się one jedynie polami
irstName
i
name
. Natomiast
klasy Listingu 3 i 5 różnią się jedynie polami
street
i
ro-
ad
. Spójrzmy raz jeszcze na wynik działania naszego
programu. Wszystkie pola zostały poprawnie przema-
powane, oprócz pól
name
i
road
. Tak więc, Dozer auto-
matycznie dopasowuje pola o tej samej nazwie. Jest to
domyślne zachowanie przy braku jakiejkolwiek konigu-
racji. Dodatkowo mapowanie przeprowadzone za po-
mocą Dozer'a ma charakter rekurencyjny.
Dozer domyślnie stara się wywoływać metody o na-
zewnictwie ustalonym przez Java Beans przy pobiera-
niu bądź zapisywaniu wartości. Biblioteka domyślnie
zawiera konwersję pomiędzy podstawowymi typami ję-
zyka Java (prymitywy, obiekty opakowujące, String, Da-
te itd.) oraz obsługę kolekcji.
public
JabberPerson
()
{}
public
JabberPerson
(
String
irstName
,
String
lastName
,
int
age
,
JabberAddress
address
)
{
this
.
irstName
=
irstName
;
this
.
lastName
=
lastName
;
this
.
age
=
age
;
this
.
address
=
address
;
}
/* Implementacja metod typu get i set zostala
pominieta*/
Pliki konfiguracyjne
Naprawmy niedociągnięcia z przykładu pierwsze-
go. Zmuśmy Dozer'a do poprawnego mapowania pól
name
i
road
. Głównym założeniem Dozer'a jest zapisy-
wanie relacji pomiędzy klasami w plikach konfiguracyj-
nych, które są zwykłymi plikami XML. Dodajmy do na-
szego projektu plik konfiguracyjny znajdujący się na Li-
stingu 6. Pokrótce omówmy jego strukturę. Relacja po-
między dwoma klasami definiowana jest za pomocą ta-
ga
mapping
(wszystkie tagi
mapping
muszą znajdować
się w tagu
mappings
). Każdy tag
mapping
musi posia-
dać tagi
class-a
i
class-b
definiujące odpowiednio kla-
sę źródłową i docelową. Dodatkowo tag
mapping
mo-
że zawierać dowolną ilość tagów
field
, reprezentują-
cych zależności pomiędzy polami obu tych klas. Z ko-
lei tag
field
musi posiadać tagi
a
i
b
odpowiednio defi-
niujące pole z klasy źródłowej i pole z klasy docelowej.
Przykładowy fragment zmusza Dozer'a do mapowania
pola
firstName
z klasy
JabberPerson
na pole
name
z kla-
sy
PidginPerson
:
public
String
toString
()
{
return
ToStringBuilder
.
relectionToString
(
this
)
;
}
}
Listing 3.
Źródłowe DTO (plik JabberAddress.java)
import org.apache.commons.lang.builder.ToStringBui
lder;
public
class
JabberAddress
{
private
String
street
;
private
String
number
;
public
JabberAddress
()
{}
public
JabberAddress
(
String
street
,
String
number
)
{
this
.
street
=
street
;
this
.
number
=
number
;
}
<mapping>
<class-b>JabberPerson</class-b>
<class-a>PidginPerson</class-a>
<ield>
<a>irstName</a>
<b>name</b>
</ield>
</mapping>
/* Implementacja metod typu get i set zostala
pominieta*/
public
String
toString
()
{
return
ToStringBuilder
.
relectionToString
(
this
)
;
}
}
8
6/2010
Biblioteka Dozer
Listing 4.
Docelowe DTO (plik PidginPerson.java)
import org.apache.commons.lang.builder.ToStringBuilder;
public
class
PidginPerson
{
private
String
name
;
private
String
lastName
;
private
int
age
;
private
PidginAddress
address
;
/* Implementacja metod typu get i set zostala pominieta*/
public
String
toString
()
{
return
ToStringBuilder
.
relectionToString
(
this
)
;
}
}
Listing 5.
Docelowe DTO (plik PidginAddress.java)
import org.apache.commons.lang.builder.ToStringBuilder;
public
class
PidginAddress
{
private
String
road
;
private
String
number
;
/* Implementacja metod typu get i set zostala pominieta*/
public
String
toString
()
{
return
ToStringBuilder
.
relectionToString
(
this
)
;
}
}
Listing 6.
Podstawowy przykład pliku kon�guracyjnego (plik simple-example-mappings.xml)
<
mappings xmlns=
"http://dozer.sourceforge.net"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:
schemaLocation=
"http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/
beanmapping.xsd"
>
<
mapping
>
<
class-a
>
JabberPerson
<
/class-a
>
<
class-b
>
PidginPerson
<
/class-b
>
<
ield
>
<
a
>
irstName
<
/a
>
<
b
>
name
<
/b
>
<
/ield
>
<
/mapping
>
<
mapping
>
<
class-a
>
JabberAddress
<
/class-a
>
<
class-b
>
PidginAddress
<
/class-b
>
<
ield
>
<
a
>
street
<
/a
>
<
b
>
road
<
/b
>
<
/ield
>
<
/mapping
>
<
/mappings
>
www.sdjournal.org
9
BIBLIOTEKA MIESIĄCA
Wykorzystajmy nowo stworzony plik koniguracyjny
w naszym programie. W tym celu rozszerzmy dotych-
czasową implementację klasy
DozerExample
. Na po-
czątek zmodyikujemy instrukcję odpowiedzialną za
ustawianie listy plików koniguracyjnych. Należy za-
mienić starą instrukcję:
<ield-exclude>
<a>status</a>
<b>status</b>
</ield-exclude>
Atrybut type
Domyślnie definiowane mapowania mają charakter
dwukierunkowy. Jednak jesteśmy w stanie wymusić,
aby dana definicja mapowania była tylko jednokierun-
kowa. Realizuje się to poprzez dodanie atrybutu
type
o wartości
one-way
do taga
mapping
:
mapper.setMappingFiles(new ArrayList<String>());
na nową:
mapper.setMappingFiles(Arrays.asList("simple-example-
mappings.xml"));
<mapping type="one-way">
Podczas ładowania pliku koniguracyjnego, Dozer
zweryikuje, czy użyte tam nazwy klas lub pól faktycz-
nie istnieją w rzeczywistości. Pozostaje jeszcze dodać
mapowanie odwrotne. Dokonamy tego, dodając poniż-
szą instrukcję na końcu metody
main
:
Atrybut wildcard
Standardowo Dozer próbuje automatycznie dopasować
do siebie pola, po ich nazwie. Oczywiście mamy moż-
liwość wyłączenia takiego zachowania. Realizujemy to
poprzez dodanie atrybutu
wildcard
o wartości
false
do
taga
mapping
:
System.out.println("PidginPerson->JabberPerson:
" + mapper.map(destination,
JabberPerson.class));
<mapping wildcard="false">
Zagnieżdżone odwołania
Dozer oferuje możliwość odwoływania się do zagnież-
dżonych pól. Dla przykładu posiadamy obiekt, który za-
wiera pole
status
. Natomiast nasz drugi obiekt posia-
da pole
description
, jednak znajduje się ono głęboko
w hierarchii:
Po uruchomieniu tak zmodyikowanego programu, na
wyjściu pojawia się następujący komunikat:
JabberPerson->PidginPerson: PidginPerson@96ad7c[name=J
an,lastName=Kowalski,age=40,addres
s=PidginAddress@9ea173[road=Wojska
Poskiego,number=3]]
PidginPerson->JabberPerson: JabberPerson@c9b196[irstName
=Jan,lastName=Kowalski,age=40,addres
s=JabberAddress@a9f87c[street=Wojska
Poskiego,number=3]]
<ield>
<a>status.description</a>
<b>status</b>
</ield>
Atrybut copy-by-reference
Dozer obsługuje kopiowanie pól poprzez referencje.
Nie są wykonywane wtedy żadne konwersje. Umoż-
liwia to zmniejszenie zapotrzebowania na pamięć,
a także zwiększa wydajność. Aby włączyć kopiowanie
pola poprzez referencje, należy dodać atrybut
copy-by-
reference
o wartości
true
do taga
field
:
Pola
name
i
road
zostały przemapowane w sposób po-
prawny. Jakby tego było mało, pokazaliśmy, że wszyst-
kie deiniowane zależności pomiędzy klasami mają
charakter dwukierunkowy (chyba że skonigurujemy in-
aczej, o czym później).
Zaawansowana konfiguracja
Zobaczmy, jakie możliwości do tej pory ukrywały przed
nami pliki konfiguracyjne. Pokrótce przedstawię wybra-
ne sposoby sterowania mapowaniem. Oczywiście nie
jest to kompletna lista. Szczegółowe informacje moż-
na znaleźć na stronie domowej biblioteki (patrz: ram-
ka
W sieci
).
<ield copy-by-reference="true">
<a>ield1</a>
<b>ield2</b>
</ield>
Atrybut map-id
Mamy możliwość stworzenia wielu mapowań pomiędzy
tymi samymi klasami, nadając każdemu nazwę profi-
lu. Nazwę profilu definiujemy poprzez dodanie atrybu-
tu
map-id
(gdzie wartość atrybutu to nazwa profilu) do
taga
mapping
:
Tag field-exclude
Za pomocą taga
field-exclude
możemy wykluczyć
z mapowania konkretne pola. Dla przykładu, poniższy
fragment wyklucza mapowanie pola
status
:
10
6/2010
Plik z chomika:
Kapy97
Inne pliki z tego folderu:
2010.02_Biblioteka cocos2d-iphone_[Biblioteka Miesiaca].pdf
(1297 KB)
2006.06_Anti-Grain Geometry C++ i grafika 2D o wysokiej dokładności_[Biblioteka Miesiaca].pdf
(848 KB)
2006.04_Boost.MPL metaprogramowanie_[Biblioteka Miesiaca].pdf
(438 KB)
2006.12_Biblioteka Boost PropertyTree_[Biblioteka Miesiaca].pdf
(471 KB)
2006.10_JFreeReport – darmowe raporty_[Biblioteka Miesiaca].pdf
(599 KB)
Inne foldery tego chomika:
Algorytmy
Antyhaking
Aplikacje Biznesowe
Aspekty
Bazy Danych
Zgłoś jeśli
naruszono regulamin