Biblioteka(AndroMDA+JSF+Spring+JPA) -część VI- model dziedziny

Ponieważ moja aplikacyjka nie będzie posiadała zbyt rozbudowanej funkcjonalności, sam model dziedziny również nie będzie zbyt finezyjny :).

Program ma umożliwić rejestrację Wypożyczeń(posiada daty od i do) i Rezerwacji(data rezerwacji) przez Czytelników zarejestrowanych w Bibliotece. Każdy Czytelnik jest Osobą posiadającą swoje imię i nazwisko, oraz numer biblioteczny. Z Czytelnikiem związana jest również lista jego Rezerwacji oraz Wypożyczeń. Rezerwacje i Wypożyczenia dotyczą Książek. Sama Książka posiada swój tytuł, oraz numer katalogowy.Każda Książka mogła zostać napisana przez wielu Autorów (jest to Osoba z imieniem i nazwiskiem, oraz unikalnym identyfikatorem autora).Każdy Autor z kolei mógł napisać wiele Książek.

Myślę że na początek to wystarczy. Pora wykonać model.

Uruchamiam ArgoUML z załadowanym profilem AndroMDA, zakładam pakiet "dziedzina" i przygotowuje projekt. Wygląda on mniej więcej tak:


Dokładna instrukcja, w oparciu o którą tworzyłem obiekty dziedziny zamieszczona jest na stronach dokumentacji cartridge'a EJB3 .

Według mnie na kilka, absolutnie kluczowych zasad należy zwrócić szczególną uwagę.

Przede wszystkim, do naszego projektu powinien zostać załadowany profil AndroMDA dla ArgoUML (o sposobie jego instalowania i osadzania w projekcie pisałem we
wcześniejszym poście
).

Po drugie, wszystkie encje muszą być oznaczone stereotypem "Entity".

Po trzecie, typy dla atrybutów encji muszą pochodzić z załadowanego profilu.

W przypadku ArgoUML, typ atrybutu pochodzący z profilu wygląda tak:


Skoro mój model dziedziny jest już gotowy pora wygenerować kod. W tym celu eksportuje model do formatu xmi (File-Export XMI...). Wygenerowany plik zapisuję jako "biblioteka.xmi" w ścieżce "mda/src/main/uml/biblioteka.xmi".


Teraz tylko uruchomienie pluginu



i następuje przetwarzanie modelu:

INFO  [AndroMDA]   +  registering component 'translation-library'
INFO  [AndroMDA] - core initialization complete: 13.07[s] -
INFO  [AndroMDA] loading model --> 'file:/dane/projekty/sandbox/biblioteka/mda/src/main/uml/biblioteka.xmi'
INFO  [AndroMDA] referenced model --> 'file:/dane/projekty/sandbox/biblioteka/mda/src/main/uml/andromda-profile-32-noextensions.xmi'
INFO  [AndroMDA] - loading complete: 6.005[s] -
INFO  [AndroMDA] - validating model -

Klasy wynikowe, zgodnie z przygotowaną przeze mnie konfiguracją pluginu AndroMDA trafiają do modułu "model", do pakietu "dziedzina"


Kod dla najbardziej złożonej klasy Czytelnik wygląda następująco:

// license-header java merge-point
//
// Attention: Generated code! Do not modify by hand!
// Generated by: EntityEmbeddable.vsl in andromda-ejb3-cartridge.
//
package dziedzina;

import java.io.Serializable;
import java.util.Set;
import java.util.TreeSet;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;

/**
 * Autogenerated POJO EJB class for Czytelnik containing the
 * bulk of the entity implementation.
 *
 * This is autogenerated by AndroMDA using the EJB3
 * cartridge.
 *
 * DO NOT MODIFY this class.
 *
 * 
 */
@Entity
@DiscriminatorValue("C")
@NamedQuery(name = "Czytelnik.findAll", query = "select czytelnik from Czytelnik AS czytelnik")
public class Czytelnik
    extends Osoba
    implements Serializable
{
    private static final long serialVersionUID = 7232739200991516470L;

    // ----------- Attribute Definitions ------------

    private String numerBiblioteczny;

    // --------- Relationship Definitions -----------

    private Set<Wypozyczenie> listaWypozyczen = new TreeSet<Wypozyczenie>();
    private Set<Rezerwacja> listaRezerwacji = new TreeSet<Rezerwacja>();
    // ---- Manageable Display Attributes (Transient) -----


    // --------------- Constructors -----------------

    /**
     * Default empty constructor
     */
    public Czytelnik() {}

    /**
     * Implementation for the constructor with all POJO attributes except auto incremented identifiers.
     * This method sets all POJO fields defined in this class to the values provided by
     * the parameters.
     *
     * @param imie Value for the imie property
     * @param nazwisko Value for the nazwisko property
     * @param numerBiblioteczny Value for the numerBiblioteczny property
     */
    public Czytelnik(String imie, String nazwisko, String numerBiblioteczny)
    {
        setImie(imie);
        setNazwisko(nazwisko);
        setNumerBiblioteczny(numerBiblioteczny);
    }

    /**
     * Constructor with all POJO attribute values and CMR relations.
     *
     * @param imie Value for the imie property
     * @param nazwisko Value for the nazwisko property
     * @param numerBiblioteczny Value for the numerBiblioteczny property
     * @param listaWypozyczen Value for the listaWypozyczen relation
     * @param listaRezerwacji Value for the listaRezerwacji relation
     */
    public Czytelnik(String imie, String nazwisko, String numerBiblioteczny, Set<Wypozyczenie> listaWypozyczen, Set<Rezerwacja> listaRezerwacji)
    {
        setImie(imie);
        setNazwisko(nazwisko);
        setNumerBiblioteczny(numerBiblioteczny);

        setListaWypozyczen(listaWypozyczen);
        setListaRezerwacji(listaRezerwacji);
    }

    // -------- Attribute Accessors ----------

    /**
     * Get the numerBiblioteczny property.
     * 
     * @return String The value of numerBiblioteczny
     */
    @Column(name = "NUMER_BIBLIOTECZNY", insertable = true, updatable = true)
    public String getNumerBiblioteczny()
    {
        return numerBiblioteczny;
    }

    /**
     * Set the numerBiblioteczny property.
     * @param value the new value
     */
    public void setNumerBiblioteczny(String value)
    {
        this.numerBiblioteczny = value;
    }

    // ------------- Relations ------------------

    /**
     * Get the listaWypozyczen Collection
     *
     * @return Set<Wypozyczenie>
     */
    @OneToMany(mappedBy = "czytelnik")
    public Set<Wypozyczenie> getListaWypozyczen()
    {
        return this.listaWypozyczen;
    }

    /**
     * Set the listaWypozyczen
     *
     * @param listaWypozyczen
     */
    public void setListaWypozyczen (Set<Wypozyczenie> listaWypozyczen)
    {
        this.listaWypozyczen = listaWypozyczen;
    }

    /**
     * Get the listaRezerwacji Collection
     *
     * @return Set<Rezerwacja>
     */
    @OneToMany(mappedBy = "czytelnik")
    public Set<Rezerwacja> getListaRezerwacji()
    {
        return this.listaRezerwacji;
    }

    /**
     * Set the listaRezerwacji
     *
     * @param listaRezerwacji
     */
    public void setListaRezerwacji (Set<Rezerwacja> listaRezerwacji)
    {
        this.listaRezerwacji = listaRezerwacji;
    }

    // -------- Common Methods -----------

    /**
     * Indicates if the argument is of the same type and all values are equal.
     *
     * @param object The target object to compare with
     * @return boolean True if both objects a 'equal'
     */
    public boolean equals(Object object)
    {
        if (this == object)
        {
            return true;
        }
        if (!(object instanceof Czytelnik))
        {
            return false;
        }
        final Czytelnik that = (Czytelnik)object;
        if (this.getId() == null || that.getId() == null || !this.getId().equals(that.getId()))
        {
            return false;
        }
        return true;
    }

    /**
     * Returns a hash code value for the object
     *
     * @return int The hash code value
     */
    public int hashCode()
    {
        int hashCode = super.hashCode();
        hashCode = 29 * hashCode + (getId() == null ? 0 : getId().hashCode());

        return hashCode;
    }

    /**
     * Returns a String representation of the object
     *
     * @return String Textual representation of the object displaying name/value pairs for all attributes
     */
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("Czytelnik(=");
        sb.append(super.toString());
        sb.append("numerBiblioteczny: ");
        sb.append(getNumerBiblioteczny());
        sb.append(")");
        return sb.toString();
    }

}

Żeby dowiedzieć się czy wygenerowany kod jest naprawdę w porządku, przygotuję kilka testów jednostkowych. Tyle że opiszę je już następnym razem....

Biblioteka(AndroMDA+JSF+Spring+JPA) -część V- mapowanie relacji

No i się doczekałem :) konfiguracja skończona. Wreszcie mam środowisko na którym mogę naprawdę się pobawić. Tak jak już pisałem zacznę od modelu dziedziny. Zanim jednak zamodeluję sobie kawałek aplikacji postanowiłem przyjrzeć się jak wygląda kod encji wygenerowany przez framework.Oto krótki przegląd mojego eksperymentu.

Relacje @One-To-One


Asocjacja dwukierunkowa
Na początek spróbuję sprawdzić relację One-To-One zamodelowaną za pomocą relacji asocjacji dwukierunkowej.


Kod po stronie klasy Przedsiebiorca.java
@OneToOne(mappedBy = "przedsiebiorca")
public Adres getAdresDzialalnosciGospodarczej()
{
return this.adresDzialalnosciGospodarczej;
}

Kod po stronie klasy Adres.java
@OneToOne(mappedBy = "adresDzialalnosciGospodarczej")
public Przedsiebiorca getPrzedsiebiorca()
{
return this.przedsiebiorca;
}

No i niestety... od razu problem, bo kod nie jest poprawny. Po obydwu stronach relacji wygenerowana została adnotacja z atrybutem "mappedBy" - framework nie poradził sobie z określeniem właściciela relacji. A więc tak się nie da. Skoro więc nie tak.. to pozostaje pytanie "jak"?
Wskazówkę niesie dokumentacja:

 "To model the owning side of a One-To-One or Many-To-Many bidirectional relationship, you indicate that end (the owning end) of the relationship as an aggregate or composite end. "

A więc wygląda na to, że dwukierunkowa  relacja pomiędzy encjami musi być modelowana przy pomocy agregacji lub kompozycji...Szkoda, bo przecież nie każdy związek pomiędzy obiektami to od razu agregacja lub kompozycja...

Agregacja dwukierunkowa
No to skoro wiem, co mówi teoria, pora spróbować praktyki, jeśli ma być agregacja, to niech będzie agregacja:

Kod po stronie klasy Przedsiebiorca.java
@OneToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "ADRES_DZIALALNOSCI_GOSPODAR_FK")
public Adres getAdresDzialalnosciGospodarczej()
{
return this.adresDzialalnosciGospodarczej;
}

Kod po stronie klasy Adres.java
@OneToOne(mappedBy = "adresDzialalnosciGospodarczej")
public Przedsiebiorca getPrzedsiebiorca()
{
return this.przedsiebiorca;
}

A więc tereaz generata jest w porządku. Właściciel relacji został prawidłowo wyodrębniony. Po jego stronie wygenerowany został klucz obcy - więc wygląda że wszystko jest ok.


Asocjacja jednokierunkowa
Relację skierowaną wypróbuję sobie na podstawie asocjacji.



Kod po stronie klasy Przedsiebiorca.java
@OneToOne(optional = false)
@JoinColumn(name = "ADRES_DZIALALNOSCI_GOSPODAR_FK")
public Adres getAdresDzialalnosciGospodarczej()
{
return this.adresDzialalnosciGospodarczej;
}

Kod po stronie klasy Adres.java
BRAK

A więc w przypadku relacji skierowanych, kod wygenerowany na podstawie asocjacji jest już całkowicie poprawny.

Relacje @One-To-Many



Asocjacja dwukierunkowa



Kod po stronie klasy Przedsiebiorca.java
@OneToMany(mappedBy = "przedsiebiorca")
public Set getAdresy()
{
return this.adresy;
}

Kod po stronie klasy Adres.java
@ManyToOne(optional = false)
@JoinColumn(name = "PRZEDSIEBIORCA_FK")
public Przedsiebiorca getPrzedsiebiorca()
{
return this.przedsiebiorca;
}

Powyższy kod jest poprawny, więc wygląda na to, że relacja @OneToMany może być modelowana przy pomocy relacji asocjacji dwukierunkowej.

Ale zaraz... coś tu jest nie tak... właścicielem relacji w UMLu jest klasa Przedsiebiorca.java, a klucz główny został wygenerowany po stronie Adres.java, dlaczego? A więc krótka wycieczka do specyfikacji "ejb-3_0-fr-spec-persistence" i wszystko jasne...
"The many side of one-to-many / many-to-one bidirectional relationships must be the owning
side, hence the mappedBy element cannot be specified on the ManyToOne annotation." 


Właściciel relacji po stronie JPA jest determinowany krotnością elementów, a nie kierunkiem relacji UML-owej. A więc wszystko jasne...

Agregacja dwukierunkowa




Kod po stronie klasy Przedsiebiorca.java
@OneToMany(mappedBy = "przedsiebiorca")
public Set getAdresy()
{
return this.adresy;
}

Kod po stronie klasy Adres.java
@ManyToOne(optional = false)
@JoinColumn(name = "PRZEDSIEBIORCA_FK")
public Przedsiebiorca getPrzedsiebiorca()
{
return this.przedsiebiorca;
}

Tutaj również wygenerowany kod jest poprawny.

Asocjacja jednokierunkowa



Kod po stronie klasy Przedsiebiorca.java
@OneToMany()
@JoinTable
(
name = "PRZEDSIEBIORCA2ADRESY",
joinColumns = {@JoinColumn(name = "PRZEDSIEBIORCA_ID_FK", referencedColumnName = "ID")},
inverseJoinColumns = {@JoinColumn(name = "ADRESY_ID_FK", referencedColumnName = "ID")}
)
public Set getAdresy()
{
return this.adresy;
}

Kod po stronie klasy Adres.java
Brak

Relacje @Many-To-Many

Znany cytat z dokumentacji AndroMDA mówi, że relacja @ManyToMany powinna być modelowana agregacją dwukierunkową.
 "To model the owning side of a One-To-One or Many-To-Many bidirectional relationship, you indicate that end (the owning end) of the relationship as an aggregate or composite end. "



Agregacja dwukierunkowa




Kod po stronie klasy Firma.java
@ManyToMany()
@JoinTable
(
name = "FIRMY2LOKALIZACJE",
joinColumns = {@JoinColumn(name = "FIRMY_ID_FK", referencedColumnName = "ID")},
inverseJoinColumns = {@JoinColumn(name = "LOKALIZACJE_ID_FK", referencedColumnName = "ID")}
)
public Set getLokalizacje()
{
return this.lokalizacje;
}

Kod po stronie klasy Lokalizacja.java
@ManyToMany(mappedBy = "lokalizacje")
public Set getFirmy()
{
return this.firmy;
}

A więc z tą relacją plugin również nie miał problemów. :)

Podsumowanie

Ten krótki przegląd możliwości pluginu w zakresie generowania encji przekonał mnie, że jest on frameworkiem wartym uwagi. Wygenerowany kod źródłowy jest dobrej jakości, a zachowanie pluginu przewidywalne i co rzadko spotykane - dobrze opisane w dokumentacji.
Następnym razem skupię się już na praktycznym zastosowaniu AndroMDA. Opiszę model dziedziny mojej małej aplikacji i sprawdzę czy wygenerowany kod działa na środowisku docelowym (a więc Spring + Tomcat)....

Tymczasem spaaaaać... :)...

Biblioteka(AndroMDA+JSF+Spring+JPA) -część IV- konfiguracja frameworka

Ilość parametrów obsługiwanych przez AndroMDA jest imponująca. Framework jest elastyczny i daje się łatwo dopasowywać do specyfiki projektu. Konfiguracja odbywa się przez parametryzację dwóch plików xml. Nie zamierzam wypisywać wszystkich możliwych do wprowadzenia opcji (tym bardziej, że są one dostępne w oficjalnej dokumentacji). Chciałbym jednak pokrótce wymienić najważniejsze fragmenty konfiguracji i opisać ustawione przeze mnie parametry.

pom.xml (/mda/pom.xml)

Zawartość pliku pom.xml modułu mda opisywałem już jakiś czas temu, dlatego nie będę się na nim skupiał.  Ponieważ na początku mojej przygody z AndroMDA chciałbym ograniczyć się jedynie do wygenerowania persystentnego modelu dziedziny , potrzebny mibędzie tylko jeden cartridge, który posłuży do wygenerowania kodu encji (JPA).

Z tego powodu z sekcji zależności usunąłem prawie całą zawartość, a jedynym wpisem pozostał:

<dependency>
<groupId>org.andromda.cartridges</groupId>
<artifactId>andromda-ejb3-cartridge</artifactId>
<version>${andromda.version}</version>
<scope>runtime</scope>
</dependency>

Dodatkowo, założyłem sobie, że kody wygenerowane przez AndroMDA trafiały będą do modułu "model", dlatego w sekcji właściwości zdefiniowałem sobie stałą wskazującą na umiejscowienie kodów źródłowych tego modułu:

<model.dir>${project.basedir}/../model/src/main/java/</model.dir>

będę się do niej odwoływał, przy konfiguracji cartridge ejb3.

Konfiguracja sposobu przetwarzania (/mda/src/main/config/andromda.xml)

Sercem konfiguracji frameworku AndroMDA jest plik /mda/src/main/config/andromda.xml . Posiada on dosyć czytelną strukturę, która została dobrze opisana w dokumentacji projektu.

1. Sekcja properties
W sekcji tej definiowane są parametry definiujące sposób przetwarzania modelu. Według mnie na szczególną uwagę zasługują dwie flagi:
  • modelValidation - określa czy model jest awalidowany pod kątem zgodności z profilem 
  • outputEncoding - kodowanie plików wynikowych
<properties>
<property name="modelValidation">true</property>
<property name="outputEncoding">true</property>
</properties>

2. Sekcja repositories
W sekcji tej definiowane są źródła, z których zaczytywany jest model, oraz wskazywane są pliki umożliwiające prawidłowe przetwarzanie jego przetwarzanie. Najważniejsze parametry, które ustawić  można w tej sekcji definiowane są w elemencie "model":
  • uri - pozwala na wskazanie ścieżki do pliku modelu
  • moduleSearchLocations - umożliwia wskazanie miejsca, z którego zaczytywane mają być pliki konieczne do przetwarzania modelu (np. pliki profilu)
W moim przypadku sekcja ta wygląda następująco:

<repository name="netBeansMDR">
<models>
<model>
<uri>file:${project.basedir}/src/main/uml/biblioteka.xmi</uri>
<moduleSearchLocations>
<location patterns="**/*.xmi">${project.basedir}/src/main/uml/</location>
</moduleSearchLocations>
</model>
</models>
</repository>
</repositories>


3. Sekcja mappingsSearchLocations
Sekcja ta pozwala na wskazanie lokalizacji, w której znajdują się pliki zawierające mapowania typów umlowych na typy Java.
<mappingsSearchLocations>
<location patterns="*.xml">${conf.dir}/mappings</location>
</mappingsSearchLocations>

4. Sekcja namespaces
Jest to kluczowa sekcja, w której zdefiniowane są aktywne cartridge - czyli biblioteki dokonujące faktycznych przekształceń kodu. Dla każdego z cartridgy możliwe jest podanie całego zatrzęsienia parametrów, które w znaczący sposób dopasowują generatę do indywidualnej specyfiki projektu. W tym miejscu należy zwrócić uwagę na fakt, że każdy użyty cartridge musi zostać wymieniony w /mda/pom.xml w sekcji zależności.
Dobór cartridgy ma decydujący wpływ na formę otrzymanych plików wyjściowych. Np. użycie cartridge "spring" spowoduje wygenerowanie beanów springowych, natomiast cartridge "ejb3" pozwala na wygenerowanie kodów zgodnych z ejb3.

Szczególne znaczenie ma namespace o nazwie "default". Parametry w nim zdefiniowane będą wpływały na każdy inny cartridge.

Na stronie projektu, w sekcjach poświęconych poszczególnym cartridgom możemy odnaleźć dokładną listę parametrów konfiguracyjnych obsługiwanych przez dany cartridge (np dla c. ejb3 strona ta wygląda następująco).

<namespaces>
<namespace name="default">
<properties>
<property name="enableTemplating">true</property>
<property name="enableAnnotations">true</property>
<property name="typeSafeEnumsEnabled">true</property>
<property name="languageMappingsUri">JavaMappings</property>
<property name="wrapperMappingsUri">JavaWrapper</property>
<property name="sqlMappingsUri">${sql.mappings}</property>
<property name="jdbcMappingsUri">JDBC</property>
<property name="maxSqlNameLength">30</property>
<property name="foreignKeySuffix">_FK</property>
<property name="ejbJndiNamePrefix">${application.id}-${project.version}</property>
<property name="enumerationLiteralNameMask">upperunderscore</property>
<property name="persistenceContainerName">jboss</property>
<property name="pluralizeAssociationEndNames">false</property>
<property name="pluralizeAttributeNames">false</property>
<property name="pluralizeParameterNames">false</property>
</properties>
</namespace>

<namespace name="ejb3">
<properties>
<property name="entity-beans">${model.dir}</property>
</properties>
</namespace>
</namespaces>

W powyższej konfiguracji chciałbym zwrócić uwagę na wpis dotyczący cartridgea ejb3
<property name="entity-beans">${model.dir}</property>
Definiuje on miejsce do którego trafiały będą wytworzone artefakty. Stała ${model.dir} zdefiniowana została w /mda/pom.xml .

Biblioteka(AndroMDA+JSF+Spring+JPA) -część III - baza danych

Przed rozpoczęciem pracy nad tworzeniem mojej małej aplikacji muszę zainstalować sobie bazę, w której będe przechowywał dane, oraz wygodne narzędzie, które pozwoli mi na bieżąco śledzić
zmiany w tabelach.

1. Instalacja HSQLDB
Jako bazki danych użyję HSQLDB w wersji 1.8.0. Uruchamiam ją w trybie serwera standalone.

mw@mw:/dane/java/hsqldb-1.8.0/hsqldb/bin> java -cp ../lib/hsqldb.jar org.hsqldb.Server

Po wyświetleniu komunikatów startowych baza wstaje i oczekuje na połączenia.

[Server@156ee8e]: [Thread[main,5,main]]: checkRunning(false) entered
[Server@156ee8e]: [Thread[main,5,main]]: checkRunning(false) exited
[Server@156ee8e]: Startup sequence initiated from main() method
[Server@156ee8e]: Loaded properties from [C:\sandbox\programy\hsqldb_1_8_0_10\hsqldb\bin\server.properties]
[Server@156ee8e]: Initiating startup sequence...
[Server@156ee8e]: Server socket opened successfully in 9 ms.
[Server@156ee8e]: Database [index=0, id=0, db=file:mydb, alias=xdb] opened sucessfully in 273 ms.
[Server@156ee8e]: Startup sequence completed in 291 ms.
[Server@156ee8e]: 2011-09-20 16:03:56.868 HSQLDB server 1.8.0 is online
[Server@156ee8e]: To close normally, connect and execute SHUTDOWN SQL
[Server@156ee8e]: From command line, use [Ctrl]+[C] to abort abruptly

2. Instalacja klienta SQL
Do podglądania zawartości tabel użyję prostego klienta SQL SquirrelSQL (http://www.squirrelsql.org/)


3. Instalacja driverów JDBC i definicja połączenia z serwerem
Żeby podłączyć się do serwera DB, konieczne jest wgranie driverów JDBC. W przypadku HSQLDB należy odnaleźć plik hsqldb.jar (HSQLDB_HOME/lib) i przegrać go do katalogu SQUIRREL_HOME/lib. Po restarcie klienta, drivery automatycznie stają się dostępne i można zdefiniować połączenie do serwera.



Przy definicji połączenia kluczowe jest podanie właściwej wartości Connection String, w moim przypadku ma on postać:
"jdbc:hsqldb:hsql://localhost/xdb"


Teraz tylko należy połączyć się z serwerem i możemy oglądać sobie zawartość naszych tabel.

Definicja springowego DataSource

Teraz muszę zadbać o to by mój projekt korzystał z przygotowanego przeze mnie połączenia do bazy. Muszę odnaleźć definicję springowego DataSource.
Nie jest to trudne, po chwili przeszukiwania trafiam na WEB-INF/config/data-access-config.xml.
Tam odnalazłem definicję źródła danych i ustawiłem właściwy url.
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost/xdb"/>
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
Ta zmiana spowoduje, że po inicjalizacji kontekstu springowego, źródłem danych mojej aplikacji będzie przygotowana wcześniej baza danych.