Enterprise Applikationen entwickeln

Inhaltsverzeichnis

1 Enterprise Anwendungen mit Enterprise Java Beans (EJB)

1.1 Merkmale verteilter Systeme

1.2 Typische Struktur einer (einfachen) EJB-Anwendung

1.2.1 Server-Seite

  1. Business-Klassen:

    Hier wird in Form von Business-Methoden die gesamte Geschäftslogik implementiert (d.h. definiert);
    dabei muss die Klasse das bzw. die zugeordnete(n) Business-Interface(s) implementieren

  2. Business-Interfaces:

    enthalten (grundsätzlich) "nur" Beschreibungen, d.h. Deklarationen, von Business-Methoden;
    hier werden ALLE Business-Methoden, die von einem Client aus aufrufbar sein sollen, zusammengestellt;

    dadurch wird dieses Interface tatsächlich zur "Aufruf-Schnittstelle" der Business-Klasse;
    es gibt 2 Arten:

    1. Remote-Business-Interfaces:

      für "externe (Remote-) Clients, die in einer ANDEREN VM laufen

    2. Local-Business-Interfaces:

      für "lokale" Clients, die in der SELBEN VM laufen;
      dabei kann es sich um ein Servlet (Web-Client) oder eine andere EJB handeln

1.2.2 Client-Seite (zunächst für Remote-Clients)

  1. Client-Klasse:

    eine einfache Java-Klasse mit einer main- Methode; von hier aus werden DIE Business-Methoden, die im Remote-Business-Interface genannt sind, aufgerufen;
    die Besonderheit liegt hier im Aufruf/Starten einer Methode in einer anderen VM;
    die Methodenaufrufe müssen hierbei vorbereitet werden (z.B. Zusammenstellen der Argumente) und das Ergebnis der Methode muss verarbeitet werden (z.B. "nur" ausgegeben werden)

  2. Remote-Business-Interface:

    DAS SELBE Interface, das auch Server-seitig vorhanden sein muss;
    wird (genau genommen) nur zur Übersetzungszeit benötigt;
    insbesondere kann dann (formal) über eine Referenzvariable vom Typ dieses Interfaces jede darin deklarierte Business-Methode aufgerufen werden

1.3 "Fahrplan" für die "ordentliche" Entwicklung einer EJB – Anwendung

1.3.1 Vorbereitung der Projekte in NetBeans

1. Schritt: Anlegen des Enterprise Application Projektes (auch EAR-Projekt genannt - Enterprise Archive)

...über Kategorie JavaEE...
zuständig für die Server-Seite
Auch eine Art "Master-Projekt"
Hinweis: hier muss entschieden werden, OB neben dem "obligatorischen" EJB-Sub-Projekt auch noch ein WAR-Sub-Projekt (für lokale Web-Clients) entstehen soll
beispielhaft festgelegten Bezeichnungen:
  • Demo: für das EAR-Projekt
  • Demo-ejb: für das automatisch generierte EJB-Sub-Projekt
Schritt1

2. Schritt: Anlegen des Enterprise Application Client Projektes

...über Kategorie JavaEE...
beispielhaft festgelegte Bezeichnung: Demo-client
zuständig für die Remote-Client-Seite
WICHTIG: hier muss das richtige EAR-Projekt zugeordnet werden! (hier: Demo )
Hinweis: hier kann auch automatisch ein Package und eine "Main-Class" generiert werden,
z.B.: democlient.Main
Schritt2

3. Schritt: Anlegen des Class Library Projektes

...über Kategorie Java...
beispielhaft festgelegte Bezeichnung: Demo-classlib
zuständig für das, was auf BEIDEN Seiten "benötigt" wird, also speziell das Remote-Business-Interface
Schritt3

4. Schritt: Verbinden des Enterprise Application Client Projektes mit dem Class Libary Projekt

...über Kontext-Menü des Enterprise Application Client Projektes...
Schritt4

1.3.2 Implementieren der Komponenten:

Schritt 1: Anlegen einer „"Business-Klasse" (d.h. einer EJB) als eine sog. Session Bean

über Kontext-Menü des EJB-Sub-Projektes New Session Bean
Name der "Business-Klasse": Hallo
Package angeben: demoserver
Art der Session Bean festlegen: Stateless
Mögliche Business Interfaces festlegen: Remote in Project: Demo-classlib
anschließend die benötigten "Business-Methoden"(innerhalb dieser Klasse) anlegen.
jede Methode über "Insert Code..."aufnehmen!.
Name der Business-Methode festlegen: sagWas
und Ergebnistyp: String
und Parameter:...über Add ... Name: name
Datentyp: String
und festlegen in welche(s) Business-Interface(s)
eine Deklaration der Business-Methode aufgenommen werden soll
Remote

Hinweis: in der automatisch generierten @Stateless -Annotation noch ein Attribut ergänzen (um für den JNDI (Java Naming and Directory Interface) -Dienst des Servers einen "Anfasser" für die Session Bean zuzuordnen)
@Stateless(mappedName = "HalloBean")

Schritt 2: Anlegen bzw. Implementieren der main() -Methode des Remote-Clients

public static void main(String[] args)
{
    InitialContext ic = new InitialContext(); //(a)
    HalloRemote hr = (HalloRemote) ic.lookup("HalloBean"); //(b)
    System.out.println(hr.sagWas("Fritz")); //(c)
}

Anmerkungen:

  1. über das hier erzeugte Objekt wird eine Verbindung zum JNDI-Dienst des Servers hergestellt;
    da diese Klasse nicht im Standard-Package enthalten ist, ist ein entsprechender Import notwendig:
    import javax.naming.InitialContext;
  2. über dieses InitalContext -Objekt wird der JNDI-Dienst um die Adresse der gewünschten Session Bean Instanz gebeten;
    dabei könnte eine NamingException geworfen werden (auch nötig: import javax.naming.NamingException ), für die die catch-or-throw -Regel zu beachten ist!

    da die lookup-Methode die gewünschte Adresse als eine vom Typ Object abliefert, muss explizit in den verwendbaren Typ des Remote-Business-Interface gecastet werden

  3. über die Referenzvariable (vom Typ des Remote-Business-Interface) kann jetzt die gewünschte Methode aufgerufen werden

1.4 Nutzbare (weitere) Methoden in der Implementierung von Business-Methoden

Beispiel-Szenario:
die sagWas() -Methode der Hallo -Session Bean soll ein etwas "dynamischeres" Ergebnis liefern:
Guten Morgen Fritz, heute ist Montag
Guten Tag Dienstag
Guten Abend Sonntag
Möglichkeiten
  1. vorbereitete Methoden aus "Standard"-Klassen (z.B. String)
  2. andere Business-Methoden DERSELBEN Session Bean
  3. "helfende" Methoden innerhalb der Session Bean:
    solche Methoden tauchen NICHT in einem Business-Interface auf, sind also von "außerhalb" NICHT erreichbar!
    z.B.: 1. Teil des Ergebnisses der sagWas() -Methode soll so realisiert werden
    WICHTIG: die Methode NICHT über "Insert Code ..." einbauen, sondern "normal" implementieren
    private String tageszeit()
    {
        Date d = new Date();
        int stunde = d.getHours();
        if (stunde < 11 )
            return "Guten Morgen ";
        if (stunde < 17)
            return "Guten Tag ";
        return "Guten Abend ";
    }
  4. Business-Methoden einer ANDEREN Session Bean
    Voraussetzung: in der ANDEREN Session Bean taucht die Deklaration der gewünschte Methode (mindestens) im Local-Business-Interface auf
    z.B.: der abschließende Teil des Ergebnisses der sagWas() -Methode soll so realisiert werden
    Umsetzung:
    1. eine weitere Session Bean DatumAuswerten, insbesondere mit einem Local-Business-Interface anlegen;
      darin die gewünschten Methoden definieren:
      @Override
      public String wochentag()
      {
          Date d = new Date();
          return String.format("%tA", d);
      }
    2. in der Hallo-Bean (d.h. der rufenden Bean) eine Referenzvariable vom Typ DatumAuswertenLocal aufnehmen "lassen": über "Insert Code..." "Call Enterprise Bean...
      Ergebnis:
      @EJB
      private DatumAuswertenLocal datumAuswerten;
    3. über diese Referenzvariable in der sagWas()-Methode die gewünschte Methode aufrufen
      ...datumAuswerten.wochentag()...
    Auswertung: d.h.: ein lokaler Business-Methoden-Aufruf ist viel einfacher zu handhaben, als ein "entfernter"!
    anstelle eines "expliziten Lookups" wird nur ein "impliziter" Lookup" benötigt (gesteuert über die @EJB Annotation)

1.5 Besonderheiten für Parameter- und Ergebnis-Datentypen von Business-Methoden

1.5.1 Lokale Methodenaufrufe

1.5.2 Remote Methodenaufrufe

1.5.3 Vergleich zwischen Remote und lokalen Aufrufen von Business-Methoden

Remote Lokal
Deklaration: im Remote-Business-Interface ... Local-Business-Interface
"Aufrufer": läuft in einer ANDEREN VM ... DERSELBEN VM
Art der Datenübertragung: "serialisiert" ("call-by-value") ... NICHT "serialisiert" ("call-by-reference")
Verbindungsaufbau: über einen EXPLIZITEN Lookup
InitialContext ... lookup()-Methode
... IMPLIZITER Lookup
@EJB
Referenz vom Typ des Local-Business-Interface

1.6 Benutzerdefinierte Exceptions in Business-Methoden

1.6.1 Hintergrund

1.6.2 Umsetzung

(1) "Server-Seite"

(2) "Client-Seite

1.6.3 Hinweis

insbesondere bei Remote-Clients müssen Methoden-"Ergebnisse" serialisierbar sein

ANSTELLE eines konkreten Ergebnisses könnte jetzt ein Exception-Objekt beim Client "ankommen"

d.h. auch Exception-Objekte müssen serialisierbar sein

dies ist AUTOMATISCH der Fall, da die Klasse Throwable das Interface Serializable implementiert!

1.7 Stateless und Stateful Session Beans - Merkmale und Unterschiede

1.7.1 Grundsätzliches

1.7.2 Stateless-Session Beans

1.7.3 Stateful-Session Beans

2 Datenbankzugriffe in Session Beans - Einstieg

2.1 Überblick

JDBCEJB-Diagramm

2.1.1 Beispiele

Beispiel 1

Beispiel 2

2.2 Vereinfachter Umgang mit DB-Connections

2.2.1 Idee

2.2.2 Umsetzung

  1. Verbindung zwischen Netbeans und der DB herstellen:
    • AUßERHALB eines Projektes im Register Services
    • Knoten: Databases Kontextmenü: New connection...

      addDB1
    • bei Driver: Oracle Thin auswählen und über add den Speicherort und Name der DB-Treiberdatei zuordnen

      addDB2
    • abschließend Verbindungsdaten eingeben:
      • Host: 10.140.130.10
      • Port: 1521
      • SID: CDTORA
      • Benutzer und Kennwort wie gewohnt...
      addDB3
  2. Datasource und Connection-Pool definieren:
    • im Kontextmenü des EAR-Projektes (z.B. DBBeispieleNeu ): New Others
      Categorie: Glassfish FileTypes: JDBCResource

      addDB4 addDB4a
    • Create New JDBC Connection Pool auswählen ...

      addDB5
    • ... bei JNDI-Name: Name für Datasource festlegen, z.B. jdbc/myOracle addDB5a
    • ... bei Choose Database Connection:
      • bei JDBC Connection Pool Name z.B. myOraclePool eintragen
      • und die Option Extract from existing Connection auswählen mit der in (1) angelegten Verbindung, z.B.:
        jdbc:oracle:thin:@10.140.130.10:1521:CDTORA[user on password]
      addDB6
    • abschließend deployen ...
  3. Verwendung in einer Session Bean:
    1. Verbindung mit der Datasource herstellen
      außerhalb einer Business Methode...
      über "Insert Code" ... "Use Database" ...
      bei "Reference Name" eintragen: z.B. hr
      bei "Server Data Sources" den in (2) vergebenen JNDI-Name (für die DataSource) auswählen: z.B.: jdbc/myOracle
      Ergebnis:
      								@Resource(name = "hr")
      private DataSource hr ;
      							
      (auch eine Art "Dependency Injection" - siehe @EJB ...)
    2. in den Business-Methoden ein Connection-Objekt "abholen":
      ...
      Connection con = null;
      ...
      con = hr.getConnection();
    3. abschließendes "Zurückgeben" des aus dem Connection-Pool entliehenen Connection-Objekts:
      ...
      con.close();
      ...
      (hier wird die Verbindung zur DB NICHT abgebaut!

2.3 Zwischenstand und Ausblick

Überblickejb
  1. Eine Business-Methode erteilt dem Entity-Manager irgendwelche DB-Aufträge
  2. ... und dieser setzt sie über die DataSource in "normales" SQL um ...
  3. ... der Entity Manager erzeugt sog. Entity-Objekte ...
  4. ... die dann der rufenden Business-Methode als Ergebnis des DB-Auftrags geliefert werden

2.3.1 Grundlegendes zu Arbeiten mit Entities

Begriffe

Entity:
eine(DTO)-Klasse
für jede DB- Tabelle gibt es 1 Entity- Klasse
für jede Tabellen- Zeile gibt es 1 Objekt vom Typ dieser Klasse
für jede Tabellen- Spalte gibt es 1 Attribut (Member-Variable) in der Entity-Klasse
wie üblich enthält die Entity-Klasse (mindestens) einen "typischen" Konstruktor sowie Getter()- und Setter()-Methoden für jedes einzelne Attribut
Entity-Manager:
ein Dienst des Application-Servers
nimmt DB-Aufträge entgegen; diese werden NICHT in "normalen" SQL erteilt, sondern entweder über einfache Methodenaufrufe oder aber über JPQL
er setzt diese Aufträge in "natives" SQL (herstellerspezifisch) um, nimmt eventuelle Ergebnisse der DB entgegen und erzeugt dann Entity-Objekte, die an die rufende Business-Methode abgeliefert werden
d.h. der Entity Manager nimmt unter anderem ein "Objekt-relationales Mapping" vor
JPQL:
Java Persistence Query Language
eine SQL-ähnliche Abfragesprache
in diesem "Dialekt" werden (von einer Business-Methode) Aufträge an den Entity Manager erteilt
dieser setzt sie in "native" SQL um

2.3.2 EINFACHE DB-Zugriffe über den Entity Manager

"einfach" bedeutet:
ohne Verwendung von JPQL durch direkte Methodenaufrufe über den Entity Manager
eine DML Anweisung betrifft maximal 1 Datensatz und auch ein SELECT liefert maximal 1 Datensatz
erreichbar durch Einschränkung durch den Primärschlüssel (für die WHERE Klausel)
Handwerkliches:
Vorbereitungen:
  • Voraussetzung: eine DataSource steht zur Verfügung ... z.B.: jdbc/myOracle
    1. Definieren einer Persistence Unit ("Entity Manager-Cache"):
      • Kontextmenü des EJB-Projektes: New Other Persistence Persistence Unit
      • "Anfasser" übernehmen (oder auch abändern), z.B.: DBBeispieleNeu-ejbPU
      • DataSource auswählen, z.B.: jdbc/myOracle
      Bedeutung: Verbindet eine Sammlung von Entity-Objekten mit der DB
    2. Anlegen "lassen" einer Entity-Klasse
      • Kontextmenü des EJB-Projekts: New Entity Class from Database
      • 1 Tabelle auswählen und dabei die Option "Include related Tables" abwählen
      • Benennung bzw. Zuordnung eines Package, z.B.: dbbeispieleneuentities (Empfehlung ein eigenes Package für die alle Entity-Klassen)
      • die Option "Generate Fields for Unresolved Relationships" aktivieren (damit werden Member-Variablen für Fremdschlüsselspalten mit generiert)
      Ergebnis: Klasse, z.B.: Departments
Arbeiten INNERHALB einer Session Bean:
  • AUßERHALB einer Methode: "Abrufen" einer Entity-Manager-Referenz

    Kontextmenü: Insert Code Use Entity Manager (die ebenfalls automatisch generierte Methode persist() löschen!)

    Ergebnis:

    								@PersistenceContext(unitName = "DBBeispieleNeu-ejbPU")
    private Entity Manager em;
    							
  • anschliessend innerhalb von Business-Methoden die zur Verfügung stehenden Methoden des Entity Managers nutzen...
  • Beispiele (alle enthalten in der Business-Methode alleBeispiele())
    1. Lesen eines Datensatzes aus der Tabelle DEPARTMENTS , z.B. zur DEPARTMENT_ID 80

      über die find() -Methode des Entity Manager, z.B.:

      Departments d = em.find(Departments.class, (short)80);
      
      //der Wert 80 steht für den Primärschlüsselwert...
      //(da das zugehörige Attribut in der Entity-Klasse vom Typ Short ist, Cast nach short
      //Departments.class ist der Klassenname der Tabelle der zugeordneten Entity Klasse

      die Methode liefert null , falls KEIN Datensatz zum Primärschlüsselwert existiert, ansonsten die Adresse eines von ihr erzeugten Entity-Objektes;

      über Getter-Methoden der Entity-Klasse können die "Spaltenwerte" ausgegeben werden...

    2. Einen neuen Datensatz in die Tabelle Departments schreiben, z.B.:
      DEPARTMENT_ID: 85 DEPARTMENT_NAME: Testing

      über die persist() -Methode des Entity Managers, z.B.:

      d = new Departments((short)85, "Testing"); // d.h. Anlegen eines Entity-Objektes
      em.persist(d);
      									

      Hinweis: bei DB-seitigen Problemen mit dem generierten INSERT würde eine Exception worfen...
      um solche zu vermeiden, kann über find()-Methodenaufrufe ggf. gepürft werden, ob Primär- oder Fremdschlüssel-Verletzungen zu erwarten sind

    3. den Datensatz mit der DEPARTMENT_ID 85 aus der Tabelle DEPARTMENTS löschen

      über die remove() -Methode des Entity-Managers ...

      1. Schritt das Entity-Objekt zur zu löschenden Abteilung

      d = em.find(Departments.class, (short)85);

      2. Schritt falls erster Schritt "erfolgreich"...

      if (d != null)
          em.remove(d);
      ...

      Hinweis: auch hier KÖNNTE wegen Fremdschlüsselverletzungen eine Exception geworfen werden

    4. im Datensatz zur DEPARTMENT_ID 190 aus der Tabelle Departments den DEPARTMENT_NAME in >>blabla<< ändern

      "nur" über Setter-Methoden der zugeordneten Entity-Klasse (d.h. es gibt KEINE spezielle Methode des Entity Managers

      1. Schritt: das Entity-Objekt zur zu verändernden Abteilung besorgen

      d = em.find(Departments.class, (short)190);

      2. Schritt: falls der erste Schritt "erfolgreich" ...

      if (d != null)
      d.setDepartmentName("blabla");
      									

2.4 JPQL - Grundlagen

2.4.1 Ziel

2.4.2 Typischer Aufbau von JPQL-"Routinen"

  1. den JPQL-Abfragetext vorbereiten in einem String
    String jpql = "......";
  2. anschließend diesen Abfragetext in ein Query-Objekt setzen (siehe Prepared Statement)
  3. die Query ausführen lassen (siehe executeQuery()-Methode)
    Ergebnis ist dann z.B. eine "Liste" von Entity Referenzen (siehe ResultSet)

1. Beispiel

Lesen aller Datensätze aus der REGIONS -Tabelle

1. Schritt: JPQL-Abfragetext
String jpql = "SELECT r FROM Regions r
			

Besonderheiten:

2. Schritt: Anlegen eines Query-Objektes mit Zuordnung des Abfragetextes
				Query q = em.createQuery(jpql);
			

also: Aufruf der createQuery()-Methode über eine Entity-Manager-Referenz

3. Schritt: Query und Ergebnis "auffangen"
List<Regions> erg = q.getResultsetList();

die getResultList()-Methode ist dann zu verwenden, wenn kein oder 1 oder mehrere "Ergebnisse" zu erwarten sind

2. Beispiel:

Lesen der Datensätze aus der REGIONS -Tabelle, in denn der REGION_NAME mit 'A' beginnt, absteigend sortiert nach REGION_NAME

JPQL-Abfragetext:

SELECT r FROM Regions r WHERE r.regionName LIKE 'A%' "
ORDER BY r.regionName DESC

also: ANSTELLE eines Spaltennamens muss der über den Entity-Klassen-Alias qualifizierte Attributname angegeben werden. (Rest siehe Beispiel 1)

2.4.3 Zusammenhang zwischen den Angaben in der SELECT-Klausel und dem Ergebnistyp

bisher: SELECT alias ... Ergebnis: List mit Elementen vom Typ der Entity-Klasse (keine Projektion)

(1) jetzt: Projektion auf 1 Ergebnis-"Spalte"

(2) jetzt: Projektion auf mehrere Ergebnis-"Spalten" (inkl. "JOIN)

(2a) Alternative Möglichkeit zur Umsetzung einer Projektion

(3) jetzt auch: Verwendung von Aggregatfunktionen und Gruppierung

2.4.4 JPQL-Abfragen mit Parametern

2.4.5 Abfragen mit GENAU 1 "Ergebniszeile"

2.5 Container Managed Relationships (CMR)

2.5.1 Ziel

Umsetzung der relationalen Beziehungen (in der DB) in objektorientiert Beziehungen (in Java)

2.6 2.6 Auswertungen unter Verwendung von CMR-Attributen in Java (d.h. ohne JPQL)

2.6.1 (1) Ausgehend von einem Detail-Entity-Objekt Zugriff auf Master-Daten

2.6.2 (2) Ausgehend von einem Master-Entity-Objekt Zugriff auf Detail-Daten