EJB i JPA


Witam!

Na wstępie zaznaczę, iż jest to mój pierwszy wpis typu “tutorial” na internetowym blogu. Proszę o wszelką wyrozumiałość oraz krytyczne komentarze w celu podniesieniu poziomu kolejnych:). Po tej krótkiej, ciekawej i jakże interesującej przedmowie przejdźmy do meritum sprawy, a mianowicie EJB i JPA. Mateusz Mrozowski na swoim blogu (http://tech.mrozewski.pl) opublikował szereg ćwiczeń związanych z EJB dla początkujących. Początkowo korzystał on z Elcipse i JBoss AS. Część poświęconą JPA przeniósł jednak (dla urozmaicenia:P) na NetBeans i Glassfish. Moja wewnętrzna niechęć do owych środowisk okazała się wystarczającym impulsem do powstania tego mini-artykułu. A więc do dzieła!

Naszym celem jest stworzenie trzech projektów:

  1. projektu JPA zawierającego mapowanie tabel relacyjnej bazy danych na encje.
  2. projektu EJB udostępniającego podstawowe operacje na danych. Coś na wzór Data Access Objects.
  3. projektu aplikacji klienckiej.

Jako docelową bazą danych posłużę się Oracle 10g XE (10.2). Dlaczego?! Ci co mnie znają pewnie wiedzą, i niech tak pozostanie. Oficjalnie zachęcam wszystkich do korzystania z baz Oracle, gdyż nie są trudne w instalacji, jeszcze darmowe (!) oraz szeroko stosowane w projektach komercyjnych. W projekcie JPA zmapuję tylko fragment jednej tabelki przykładowego schematu HR dalszą zabawę z JPA pozostawiając czytelnikowi.

Projekt JPA

Z Eclipse’owego menu wybieramy File -> New -> JPA Project, wpisujemy nazwę Tutorial 3 i klikamy Finish. Następnie tworzymy klasę encji edu.lantoniak.ejb3.jpa.Job oraz wklejamy kod znajdujący się na listingu zamieszczonym poniżej.

@Entity
@Table(name="JOBS")
public class Job implements Serializable {
    @Id
    @Column(name="JOB_ID")
    private String jobId;

    @Column(name="JOB_TITLE")
    private String jobTitle;

    /* Getters & setters */
    ...
}

Pamiętać należy o przeciążeniu metod equals() oraz hashCode() (zgodnie z kontraktem znanym z przygotowań do SCJP). Prezentowany przykład mapowania jest dziecinnie prosty i nikomu znającemu język angielski nie trzeba go zapewne objaśniać.

Ostatni krok niezbędny do ukończenia projektu JPA, polega na edycji pliku persistence.xml i wyspecyfikowania źródła danych, z którego korzysta aplikacja. Plik ten powinien mieć postać podobną do tej z drugiego listingu.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="Tutorial 3">
        <jta-data-source>java:/OracleDS</jta-data-source>
    </persistence-unit>
</persistence>

Dzięki tagowi <jta-data-source> deskryptor połączenia do bazy danych pobrany zostanie z zasobów serwera aplikacyjnego. Domyślna konfiguracja JBoss nie posiada zainstalowanego sterownika JDBC Oracle (link). Plik ojdbc14.jar umieścić należy w katalogu $JBOSS_HOME/server/default/lib/. Następnie w katalogu $JBOSS_HOME/server/default/deploy/ tworzymy plik oracle-ds.xml, którego przykładową zawartość prezentuje kolejny listing. Plik ten definiuje między innymi connection string, adres bazy danych, użytkownika, hasło oraz wielkość dostępnej puli połączeń.

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>OracleDS</jndi-name>
        <connection-url>jdbc:oracle:thin:@localhost:1521:XE</connection-url>
        <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
        <user-name>hr</user-name>
        <password>hr</password>
        <min-pool-size>1</min-pool-size>
        <max-pool-size>5</max-pool-size>
        <exception-sorter-class-name>
            org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter
        </exception-sorter-class-name>
        <metadata>
            <type-mapping>Oracle9i</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

Otwieramy w przeglądarce internetowej konsolę administracyjną JBoss – http://localhost:8080/admin-console/ (domyślnie admin/admin). Uwaga: ważne, aby najpierw uruchomić serwer aplikacyjny, a dopiero potem bazę danych Oracle. Występuje konflikt domyślnych portów i konsola administracyjna nie zbinduje się na port 8080. Tak to działa przynajmniej u mnie.. Nie wierzysz – sprawdź, a jeśli się mylę umieść komentarz. Tak czy owak, po zalogowaniu się, powinniśmy widzieć w sekcji Datasources źródło danych OracleDS ze statusem up.

Projekt EJB

Przypomnę, iż projekt ten pełni funkcję warstwy DAO – udostępnia operacje wyszukiwania oraz DML wyższym warstwom aplikacji. Tworzymy nowy projekt EJB o nazwie Tutorial 3 EJB. We właściwościach projektu dodajemy mozolnie skonfigurowany uprzednio moduł JPA (Tutorial 3) do Java Build Path. Następnie tworzymy nowe bezstanowe ziarno EJB (patrz listing 4).

@Stateless
public class JobDAO implements JobDAORemote, JobDAOLocal {
    @PersistenceContext
    private EntityManager em;

    public JobDAO() {
    }

    @Interceptors(DAOInterceptor.class)
    public void add(Job job) {
        em.persist(job);
    }
}

Do bean’a JobDAO, za pomocą adnotacji @PersistenceContext, wstrzykujemy EntityManager. Obiekt ten umożliwia podstawowe operacje na encjach. Jeśli chodzi o szerszy opis klasy PersistenceContext, to odsyłam do fachowej literatury, z której dowiesz się znacznie więcej niż z tego prostego tutorialu. W prezentowanym powyżej kodzie, niespodziankę stanowi interceptor DAOInterceptor. Interceptor jest to klasa umożliwiająca wykonanie pewnych operacji przed oraz po wykonaniu danej metody (w tym przypadku add()), lub wszystkich metod danej klasy. Kod interceptora przedstawia się następująco:

public class DAOInterceptor {
    @AroundInvoke
    public Object profile(InvocationContext invocation) throws Exception {
        if ("add".equals(invocation.getMethod().getName())) {
            if (invocation.getParameters()[0] instanceof Job) {
                Job job = (Job) invocation.getParameters()[0];
                job.setJobTitle(job.getJobTitle() + " ADDED");
            }
        }
        return invocation.proceed();
    }
}

Obiekt InvocationContext enkapsuluje wszelkie dobrodziejstwa i niebezpieczeństwa jakie niesie za sobą mechanizm refleksji. W tym przypadku do każdego nowo tworzonego tytułu stanowiska, doklejany sufiks ” ADDED”. Oczywiście, zaprezentowany interceptor nie ma żadnego sensu biznesowego. Bardziej użyteczny przykład wykorzystania interceptora dotyczy wypełniania kolumn audytowych i opisany został w artykule “Using a Hibernate Interceptor To Set Audit Trail Properties”.

Projekt aplikacji klienckiej
Prosty projekt umożliwiający dostęp do wcześniej powstałego obiektu DAO tworzymy przez opcje File -> New -> Project -> Application Client Project. Mateusz Mrozowski opisuje na swoim blogu procedurę dostępu do ziaren EJB spoza kontenera. Dla porządku umieszczam jednak kod aplikacji klienckiej:

public class Main {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        props.put(Context.PROVIDER_URL, "localhost:1099");
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        try {
            Context ctx = new InitialContext(props);
            JobDAORemote jobDAO = (JobDAORemote) ctx.lookup("JobDAO/remote");
            Job job = new Job();
            job.setJobId("LUK_DYR");
            job.setJobTitle("Dyrektor");
            jobDAO.add(job);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
}

Źródła projektów: Tutorial 3 All.zip.

One Response to EJB i JPA

  1. ktoslepszy says:

    Proste, jasne i pomocne, dzięki;)

Leave a reply to ktoslepszy Cancel reply