Informatika gyűjtemény

NézetNyomtat

Java EE nagyongyorstalpaló

A következőkben megnézzük egy olyan, egyszerű Java Enterprise Alkalmazás elkészítését, ami azokat a technológiákat használja, mint az njCMS. Azoknak akik ebből akarják a Java EE-t megérteni: ez a leírás hiányos és szinte biztos, hogy tartalmaz tárgyi tévedéseket.

Előkészületek

Kell NetBeans. Részletek itt.

Hello Enterprise World

Hozz létre egy Enterprise Application-t, default beállításokkal. (Ha szervernek Tomcat-et ajánl, akkor állítsd ét Glassfish V2-re. A Tomcat a Java EE-nek csak a webes rész-API-ját implementálja.)
Amint láthatod 3 project jött létre:
Tutorial
Ez az egész alkalmazás főprojektje, összefogja a következő kettőt.
Tutorial-ejb
Ebben kell megvalósítani az üzleti logikát
Tutorial-war
Ebben kell megvalósítani a webes user interfészt, servletekkel, jsp-vel, stb. Ez olyan mint a tomcat-ből is ismert sima webes projektek, azzal a különbséggel, hogy a Tutorial-ejb szolgáltatásait használja fel.
Az így készülő alkalmazások a háromrétegű architektúrát fogják követni: adatbázis, üzleti logika, user interfész. Az üzleti logika végez minden adatbázis-műveletet, tehát elfedi az adatbázist az user-interfész elől, felel az adatbázisban lévő adatok konzisztenciájáért. A user interfész tehát a felhasználóval és az üzleti logikával lesz közvetlen kapcsolatban, másszóval nem lesz benne például közvetlen adatbázis-manipuláció.

Session beanek definíciója

Első körben még az adatbázissal ne foglalkozzunk, csak nézzünk egy példát a user interfész - üzleti logika (másszóval war-ejb) együttműködésre! Java EE-ben az üzleti logika Java Bean nevű komponensekből áll, amik szolgáltatásokat nyújtanak kifelé. Ezeket használhatja a user interfész. Első java beanünk az Állapotnélküli Session Bean. Ez tulajdonképpen egy java osztály, aminek nincsenek mezői, a metódusai pedig a kívülről elérhető szolgáltatások, azaz az Üzleti metódusok. Valahogy így néz ki:
@Stateless     //ez egy annotáció, ez jelöli, 
               //hogy állapotnélküli session beanről van szó
public class TestBean implements TestLocal {
    public String helloMethod(String name) {
        return "hello "+name+"!";
    }
}
Két megjegszés: az annotációk a Java 1.5-ben jelentek meg. Metaadatokat lehet velük kapcsolani: osztályhoz, osztály-mezőhöz vagy metódushoz. Ez a session bean önmagában még nem életképes, mert nem adtuk meg az interfészét. Ez egy Java interface lesz, és ezen keresztül lehet majd elérni a beanen kívülről is a szolgáltatásait. Így néz ki:
@Local
public interface TestLocal {
    String helloMethod(String name);
}
A @Local annotáció azt jelenti, hogy ezen az interfészen keresztül csak azonos JVM-ben futó programból lehet ezt a Session Bean-t elérni. A lényeg: az njCMS-ben mindig ezt használjuk.

Session beanek használata

Egy session bean-t például egy servletből lehet meghívni, így:
public class Demo1 extends HttpServlet {
    @EJB TestLocal testLocal;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("<html>");
            out.println("<body>");
            out.println(testLocal.helloMethod("Enterprise World"));
            out.println("</body>");
            out.println("</html>");
        } finally { 
            out.close();
        }
    } 
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }

}
Itt a @EJB annotáción van a lényeg, és azon, hogy a session bean interfészére kell hivatkozni, nem pedig az implementációjára. (És a konténer - esetünkben a GlassFish - majd megkeresi ehhez az interfészhez az EJB-ben azt osztályt, amelyik implementálja.)

Entity Beanek

Az Entity Beanek nem szolgáltatást nyújtanak, hanem adatot tárolnak. Ráadásul perzisztens, azaz maradandó módon. Egyszerűbben: egy Entity Bean valójában egy adatbázistábla egy sorának felel meg. Így lehet csinálni egyet:
@Entity
public class Semmi {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    private String name;
    private Long val;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getVal() {
        return val;
    }

    public void setVal(Long val) {
        this.val = val;
    }
}
Ez például a "Semmi" nevű adatbázistáblát írja le. Ennek három oszlopa lesz, Id, Name és Value. Valamilyen egyedi azonosítónak mindig kell lennie, és azt mindig a @Id annotáció jelzi. A "Semmi" nevű adatbázistábla sorai pedig ennek az osztálynak a példányainak felelnek meg. A táblát manipulálni csak az üzleti logikában, azaz az ejb projekt fájljaiban lehet. Ráadásul azon belül is csak bizonyos helyeken. Például session beanekben:
@Local
public interface TestLocal {

    String helloMethod(String name);
    void createSemmi(String name, Long value);
    List<Semmi> allSemmi();
    
}

@Stateless
public class TestBean implements TestLocal {
    @PersistenceContext 
    EntityManager em;
    
    public String helloMethod(String name) {
        return "hello "+name+"!";
    }

    public void createSemmi(String name, Long value) {
        Semmi s = new Semmi();
        s.setName(name);
        s.setVal(value);
        em.persist(s);   //beszúrás az adatbázisba
    }

    public List<Semmi> allSemmi() {
        return em.createNamedQuery("allSemmiQuery").getResultList();
    }
}
Ez természetesen két fájl. Az entitásokat egy EntityManager típusú objektum szolgáltatásaival tudjuk manipulálni. Ez ugyan egy tagváltozója az állapotnélküli Session Bean-nek, de az előtte lévő annotáció jelzi, hogy az értékét a konténer fogja beállítani. Paraméterként sose adjuk át, mindig helyileg definiáljuk!
Az allSemmi metódus visszatérési értéke az adatbázisban lévő összes Semmi objektum listája. (Ez kiadható a web-rétegnek, de ekkor a lista elemei lecsatolódnak, vagyis a rajtuk végzett módosítások már nem kerülnek vissza az adatbázisba.) Visszatérve a metódus tartalmához, ahhoz, hogy ez működjön, definiálni kell még az "allSemmiQuery" nevű lekérdezést, amit itt most meghívunk. Ennek a nyelve az SQL-hez hasonló EJB-QL és a definíció módja:
@NamedQuery(name="allSemmiQuery", query="SELECT s FROM Semmi s")
Ha még emlékszel, akkor az elején azt mondtam, hogy annotációt csak osztály, metódus, vagy mező elé lehet rakni, másszóval mindig kell valami amit annotál. A NamedQuery-ket entitások elé kell rakni. (De nem számít melyik elé.)
@Emtity
@NamedQuery(name="allSemmiQuery", query="SELECT s FROM Semmi s")
class Semmi {
    ...
Mindez egy servletből egyszerűen meghívható. És ez a példa egyben bemutatja az njCMS-ben (és széleskörűen) használt Servlet-JSP konvenciót:
public class Demo2 extends HttpServlet {
    @EJB TestLocal testLocal;
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String name = request.getParameter("name");
        String value0 = request.getParameter("value");
        if (name != null && value0 != null) {
            Long value = Long.parseLong(value0);
            testLocal.createSemmi(name, value);
        }
        
        List<Semmi> sl = testLocal.allSemmi();
        request.setAttribute("sl", sl);
        request.getRequestDispatcher("/demo2.jsp").forward(request, response);
    } 

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doGet(request, response);
    }
}
Tehát ez a Servlet ha kap megfelelő paramétereket akkor létrehoz egy Semmit, utána pedig minden esetben lekéri a Semmik listáját, majd lefuttatja az utolsó két, misztikus sorát. Ezek valójában csak a kimenő html-t generálják le a meglévő adatok birtokában, néhány out.println() és egy ciklus ugyanezt valósítaná meg. Ebben az esetben viszont egy jsp fájl végzi a html generálását, ami szebb. (JSP-ben körülményes session-beant hívni. És ez azért is jó, mert még egy szinten szétválasztjuk a megjelenítést és az adatfeldolgozást.) A JSP-ben még azt figyeld meg, hogy kerüljük a <% ... %> típusú beszúrt kódokat:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <body>
        <form action="Demo2" method="post">
            name: <input type="text" name="name"><br />
            value: <input type="text" name="value"><br />
            <input type="submit" value="Létrehoz">
        </form>
        <c:forEach items="${sl}" var="semmi">
            name= ${semmi.name}, value= ${semmi.val} <br />
        </c:forEach>
    </body>
</html>
Az sl változó azért érhető el, mert beállítottuk setAttribute-el.