Java für ABAP-Entwickler - EDV

Werbung
6
Anwendungsschichten
Klassische Anwendungen sowohl aus der ABAP- als auch aus
der Java-/J2EE-Welt lassen sich mit einem dreischichtigen
Modell beschreiben. So enthält jede Applikation gewisse
Anteile an Datenhandling, Businesslogik und Präsentationslogik. In diesem Kapitel betrachten wir alle Schichten für beide
Seiten.
6.1
Beschaffungslogik und Persistenz
Die Schicht der Datenbeschaffungslogik und Persistenz hat die Aufgabe,
der nachfolgenden Businesslogik, die mit Daten umgehen und diese prozessieren muss, dieselben zur Verfügung zu stellen. Ziel dabei ist es, mit
der Beschaffungslogik gegenüber der Businesslogik eine gewisse Abstraktion gegenüber den unzähligen Ressourcen, auf die zugegriffen werden
kann – Datenbanken, Dateien, Remote-Aufrufe und Services –, zu etablieren und den Zugriff im Optimalfall vollständig zu standardisieren, im
Regelfall aber zumindest stark zu vereinfachen und die technischen
Details, die gegenüber der Businesslogik nicht relevant sind, auszublenden.
Die folgenden Unterkapitel stellen die Möglichkeiten der beiden Plattformen vor, mit verschiedenartigsten Ressourcen umzugehen.
6.1.1
ABAP
Open SQL
SQL (Structured Query Language) ist eine strukturierte Abfragesprache
für relationale Datenbanken. Sie steht für nahezu jedes RDBMS (Relationales Datenbank-Management-System) zur Verfügung, allerdings in herstellerabhängigen Varianten. Die SQL-Standards von ANSI (American
National Standards Institute) und ISO (International Standards Organization) dienen meist nur als Richtlinien, an die sich die Datenbankhersteller
mehr oder weniger halten.
SQL erlaubt nicht nur die Abfrage von Daten aus der Datenbank, sondern
auch die Änderung von Tabelleninhalten, Modifikation von Strukturen,
Einrichtung von Benutzerberechtigungen und Einstellungen für die Systemsicherheit. Dabei untergliedert man SQL in DML (Data Manipulation
Language) zum Lesen und Ändern von Daten, DDL (Data Definition Lan-
Anwendungsschichten
283
guage) zum Anlegen und Verwalten von Tabellen auf der Datenbank und
DCL (Data Control Language) für Berechtigungsprüfungen und zur Überprüfung der Konsistenz.
Eine Teilmenge der SQL-Anweisungen, genannt Open SQL, ist in allen
Datenbanken bekannter Software-Häuser implementiert und steht in
ABAP vollständig zur Verfügung. Dies ermöglicht einen einheitlichen
Zugriff auf alle von SAP unterstützten Datenbanken und damit eine weitgehende Unabhängigkeit der ABAP-Entwicklungen von Datenbankprodukten, sofern man sich auf Open SQL beschränkt.
Native SQL
Open SQL enthält allerdings lediglich DML-Befehle, die bereits ausführlich in Abschnitt 4.1.4 erläutert wurden. Für den Fall, dass diese für eine
spezielle Anforderung nicht ausreichend sein sollten, ist es in ABAP auch
möglich, datenbankspezifische Kommandos abzusetzen. Dazu wird die
Native-SQL-Anweisung zwischen die ABAP-Befehle EXEC SQL und ENDEXEC gestellt:
EXEC SQL.
CREATE TABLE BUILD_COMP (
CLIENT CHAR(3) NOT NULL,
BUILD CHAR(9) NOT NULL,
COMP1 CHAR(6) NOT NULL,
COMP2 CHAR(6) NOT NULL,
PRIMARY KEY (CLIENT, BUILD)
)
ENDEXEC.
Listing 6.1 Beispiel einer in ABAP eingebetteten Native-SQL-Anweisung
Die so in ABAP eingebetteten Befehle werden direkt an das Datenbanksytem durchgereicht. So erlaubt Native SQL die Nutzung der gesamten
Funktionalität, die die datenbankseitige Schnittstelle bietet.
Auf ABAP-Seite enthält jeder Workprozess auf einem Applikationsserver
eine Datenbankschnittstelle mit einer herstellerabhängigen Schicht, über
die die gesamte Information zwischen ABAP-Seite und Datenbank fließt.
Bei Verwendung von Native-SQL-Befehlen in ABAP-Programmen kann
bei einem Wechsel auf eine andere Datenbank ein erheblicher Mehraufwand entstehen, da sich die Kommandos der Datenbanken im Allgemeinen unterscheiden und die entsprechenden Codezeilen gefunden und
angepasst werden müssen. Ohnehin sollten in Anwendungsprogrammen
keine DDL-Operationen ausgeführt werden; das Anlegen und Verwalten
284
Anwendungsschichten
von Tabellen sollte im ABAP Dictionary ausgeführt werden. Außerdem
führt das SAP-System für datenbankspezifische Befehle keine weiteren
Prüfungen durch. Aus diesen Gründen sollte Native SQL in ABAP so weit
wie möglich vermieden werden.
Logische Datenbanken
Bei einer logischen Datenbank handelt es sich um nichts anderes als ein
ABAP-Programm. Jedoch sind logische Datenbanken spezielle Programme, die einem Anwendungsprogramm Daten zur Verarbeitung zur
Verfügung stellen können. Die häufigste Verwendung logischer Datenbanken ist das Lesen von Daten aus Datenbanktabellen und die Verknüpfung mit einem ausführbaren Programm. Darüber hinaus können logische
Datenbanken über den Funktionsbaustein LDB_PROCESS aufgerufen
werden. Dies ermöglicht es, mehrere logische Datenbanken innerhalb
eines ausführbaren Programms aufzurufen. Diese können dann entsprechend komplex geschachtelt werden.
Mit der logischen Datenbank werden die Datenbankzugriffe durch OpenSQL-Zugriffe außerhalb des Anwendungsprogramms realisiert. Die logische Datenbank liest aus der Datenbank zeilenweise und stellt sie dem
ausführbaren Programm zur Laufzeit zur Verfügung.
Logische Datenbanken sind hierarchisch organisiert, da viele Tabellen
über Fremdschlüssel in Beziehung stehen.
Folgende Aufgaben können von logischen Datenbanken übernommen
werden:
Aufgaben
왘 Sie können in mehreren ausführbaren Programmen genutzt werden.
왘 Einheitliches Selektionsbild für alle Programme, die eine logische
Datenbank nutzen.
왘 Die Berechtigungsprüfung ist zentral in der logischen Datenbank abge-
legt.
왘 Änderungen zur Verbesserung der Performance greifen in allen
Anwendungsprogrammen, die die logische DB nutzen.
Grundsätzlich kann man die logische Datenbank in drei Objekte unterteilen: Die Strukturdefinition legt die Datensicht der logischen Datenbank
fest, die Selektion definiert die Benutzeroberfläche des ausführbaren Programms und im Datenbankprogramm erfolgen schließlich die Anweisungen für das Lesen der Daten und die Übergabe an die Aufrufer der logischen Datenbank. Der Aufruf hat folgende Struktur:
Beschaffungslogik und Persistenz
Bestandteile
285
GET <Tabellenkopf>.
...
GET <Tabellenposition>.
...
Listing 6.2 Prinzipieller Aufruf in einem ausführbaren Programm
Persistente Objekte
Die persistenten Objekte gehören zu den Object Services und stellen
Anwendungen verschiedene zentrale Dienste zur Verfügung, die nicht
direkt durch ABAP-Objects-Sprachelemente dargestellt werden. Derzeit
werden von SAP zwei solcher Objektdienste zur Verfügung gestellt, die
Persistenzdienste und die Transaktionsdienste. Im Rahmen dieses
Abschnitts wird der Persistenzdienst kurz dargestellt.
Ein Persistenzdienst unterstützt den ABAP-Entwickler bei der objektorientierten Arbeit mit Daten in relationalen Datenbanken.
Transiente und
persistente Daten
Grundsätzlich können Daten in zwei verschiedene Kategorien unterschieden werden: in transiente und in persistente Daten. Vereinfacht kann
man sagen, dass transiente Daten nur während der Laufzeit eines Programms, dagegen aber persistente Daten dauerhaft, z.B. auf der Datenbank, existieren. Darüber hinaus können persistente Daten auch als
Inhalte auf der Applikationsebene oder auf der Präsentationsebene vorkommen. In objektorientierten Programmen werden Daten in der Regel
als Attribute von Objekten dargestellt. Selbstverständlich werden in
Methoden lokale Daten definiert und benutzt, diese werden jedoch hier
nicht betrachtet. Ein Objekt in der objektorientierten Programmierung
lebt nur zur Laufzeit eines Programms, zwischen der Erzeugung und der
Löschung eines Modus des Programms. Um in Objekten mit persistenten
Daten zu arbeiten, müssen innerhalb der Methoden der Klasse Zugriffe
auf die Ablage programmiert werden.
Der Sinn persistenter Objekte liegt deshalb darin, die Daten eines
Objekts transparent für den Entwickler in der Datenbank abzuspeichern
und sie während der Initialisierung des Objekts wieder zu beschaffen,
damit ein Programm mit den gleichen Objekten weiterarbeiten kann, die
ein anderes Programm in einem bestimmten Zustand hinterlassen hat.
Die Aufgabe eines Persistenzdienstes besteht daher darin, die Möglichkeit zur Verfügung zu stellen, die Attribute eines Objekts persistent zu
speichern und auf die richtige Klasse abzubilden.
286
Anwendungsschichten
Um den Persistenzdienst für Objekte zu nutzen, müssen deren Klassen als
so genannte persistente Klassen im Class Builder definiert werden. Diese
Objekte und der Zustand des Objekts werden durch den Persistenzdienst
verwaltet. Die Objekte einer solchen Klasse werden in einem ABAP-Programm nicht über die Anweisung CREATE OBJECT erzeugt, sondern mit
einer Methode des Persistenzdienstes. Diese sorgt auch für die richtige
Initialisierung. Die persistenten Klassen können neben der eindeutigen
Identität auch Schlüsselattribute zur eindeutigen Identifizierung des
Objekts enthalten. Der Persistenzdienst verwaltet die persistenten
Objekte und sorgt für die Verbindung zwischen Objekt und Datenbank.
Tutorium: Persistente Klasse
In diesem Tutorium wird dargestellt, wie eine einfache persistente Klasse
für eine Datenbanktabelle angelegt wird. Das Ziel besteht darin, dem Entwickler die Feinheiten bei der Entwicklung persistenter Klassen vor
Augen zu führen, um einen Vergleich mit dem später ausführlich erläuterten Java-Äquivalent – JDO – durchführen zu können.
Zielsetzung
Sie sollten sich mit den Grundlagen der ABAP Workbench auskennen und
darüber hinaus auch mit ABAP Objects in Berührung gekommen sein.
Voraussetzungen
1. Im Class Builder SE24 oder im Object Builder SE80 wird die persistente
Klasse angelegt.
Ablauf
2. Geben Sie den Namen für die persistente Klasse mit ZCL_<dbtab>_
PERSISTENT an.
3. In den Eigenschaften für die Klasse wählen Sie unbedingt den Klassentyp Persistente Klasse aus.
4. Die angelegte Klasse implementiert die Methoden des Interfaces IF_
OS_STATE, die den Zustand des Objekts verwalten.
5. Daneben werden nun automatisch noch weitere Klassen zu der neuen
persistenten Klasse generiert – die Klassen ZCB_<dbtab>_PERSISTENT
und ZCA_<dbtab>_PERSISTENT.
6. Über die Persistenzabbildung wird der Klasse ZCL_<dbtab>_PERSISTENT die Datenbanktabelle <dbtab> zugeordnet. Die Persistenzabbildung erreicht man über das Menü Springen • Persistenzabbildung.
7. Hier kann das Mapping für die Datenbanktabelle DBTAB erfolgen.
8. Sichern und aktivieren Sie die persistente Klasse.
9. Das folgende Coding soll einen Eindruck vermitteln, wie eine solche
persistente Klasse innerhalb eines Kontextes – z.B. eines Reports – verwendet wird. Über die Referenzvariable agent wird eine Referenz auf
Beschaffungslogik und Persistenz
287
die persistente Klasse ZCL_<dbtab>_Persistent zugewiesen. Mit der
Methode GET_PERSISTENT wird geprüft, ob ein Eintrag in der Datenbank existiert. Existiert kein Eintrag, wird eine Ausnahme ausgelöst.
Innerhalb dieses CATCH-Blocks wird dann versucht, ein Objekt anzulegen. Erst nachdem das COMMIT WORK abgestetzt wurde, existiert auf
der Datenbank ein entsprechender Eintrag. Wird der Commit Work
nicht abgesetzt, besteht das erzeugte Objekt nur während der Laufzeit.
DATA: connection TYPE REF TO zcl_<dbtab>_persistent,
agent
TYPE REF TO zca_<dbtab>_persistent.
Agent = zca_<dbtab>_persistent=>agent.
TRY.
Connection = agent->get_persistent(
i_key1 = wa_<dbtab>-key1
...
i_keyn = wa_<dbtab>-keyn ).
CATCH cx_os_object_not_found.
TRY.
agent->create_persistent(
i_key1 = wa_<dbtab>-key1
...
i_field1 = wa_<dbtab>-field1
... ).
CATCH cx_os_object_not_found.
...
ENDTRY.
ENDTRY.
6.1.2
Java
Um nun auf der anderen Seite die komplexen Architekturdetails der Java
Personality der Persistenzschicht des SAP Web Application Servers zu
analysieren, sind zunächst noch einmal die Zusammenhänge der Datenhaltung innerhalb des SAP-Kontextes zu betrachten. Innerhalb der reinen
ABAP-Welt, auf die Sie sich bisher konzentriert haben, werden alle Daten
in einer mehr oder weniger zentralen Datenbank gespeichert. So setzte
ein ABAP-Programm über einen der eben vorgestellen Mechanismen
normalerweise direkt auf das zugrunde liegende Datenbanksystem auf.
Warum sollte sich dies so nicht auch einfach in Java realisieren lassen? So
würde man annehmen, dass man neue Tabellen im ABAP Dictionary
anlegt, auf die man von irgendeiner Stelle im Java-Code zugreift.
288
Anwendungsschichten
Während diese Vorgehensweise auf den ersten Blick sehr einfach und
auch schlüssig erscheint, birgt sie doch einige Nachteile: Der herausragendste ist die Tatsache, dass dieser Ansatz so nicht den J2EE-Standards
entspricht, denn er würde die Existenz einer ABAP-Instanz voraussetzen,
was für eine reine J2EE-Umgebung – die sich auch außerhalb des SAPUmfelds großer Beliebtheit im Rahmen von Enterprise-Projekten erfreut –
offensichtlich sehr unwahrscheinlich ist. Weiterhin würde das Zusammenlegen von ABAP- und Java-Tabellen dazu führen, dass Java-Entwickler
den ABAP-Konventionen folgen müssten, beispielsweise bezüglich Sperrverwaltung oder Update-Aufträgen, die Datenkonsistenz sicherstellen.
Nichtsdestotrotz bringt eine zentrale Datenbank der Java-Identität des
Web AS enorme Vorteile. So setzt die zentrale Instanz des Web AS auf
einer zentralen Datenbankinstanz auf, die sich ähnlich zu der korrespondierenden ABAP-Instanz verhält – Customizing- und Konfigurationsdaten
werden zusätzlich zu den Anwendungstabellen gespeichert. Um dies zu
ermöglichen, beinhalten die Design-Ziele der SAP folgende Maßgaben:
Design-Ziele
왘 Strikte Trennung von ABAP- und Java-Persistenz
Beide Personalities haben ihr eigenes abgegrenztes Datenbankschema,
ausgeprägt durch zwei logisch – oder sogar physisch – getrennte
Datenbanken. Keine Transaktion kann sich direkt über beide Schemata
erstrecken, wobei sicherlich beispielsweise eine Java-Anwendung auf
ABAP-Daten zugreifen kann, allerdings nicht auf Datenbankebene –
sprich: Tabellenzugriffe zwischen ABAP- und Java-Stack sind nicht
möglich. Dies erfolgt auf der Ebene der Geschäftslogik bzw. der diese
kapselnden Middleware, beispielsweise über RFC mittels Java Connector (JCo). Die Kollaboration muss an dieser Stelle also auf Komponentenebene erfolgen.
왘 Minimierung von Datenbankadministrations-Aufwendungen
Um trotz der notwendigen Trennung beider Datenbankschemata die
Aufwendungen für Installation und Administrierung möglichst gering
zu halten, ist es möglich, beide Schemata innerhalb einer einzigen
Datenbank zu realisieren. Das bedeutet, eine ABAP-Transaktion greift
auf das ABAP-Schema zu, eine Java-Transaktion auf das korrespondierende Java-Schema, jedoch in ein und derselben physikalischen Datenbank.
왘 Erweiterung der Java-Persistenz-Technologien
Altbekannte Fähigkeiten und Konzepte aus der ABAP-Welt, beispielsweise das Cachen von Statements und Tabellenpufferunterstützung,
wurden in die Java-Welt übertragen.
Beschaffungslogik und Persistenz
289
Ein anderer Aspekt, der die Architektur der Persistenzschicht auf der JavaSeite maßgeblich beeinflusst hat, ist die Objektorientierung der Sprache
Java. Während herkömmliche SAP-Anwendungen in der Regel noch auf
relationaler Persistenz und prozeduralem Code basieren – wodurch sich
Geschäftsdaten relativ einfach in Tabellen abbilden lassen –, ist man in
Java eher gezwungen, in Objekten zu denken. Aus diesem Grund bietet
SAP beide Möglichkeiten der Datenzugriffsarten an, die grundsätzlich
voneinander zu unterscheiden sind: relationale und objekt-basierte
Datenhaltung. Entsprechend unterschiedlich ist auch der Umgang für den
Entwickler mit den Daten.
Open SQL for Java
Framework für
einheitliche
Datenzugriffsebene
Ebenso wie Open SQL innerhalb einer ABAP-Umgebung einen einheitlichen Zugriff auf Datenbanken ermöglicht, stellt Open SQL for Java eine
einheitliche Datenzugriffsebene für Java-Anwendungen zur Verfügung.
Diese Schicht stellt zum einen leistungssteigernde Mechansismen wie
Tabellenpuffer und Statement Pooling zur Verfügung und ermöglicht
zugleich einen übertragbaren Zugriff auf verschiedenste Datenbanken
wie Oracle, IBM DB2, Microsoft SQL Server, MaxDB und weitere.
Anwendungen müssen auf diese Art nicht angepasst werden, denn das
SQL Subset SQLJ gleicht die Unterschiede zwischen den Datenbanken
aus. So können Anwendungen ohne Änderungen auf verschiedensten
Datenbanken laufen.
Alle Programmiermodelle, die SAP für die unterstützten Datenbanken
anbietet, sind fester Bestandteil dieses Open SQL for Java Frameworks.
Der Anwendungsentwickler hat die Option, über verschiedene Wege auf
die Daten der Persistenzschicht zuzugreifen. Sämtliche Zugriffsmöglichkeiten innerhalb von Open SQL for Java basieren auf der untersten
Instanz – auf dem bereits in Abschnitt 5.2 eingeführten JDCB API. Auf
dieser Programmierschnittstelle setzt SAP nun verschiedene Abstraktionsschichten auf, die unabhängig voneinander in einer Anwendung koexistieren und genutzt werden können und auf jeweils einer eigenen Hierarchieebene verschiedene Funktionalitäten anbieten bzw. gewisse Vor- und
Nachteile implizieren.
JDBC
Wie man Abbildung 6.1 entnehmen kann, repräsentiert JDBC das niedrigste Abstraktionsniveau – sämtliche höheren Schichten generieren letzten Endes JDBC-Aufrufe, die vom herstellerspezifischen JDBC-Datenbanktreiber im Sinne des Java JDBC API als SQL-Statements prozessiert
290
Anwendungsschichten
und direkt an die Datenbank übermittelt werden. JDBC ist sehr populär,
allein schon aufgrund der Fülle von Beispielcodes, die allgemein zugänglich sind.
Relational Persistence (SQL)
SQLJ
JDBC (J2EE)
Object Relational Persistence
EJB CMP (J2EE)
JDO
»open«
Open SQL Engine
Table Buffer
SQL
Processor
DB
Access
Layer
Table Catalog
»native«
Statement Cache
SQL Trace
»vendor«
Connection Pool
Vendor-specific JDBC Driver
Database
Abbildung 6.1 Open SQL Framework – Datenbank-Zugriffsebenen
Allerdings garantiert die Verwendung von nativem JDBC noch keine
Datenbankunabhängigkeit. Es ist von der JDBC-Treiberimplementierung
und der Semantik der letztlich zugrunde liegenden Datenbank abhängig,
wie die JDBC-Aufrufe ausgeführt werden. Wenn für die Implementierung
von Datenbankzugriffen natives SQL bzw. JDBC explizit verwendet werden, ist festzuhalten, dass das JDBC-API keinerlei Framework für die
Überprüfung und Validierung von SQL-Anweisungen beinhaltet. Der Entwickler erhält also keine Sicherheit darüber, ob sein Anwendungscode
wirklich auf einer anderen Datenbankplattform problemlos ausgeführt
wird.
Native JDBC
Um diesen möglichen Fragen um Portabilität im Zusammenhang mit
JDBC und SQL vorzubeugen, definiert SAP eine Untermenge (Subset) von
SQL-Anweisungen, die für eine Datenbankunabhängigkeit zumindest
zwischen allen von SAP unterstützten Datenbanken entworfen wurde.
Open SQL for Java ist in diesem Zusammenhang somit als Pendant zum
Beschaffungslogik und Persistenz
291
bekannten Open SQL auf der ABAP-Seite zu sehen und löst auf ähnliche
Art verwandte Probleme, die mit fast jeder Programmiersprache einhergehen.
Den Mittelpunkt des Open SQL for Java Frameworks bildet die Open SQL
Engine, bestehend aus drei Schichten, die aufeinander aufbauend von
unten nach oben mehr und mehr Funktionalitäten anbieten: Die unterste
Schicht bildet der Connection Pool, darauf setzt die Datenbankzugriffsschicht auf, hierauf wiederum die oberste Schicht, nämlich die SQL-Prozessorebene (siehe Abbildung 6.2).
Open SQL
Empfohlen
Verbindlich bei SAP-Projekten
· SQLJ
· Portabilität von SQL/JDBC garantiert
· Java Dictionary
nur für das »default«
· Table Buffer
Datenbankschema
Native SQL
· SQL Trace
· SQL Statement Cache
Empfohlen, wenn das standardmäßige
Datenbankschema nicht genutzt
werden kann, weil Datenbanktabellen
bereits existieren
Anbieter-spezifisches SQL
Toleriert
· Datenbankverbindungspool
· Full JDBC standard API
· Anbieter-spezifische SQL-Statements
Abbildung 6.2 Open SQL Engine – Schichtenmodell
Relationale und
objektrelationale
Persistenz
292
Wie bereits erwähnt, bietet SAP verschiedene Programmiermodelle an,
um auf Daten zuzugreifen: Es lassen sich zum einen relationale und
objektrelationale Persistenz gegeneinander abgrenzen, die sich wiederum
ihrerseits durch verschiedene Ansätze realisieren lassen. Innerhalb des
relationalen Modells werden die Persistenzszenarien SQLJ und JDBC
unterschieden. Für den objektorientierten Persistenzansatz bietet Java die
zwei Möglichkeiten der Realisierung über Enterprise Entity Beans oder
über Java Data Objects. Die einzelnen Programmiermodelle werden später in diesem Kapitel behandelt, zunächst genügt es zu wissen, dass,
abgesehen von SQLJ, das auf der obersten Schicht der Open SQL Engine
aufsetzt, alle anderen Modelle jeweils auf jeder der drei Ebenen innerhalb
des Open-SQL-Schichtenmodells angesetzt werden können, somit ist die
Bindung an die unterste Schicht maßgeblich vorausgesetzt. Entsprechend
untergliedert SAP in ihrer Terminologie diese drei Gruppen, nämlich
»open«-, »native«- und »vendor«-basierte Anknüpfungsmodelle. Alle
drei Ebenen der Open SQL Engine werden im Folgenden beschrieben.
Anwendungsschichten
Connection Pool – Vendor SQL
Als unterste Schicht setzt direkt auf die Schicht des anbieterspezifischen
JDBC-Treibers der Connection Pool auf. Das Aufbauen und Anbieten von
Datenbankverbindungen ist bekanntermaßen aufwändig und aus Sicht
der Systemressourcen somit teuer. Durch den Connection Pool werden
Verbindungen zu gleichen Datenquellen in einem so genannten Pool
gespeichert. Somit lassen sich Verbindungen ohne Zeitverzug aufbauen,
zudem ermöglicht dieser Pool Zugriffe auf das Default-DatenbankSchema, das bereits vorkonfiguriert im Connection Pool vorhanden ist,
und nicht angepasst werden muss.
Connection Pools werden dabei zentral im J2EE-Server seitens der Administration angelegt und danach lediglich über einen logischen und eindeutigen Namen aus einem JNDI-Kontext referenziert. Sensible Daten
wie Authentifikationsdaten oder Belastungsmaxima werden so von den
Entwicklern fern gehalten und verlagern auf diese Weise typisch architektonische Parameter nicht unnötigerweise in die Applikationslogik.
Anwendungen deklarieren Ressourcenreferencen zum Pool als Datenquelle. Sie erhalten die Verbindungen über den Pool und lösen die Verbindungen ebenso über ihn. Der Connection Pool wird sowohl zwischen
verschiedenen Requests als auch zwischen verschiedenen Anwendungen
gemeinsam genutzt. Neben den bereits erwähnten Performanceaspekten
bietet sich hierdurch eine zentrale Stelle sowohl zur Konfiguration als
auch für das Monitoring der Datenbankverbindungen und -zugriffe.
Sie können Abbildung 6.3 entnehmen, dass jeder Anknüpfungsansatz
mindestens auf dieser Schicht aufbaut, womit sämtliche Funktionalitäten
dieses Layers grundsätzlich immer zur Verfügung stehen.
Greifen Anwendungen direkt auf Daten in einer relationalen Datenbank
zu, so kommt immer JDBC zum Einsatz. Durch die Verwendung von Abstraktionsmodellen wie Enterprise Entity Beans oder Java Data Objects
bleibt dies dem Entwickler zwar bis zu einem gewissen Grad verborgen,
doch bereits weiter vorne wurde darauf hingewiesen, dass auch diese
objektorientierten Modelle ebenso auf JDBC zurückgreifen. Gleichwohl
gilt: Setzt man auf dieser untersten Ebene auf, können zwar die proprietären Fähigkeiten einzelner Datenbanken genutzt werden, jedoch unter
Verlust der Open-SQL-Mehrwehrte wie Portabilität, Tabellenpufferung
und SQL Statement Cache, um nur einige zu nennen. Da im Prinzip an
dieser Stelle nun mehr oder weniger direkt auf herstellerspezifischer
Ebene der Datenbank gearbeitet wird, nennt SAP einen solchen Ansatz
folglich »vendor-specific« bzw. »Vendor-SQL« oder auch »Vendor-JDBC«.
Beschaffungslogik und Persistenz
Vendor SQL bzw.
JDBC
293
J2EE
Applikation #1
J2EE
Applikation #2
Verbindungspool
· Konfigurationen
· Monitoring
Gemeinsame Nutzung von
Anfragen und Applikationen
Eine Verbindung aufbauen ist teuer!
DB-Schema
Abbildung 6.3 Connection Pool
SAP gibt die Persistierung auf direkter Grundlage dieser dritten Schicht
lediglich als »toleriert« an, somit sollte auf dieser Ebene grundsätzlich nur
entwickelt werden, wenn Anwendungen nicht ohne jene benötigten proprietären Fähigkeiten auskommen können.
DB Access Layer – Native SQL/JDBC
Als zweite Schicht setzt die Datenbankzugriffsebene auf der Schicht des
Connection Pools auf, der diese – getreu dem Gedanken von Schichtenmodellen – um gewisse Funktionalitäten erweitert. Ebenso wie auf der
herstellerspezifischen Ebene bieten sich dem Entwickler sämtliche Funktionalitäten des zugrunde liegenden proprietären Datenbanksystems,
jedoch ebenso mit Einbußen bei der Portabilität und Tabellenpufferung.
Native SQL
bzw. JDBC
Alle Methodenaufrufe werden dem zugrunde liegenden JDBC-Treiber
direkt und unverändert übermittelt. Die Implementierung des Native
JDBC API entspricht grundsätzlich einem einfachen Wrapper um den herstellerspezifischen JDBC-Treiber, jedoch mit zwei entscheidenden Erweiterungen in Bezug auf Geschwindigkeit und Instandhaltungsaufwand der
J2EE Engine: SQL Trace und Statement Pooling.
SQL Trace
SQL Trace bietet auf Abruf die Möglichkeit, sämtliche SQL-Statements,
die gegen die Datenbank gestellt werden und über Methoden dieser
Ebene oder der darüber liegenden Schicht der Open SQL Engine – die
294
Anwendungsschichten
SQL-Prozessorschicht – ausgeführt werden, zu protokollieren. SQL Trace
kann über den Visual Administrator dynamisch an- bzw. ausgeschaltet
werden. Das Log-Format ist dabei datenbankunabhängig. Die Protokollierungseinträge enthalten neben den eigentlichen SQL-Statements Informationen über den Zeitpunkt der Anfrage, die Abfragedauer, die Eingabeparameter und gegebenenfalls die Abfrageergebnisse sowie
Kontextinformationen.
SQL Trace ist über eine Browseroberfläche innerhalb des SAP Web AS
zugänglich und ist besonders nützlich für Performanceanalysen, denn es
lassen sich mit SQL Trace schnell und einfach Fehlerquellen oder schlechte
Persistenzentwürfe aufdecken, besonders wenn höherschichtige APIs verwendet werden, die eventuell eine zu große Menge von SQL-Statements
erzeugen. So kann SQL Trace auch zur Entwicklungszeit eine große Hilfe
sein, indem Entwickler lernen können, welcher SQL-Code letzten Endes
aus ihren JDOs, JSPs, Servlets und Enterprise Beans erzeugt wird.
Statement Pooling verbessert die Performance zur Laufzeit, indem SQLAnfragen, die häufiger verwendet werden, gecacht werden. Der Zwischenspeicher erlaubt es der Engine, festzustellen, ob eine Anfrage einige
Zeit vorher bereits schon einmal gestellt wurde. Somit kann signifikant
CPU-Zeit eingespart werden, denn häufig benutzte Anfragen müssen nur
einmal vorbereitet werden (Prepare-Phase) und können wiederholt direkt
ausgeführt werden. Dies reduziert die Gesamtanzahl an Parse-Routinen,
die gegen die Datenbank gestellt werden müssen, erheblich.
Statement Pooling
PreparedStatement ps = con.prepareStatement("SELECT *
FROM ZRM_RES_MASTER WHERE RESID = ?”);
ps.setInt(1, 256);
[...]
ResultSet rs = ps.executeQuery();
[...]
ps.close();
Listing 6.3 Lebenszyklus eines SQL-Statements
Beachten Sie, dass dieses Quellcodefragment Teil eines Servlets sein
könnte, das innerhalb der J2EE-Anwendung mehrfach ausgeführt wird,
lediglich mit unterschiedlichen empl_id-Werten. Das daraus resultierende SQL-Statement müsste jedes Mal an die Datenbank übermittelt
und vorbereitet werden, wenn das Servlet ausgeführt würde. Das Vorbereiten des SQL-Statements – was für die Datenbank bedeutet, es zu parsen und den optimalen Ausführungsplan zu erstellen – ist auf den meisten
Beschaffungslogik und Persistenz
295
Systemen ein sehr kostenintensiver Vorgang und erzeugt infolgedessen
langfristig einen Performance-Overhead.
PreparedStatement-Object
Statement Pooling ermöglicht nun der Anwendung das Wiederverwenden eines bereits vorbereiteten Statement-Objekts (PreparedStatement-Object) auf eine ähnliche Weise wie auch Datenbankverbindungen
wieder verwendet werden können, wenn Connection Pooling aktiviert
ist. Die Wiederverwendung ist der Anwendung gegenüber vollständig
transparent. Aus Sicht der Anwendung ist es für die Verwendung eines
PreparedStatement-Objekts gleichgültig, ob dieses am Statement Pooling partizipiert oder eben nicht. Änderungen im Code sind nicht notwendig. Wenn eine Anwendung ein PreparedStatement-Objekt
schließt, kann sie es über die Methode Connection.prepareStatement() wieder verwenden.
SAP Web AS
SQL
Wiederholte (teure!)
SQL-Statements vermeiden
Prepare SQL
JDBC-Statements cachen
SQL parsen
und kompilieren
SQL
Ausführungsplan
}
einmal !!!
Ein Cache pro physische
Datenbankverbindung
SQL ausführen
Result Set
Identifikation des Statements
über seine textuelle
Repräsentation
Datenbank
Abbildung 6.4 Statement Cache
Eine Statement-Pool-Instanz ist mit einer physischen Datenbankverbindung assoziiert und cacht PreparedStatement- und CallableStatement-Objekte, die auf dieser Verbindung erstellt werden. Jedes Mal,
wenn eine prepareStatement()- oder prepareCall()-Methode auf
einer bestimmten Verbindung aufgerufen wird, durchsucht der native
JDBC-Treiber automatisch den assoziierten Statement Pool nach einem
passenden Statement. Dabei sind für den Entwickler folgende Kriterien
relevant:
296
Anwendungsschichten
왘 Der Statement-Text muss mit dem im Cache exakt identisch sein, ins-
besondere Groß- und Kleinschreibung sind hierbei zu beachten (casesensitiv).
왘 Der Aufruf-Typ muss derselbe sein (prepared oder
callable).
왘 Der Scrollable-Type des Result Sets, das aufgrund des Aufrufs erzeugt
wird, muss derselbe sein (forward-only oder scrollable).
Wird ein passendes Statement im Pool gefunden, wird ein neues PreparedStatement-Objekt erzeugt und an den Aufrufer übergeben. Ansonsten wird der Prepare-Aufruf zunächst geparst, um ein neues Objekt zu
erzeugen. Jedes neue dieser Objekte wird gepoolt, wenn die close()Methode auf ihm aufgerufen wird.
SQL-Prozessor – Open SQL/JDBC
Die dritte und zugleich oberste Ebene der Open SQL Engine bildet die
SQL-Prozessorschicht. In dieser wird mit dem Tabellenpuffer ein weiterer
Baustein zur Effizienzsteigerung eingeführt. Ziel ist es, einzelne Teile der
Datenbanktabellen nach erstmaligem Zugriff im Application Server vorzuhalten, um mehrfache Zugriffe auf dieselben Datenbestände innerhalb
der Datenbank zu vermeiden. So werden die Datenbankbelastung und
die Netzwerkkommunikation reduziert. Es existiert jeweils ein Puffer pro
Datenbankschema und Web AS-Instanz, jedoch kann ein Puffer für mehrere Verbindungen gleichzeitig arbeiten.
Das Puffern kann für Tabellen individuell konfiguriert werden, weiterhin
kann die Granularität der Pufferung so definiert werden, dass entweder
nur Teile des Inhalts einer Tabelle oder sogar die gesamte Tabelle zwischengespeichert wird. Das Puffern ist der Anwendung gegenüber transparent gehalten, der erste Pufferzugriff lädt implizit die Daten in den Zwischenspeicher, so dass Folgezugriffe die Daten direkt von ihm erhalten
und nicht bis auf die Datenbank durchgreifen müssen.
Innerhalb des Visual Administrator besteht die Möglichkeit, Statistiken
über die Verwendung des Tabellenpuffers abzurufen. Diese stehen im
Reiter für die Monitoring Services zur Verfügung.
Visual
Administrator
Während der native SQL/JDBC-Ansatz, aufbauend auf der zweiten
Schicht der Open SQL Engine, nur für den Fall gewählt werden sollte,
dass ein Standard-Datenbankschema nicht verwendet werden kann, da
bereits Datentabellen existieren, ist der Open-SQL-Ansatz als generell
erste Wahl anzusehen. Denn nach diesem Modell sind die Rollen der Entwicklungszeit klar strukturiert, es wird das Java Dictionary explizit mit in
den Prozess mit einbezogen.
Beschaffungslogik und Persistenz
297
SAP Web AS
TabellenPuffer
SQL
Result Set
Die selben Daten nicht mehrfach
von der Datenbank lesen
}
einmal !!!
Einen Puffer pro DB-Schema und
Web AS-Instanz (ein Puffer wird für
mehrere Verbindungen verwendet)
SQL ausführen
Datenbank
Abbildung 6.5 Tabellenpuffer
Java Dictionary
Vollständig integriert in das SAP NetWeaver Developer Studio, wird das
Java Dictionary verwendet, um den Lebenszyklus der Datenbankobjekte
zu verwalten, also die Definition, Erstellung und Modifikation von Datenbankobjekten. Dies ist nur möglich, wenn die Entwicklung auf dieser
obersten Schicht der SQL Engine stattfindet. Wie auch auf der ABAPSeite sollten DDL-Operationen nur innerhalb dieses Dictionary ausgeführt werden.
Anfragen und DML-Ausdrücke werden über die Open SQL Engine abgewickelt, die somit die zweite Rolle in diesem Modell einnimmt. Datenbankverbindungen, SQL-Statement-Processing, Tabellenpuffer, Statement Pooling und SQL Trace werden über sie abgewickelt.
Doch neben diesen weiteren Möglichkeiten der Performanceoptimierung
vollendet die SQL-Prozessorebene das Konzept der Open SQL Engine
dahingehend, dass sie durch den SQL-Prozessor selbst Funktionalitäten
anbietet, die die Verwendung eines neuen Programmiermodells innerhalb der Java-Persistenz ermöglicht: Hierzu lässt sich festhalten, dass über
sämtliche bereits vorgestellten Programmiermodelle (JDBC, EJB, JDO),
egal, auf welcher Schicht sie innerhalb der Open SQL Engine basieren,
letztlich über den JDBC-Treiber die endgültigen SQL-Statements generiert
werden.
Ebenso klar ist, dass das Open SQL Framework for Java grundsätzlich sehr
große Ähnlichkeiten mit seinem ABAP-Gegenstück hat, jedoch mit einem
entscheidenden Unterschied: Reine JDBC-Anfragen bzw. resultierende
JDBC-Anfragen werden erst zur Laufzeit syntaktisch analysiert, wodurch
298
Anwendungsschichten
Fehler entsprechend auch erst zur Laufzeit festzustellen sind – nicht
bereits zur Designzeit, was den Entwicklungsprozess schwerfällig macht.
Aus diesem Grund führt SAP nun ein weiteres Abstraktionslevel für Persistenz ein: SQLJ.
SQLJ
SQLJ definiert eine Syntax, um statische SQL-Ausdrücke in Java-Quellcode einzubetten, im Gegensatz zu JDBC, wo SQL-Statements als StringArgumente einer JDBC-Methode übergeben werden.
Da der Java Compiler nicht mit diesen Ausdrücken umgehen kann, werden Quellcode-Dateien mit SQLJ-Elementen mit der Dateiendung *.sqlj
gespeichert. In einem Präprozessor-Schritt werden diese Elemente von
einem SQLJ-Übersetzer des Open-SQL-Prozessors durch Aufrufe auf die
SQLJ-Laufzeitumgebung ersetzt. Erst der daraus resultierende Java-Quelltext ist nun kompilierbar.
SQLJ wurde seinerzeit von Oracle initiiert und es gelang, ein SQLJ-Konsortium zu gründen, dem Oracle, IBM, Microsoft, Sun, Sybase, Tandem
und Informix angehören. Die Referenzimplementierung wurde daraufhin
von Oracle entwickelt und als ISO/IEC-Spezifikation standardisiert
(ISO/IEC 9075–10).
Hieraus leitet SAP nun das im Open SQL for Java Framework verwendete
API ab. In dieser Implementierung der SAP wird die Syntax stets auf Konformität mit der Open-SQL-Grammatik überprüft, wodurch höchste
Datenbankportabilität erlangt wird, da ja, wie bereits in früheren Kapiteln
festgestellt, die Open-SQL-Syntax eine Untermenge der SQL-Syntax ist,
die von allen führenden Datenbankherstellern unterstützt wird. Somit
lassen sich über SQLJ keine datenbankspezifischen SQL-Aufrufe prozessieren.
Die Open-SQL-Grammatik basiert auf Entry Level SQL, spezifiziert durch
ISO/IEC 9075 (Third Edition, 01.11.1992), darüber hinaus werden folgende SQL-Konstrukte unterstützt:
Open-SQLGrammatik
왘 Joined Tables
왘 Dynamic Parameter Specification
Open SQL unterstützt folgende Teilmenge von SQL-Befehlssätzen:
왘 Abfragen
왘 Data Manipulation Language (DML)
Beschaffungslogik und Persistenz
299
Syntax
Die SQLJ-Syntax ist sehr einfach zu lesen: In SQLJ wird SQL-Ausdrücken
die Direktive #sql vorangestellt. Der Präcompiler übergeht den JavaCode und bearbeitet direkt nur den SQL-Code, den er zunächst syntaktisch überprüft. Sind keine Fehler vorhanden, generiert der Übersetzer
Java-Quellcode und wandelt die SQLJ-Ausdrücke in die benötigten
JDBC-Aufrufe um.
Der SQLJ-Übersetzer ist vollständig transparent in das SAP NetWeaver
Developer Studio integriert. Wenn die SQLJ-Quelldateien gespeichert
werden, werden automatisch die korrespondierenden Java-Klassen
erzeugt. Zum Debuggen der Anwendung werden die ursprünglichen
SQLJ-Quelldaten angezeigt und bearbeitet.
Der Vorteil dieses im Gegensatz zu JDBC auf höherem Niveau angelegten
APIs sollen einfachere, kompaktere und robustere Programme sein. Auf
der einen Seite werden natürlich die Programme dahingehend robuster,
dass Syntax, Semantik, Typenvalidität und Portabilität bereits während
der Entwicklungszeit überprüft werden – und nicht erst zur Laufzeit nach
dem Deployen –, jedoch kann nicht pauschal von einer deutlich verringerten Komplexität von SQL-Befehlen und Quellcode gesprochen werden.
Die Testphase wird nicht zwangsläufig weniger zeitaufwändig, denn der
zusätzliche Präcompilierzyklus verlagert diese vor den Deploy-Vorgang.
SAP versucht diesem Manko zu begegnen, indem die Entwicklungsumgebung bereits das Auflösen von SQLJ unterstützt und man das Testen so
schon während der Designzeit fahren kann.
Zudem wird auch der Code nicht automatisch weniger komplex, vielmehr
kann Quellcode nun verwirrend wirken, z.B. dadurch, dass Sprachen und
Syntaxen gemischt werden. Beispielsweise werden die Variablen bei SQLJ
über :varName angesprochen.
Das Modell von SQLJ stellt insbesondere für langjährige ABAP-Entwickler
sicherlich eine Erleichterung dar, ist es doch angelehnt an die Open-SQLEinbettung von SQL in ABAP-Code. Doch da die SQL-Statements hart in
den Java-Quelltext codiert werden und die syntaktische Prüfung keine
dynamisch generierten Statements unterstützt, lassen sich mit SQLJ nur
statische SQL-Funktionalitäten nutzen, im Gegensatz zu Open SQL in
ABAP, wo das dynamische Generieren von SQL-Anweisungen zunehmend – allerdings auch nur bis zu einem gewissen Maße – ermöglicht
wird.
300
Anwendungsschichten
SQLJ-Statements beginnen mit der Direktive #sql und enden mit einem
Semikolon. Zusätzlich zu den in Java reservierten Schlüsselwörtern sind
die Worte iterator, context und with innerhalb von SQLJ-Ausdrücken
reserviert. Wie auch Java-Code selbst, sind auch SQLJ-Statements casesensitiv.
SQLJ-Entwicklung
Der eigentliche SQL-Aufruf ist innerhalb von geschweiften Klammern {
und } enthalten und ist case-insensitiv. Host-Variablen erhalten als Präfix
einen Doppelpunkt (:).
#sql context Ctx with (dataSource = ”jdbc/SYS”);
String var;
#sql [ctx] { Select col into :var FROM tab };
Innerhalb einer SQLJ-Quelldatei können folgende Kommentare verwendet werden:
왘 Java-artige Kommentare (/*
... */ oder //)
왘 SQL-artige Kommentare (/*
... */ oder --)
Die SQL-Kommentare sind nur für die SQL-Teile des Quellcodes zu verwenden, außerhalb des SQL-Fragments müssen Java-Kommentare verwendet werden:
/* #sql context Ctx with (dataSource = "jdbc/SYS");*/
// String var;
#sql [ctx] {
--Select col into :var FROM tab
};
Java-Host-Variablen werden dazu verwendet, um Daten zwischen Java
(der Host-Sprache) und SQL (der eingebetteten Sprache) auszutauschen.
Sie besitzen folgende Syntax:
Host-Variablen
<host expression> ::= (IN | OUT | INOUT)?
':'( <java variable> |
'(' <java expression> ')' ).
Host-Variablen und Expressions können überall in einem eingebetteten
SQL-Statement verwendet werden, wo die Verwendung dynamischer
Parameter durch die Open-SQL-Grammatik zugelassen ist. Um eine JavaVariable als Host-Variable zu benutzen, muss ihr das Präfix : vorangestellt
werden, zudem müssen die Variablennamen innerhalb des Java-Teils
gleichlautend mit den Namen derer im SQLJ-Teil einer Quelldatei sein,
Groß- und Kleinschreibung ist hierbei zu beachten:
Beschaffungslogik und Persistenz
301
String res_id = "1";
#sql [ctx] { DELETE FROM ZRM_RES_MASTER
WHERE RESID = :res_id };
Host-Expressions
Ebenso wie Variablen lassen sich auch komplexe Java-Ausdrücke als Host
Expression in ein SQL-Statement einbetten. Dabei muss ein Host-Ausdruck von :( und ) eingeschlossen werden. Die Host-Ausdrücke werden
dabei von links nach rechts entsprechend ihrem Auftreten innerhalb des
Statements ausgewertet.
Im folgenden Beispielcode werden zwei Java-Ausdrücke in ein SQL-Statement eingebettet. Der Ausdruck ref.getKey() ist ein IN-Parameter, der
Ausdruck values[++i] ein OUT-Parameter. Beide Ausdrücke werden
nach Ausführung des Statements ausgewertet.
String[] values = new String[5];
MyClass ref = new MyClass();
int i = 3;
#sql [ctx] { SELECT col
INTO :(values[++i])
FROM dbtab
WHERE key = :(ref.getKey()) };
Parameter-Modus
Um den Parameter-Modus einer Host-Variablen oder -Expression zu
bestimmen, können diese mit einem optionalen Parameter-Modus-Indikator »IN«, »OUT« oder »INOUT« (aus Sicht der Datenbank) gekennzeichnet werden. Dies hilft lediglich dem einfacheren Verständnis des
Quellcodes, der letztliche Datenfluss wird automatisch erkannt und entsprechend vollzogen. Der IN-Parameter zeigt an, dass Daten von der
Java-Variable in das SQL-Statement übergeben werden, während der
OUT-Parameter anzeigt, dass das Ergebnis des SQL-Statements zurück an
die Java-Anwendung übergeben wird. INOUT definiert hingegen einen
Datenfluss in beide Richtungen. Der nachfolgende Quelltext zeigt dies
beispielhaft:
#sql [ctx] { SELECT col
INTO :OUT var
FROM dbtab
WHERE key = :IN (ref.getKey()) };
Man sollte vorsichtig mit den Host-Expressions umgehen, da sie zu
bestimmten Zeitpunkten ausgewertet werden: OUT-Ausdrücke werden
nach der Ausführung des SQL-Statements, IN-Ausdrücke werden vorher
ausgewertet.
302
Anwendungsschichten
In SQLJ werden Datenbankverbindungen durch einen so genannten Connection Context identifiziert. Dieser spezifiziert die zu verwendende
Datenbank, die Session und Transaktion. Alle SQLJ-Ausdrücke oder DMLStatements müssen einen expliziten Connection Context verwenden. Das
bedeutet, dass diese Ausdrücke einen Bezeichner enthalten müssen, der
ein Connection-Context-Objekt bestimmt, auf dem der Ausdruck ausgeführt wird. Vereinfacht beschrieben, repräsentiert das Conenction-Context-Objet eine Datenbankverbindung.
DatenbankConnectionContext
Der SQLJ-Übersetzer substituiert diese Connection-Context-Deklaration
durch die Deklaration einer bestimmten Java-Connection-ContextKlasse, die das Interface sqlj.runtime.ConnectionContext implementiert. Da die generierte Klasse statische Variablen enthalten wird, darf
ein Connection Context nur als globale oder als statische innere Klasse
deklariert werden.
Die Connection-Context-Klasse repräsentiert im Gegensatz zum Objekt
keine Datenbankverbindung, sondern eine Datenquelle sowie einen logischen Katalog (zur Designzeit), auf den später in diesem Abschnitt eingegangen wird.
Es werden zwei Varianten der Datenquellen-Verbindungskontexte unterschieden: Der URL Connection Context hat Konstruktoren, die es ermöglichen, einen neuen Verbindungskontext aufgrund einer URL zu instanziieren. Der Data Source Connection Context hingegen ermöglicht das
Erstellen eines Objekts aufgrund einer Datenquelle.
Die Deklaration eines Connection Contexts kann eine with-Klausel enthalten, die den Wert für die Datenquelle spezifiziert. Dann handelt es
sich um einen Connection Context mit Datenquelle; solch ein Connection Context ist fest mit der Datenquelle verbunden, die unter dem angegebenen Namen im JNDI-Verzeichnis gefunden werden kann. Der
Defaultkonstruktor erzeugt eine Instanz dieser Kontextklasse, die eine
JDBC-Verbindung zur assoziierten Datenquelle beinhaltet. Ist die withKlausel nicht vorhanden, handelt es sich um einen URL Connection Context. Der folgende Code zeigt einen Connection Context mit Datenquelle:
Connection
Context mit
Datenquelle
#sql context SysCtx
with (dataSource = "java:comp/env/jdbc/MyDB");
[...]
SysCtx sysCtx = new SysCtx();
#sql [sysCtx] { DELETE FROM dbtab WHERE key = 17 };
Beschaffungslogik und Persistenz
303
[...]
sysCtx.close();
SQLJ in der IDE
Der gesamte Entwicklungsprozess der Persistenz einer Web AS-J2EEAnwendung wird über das SAP NetWeaver Developer Studio abgebildet.
Java Dictionary
Bei der Entwicklung eines neuen Projekts müssen zunächst sämtliche
benötigten Tabellen im Java Dictionary angelegt worden sein. Das Java
Dictionary ist in das NetWeaver Developer Studio fest integriert, beim
Anlegen neuer Tabellen werden zunächst nur auf der Client-Seite (beim
Entwickler) Metadaten über die Tabellen erzeugt, die erst beim DeployVorgang in der jeweils verwendeten Datenbank erzeugt werden. Die Vorgehensweise bei der Entwicklung mit dem Java Dictionary wurde bereits
im entsprechenden Tutorium in Kapitel 5 ausführlich beschrieben, weshalb hier nur auf die wesentlichen SQLJ-Bereiche eingegangen wird.
Zielsetzung
Voraussetzungen
Tutorium: SQLJ-Entwicklung
Es müssen die SQLJ-Quelldateien angelegt werden. Hierzu haben Sie zum
einen die Möglichkeit, vollständig neue Dateien anzulegen bzw bestehende reine Java-Quellcodes in SQLJ-Quellcodes zu konvertieren, um in
diese SQL-Statements einzubetten. Wie bereits beschrieben, generiert
der SQLJ-Übersetzer automatisch aus den SQLJ-Dateien Java-Klassen,
sobald Sie Ihre Arbeit speichern. Arbeiten Sie aus diesem Grund immer
auf SQLJ-Ebene, niemals jedoch auf den Java-Dateien, da ansonsten
Inkonsistenzen entstehen können und die Java-Klassen generell beim
Speichern der zugehörigen SQLJ-Dateien überschrieben werden.
왘 Sie befinden sich in der NetWeaver-Developer-Studio-Umgebung.
왘 Es ist bereits ein Projekt vorhanden.
Ablauf
Zum Anlegen neuer Dateien verwenden Sie den Wizard:
1. Wählen Sie File • New • Other …
2. Wählen Sie im linken Fensterbereich Persistence und daraufhin im
rechten Bereich SQLJ Source.
3. Wählen Sie Next.
4. Geben Sie die erforlerlichen Informationen wie für eine Java-Datei an.
Zum Konvertieren einer bestehenden Java-Quelldatei gehen Sie folgendermaßen vor:
304
Anwendungsschichten
1. Legen Sie eine Java-Quelldatei an, falls Sie dies noch nicht getan haben
oder über keine verwendbare Datei verfügen.
2. Klicken Sie die Java-Quelldatei mit der rechten Maustaste an und wählen Sie im Kontextmenü Convert to SQLJ aus.
Mit beiden Methoden erhalten Sie eine SQLJ-Datei und eine Java-Datei
mit demselben Namen. Bearbeiten Sie niemals die Java-Datei, denn diese
enthält den generierten Code.
Ergebnis
Der SQLJ-Checker ist transparent in SAP NetWeaver Developer Studio
integriert. Beim Konvertieren der SQLJ-Dateien, was spätestens beim
Speichern der Daten automatisch geschieht, werden über diesen Checker
die eingebetteten SQL-Anweisungen geprüft. Zum einen wird die Konformität mit der Open-SQL-Grammatik validiert, zum andern wird das
Schema gegen eine Offline-Katalogbeschreibung geprüft, die von .gdbtable-Dateien bereitgestellt wird. Diese Dateien entstammen aus der
Phase, in der Sie das Java-Dictionary-Projekt angelegt haben, in ihnen
sind sämtliche Metadaten enthalten, die auch für den Deploy-Vorgang
des Java Dictionary verwendet werden.
Validierung –
SQLJ-Checker
Damit diese Schemaprüfung durchgeführt werden kann, muss der SQLJKonverter den Pfad der .gdtable-Datei kennen, die den Offline-Katalog
beschreibt, Sie müssen also jene Datei mit ihrem Projekt fest assoziieren:
1. Wählen Sie das Projekt.
Vorgehensweise
2. Wählen Sie Properties im Kontextmenü.
3. Wählen Sie SQLJ Translator.
4. Wählen Sie XML Source und geben Sie den Pfad zur .gdtable-Datei ein.
5. Wählen Sie OK.
Durch das Assoziieren der Offline-Katalogbeschreibung bietet das NetWeaver Developer Studio nun bereits während der Designzeit Unterstützung zum Ermitteln von SQL-Fehlern. Konvertierungs- und Java-Kompilierungsfehler werden direkt angezeigt und es wird die Option geboten,
direkt zum relevanten Teil der SQLJ-Quelldatei zu navigieren, sowie Haltepunkte in der SQLJ-Quelldatei zu setzen.
Grundsätzlich sollte Debugging nicht für den generierten reinen JavaCode verwendet werden, jedoch kann das Debugging von SQLJ-Quelldaten jederzeit aktiviert werden. Das Debugging selbst verhält sich allerdings ebenso wie das Debugging für Java-Quelldateien: Es werden innerhalb der Java-Quellen Haltepunkte gesetzt, der Quelltext wird
Beschaffungslogik und Persistenz
Debugging
305
schrittweise analysiert und die Werte werden überprüft. Sie können keine
Haltepunkte für SQLJ-Anweisungen setzen.
Vorgehensweise
Um das SQLJ-Debugging zu aktivieren, gehen Sie folgendermaßen vor:
1. Wählen Sie Window • Customize Perspective …
2. Wählen Sie Other.
3. Wählen Sie SQLJ-Debugging.
4. Wählen Sie OK.
5. Daraufhin wird SQLJ-Debugging dem Run-Menü hinzugefügt.
6. Wählen Sie Run • SQLJ-Debugging
Ergebnis
SQLJ-Debugging ist nun für die aktuelle Sitzung aktiviert.
Die Vorgehensweise für die Entwicklung der SQLJ-Quelltexte selbst verhält sich prinzipiell immer nach folgendem Schema:
1. Deklarieren Sie ein Datenbank-Connection-Context-Objekt, z.B.:
#sql context SysCtx with (dataSource = "jdbc/myDB");
Dieses Objekt basiert auf der Connection Context Class.
2. Schaffen Sie eine Verbindung zur Datenbank, indem Sie das Objekt
(Connection Context) instanziieren.
3. Nun arbeiten Sie mit dieser Verbindung, indem Sie SQL-Statements
absetzen und die Ergebnisse verarbeiten können.
4. Schließen Sie die Connection.
Kombination von SQLJ und JDBC
Für die Entwicklung von dynamisch generierten Statements bietet sich
aufseiten der relationalen Persistenz JDBC an, um dynamische SQL-Abfragen bzw. -Anweisungen zu implementieren. Da SAP für die Implementierung der Java-Persistenz die Verwendung von Open SQL, also SQLJ, empfiehlt und für interne Entwicklungen sogar explizit vorschreibt, stellt sich
dem Entwickler natürlich die Frage, wie sich beide Modelle miteinander
verbinden lassen.
Um in einer Anwendung dynamische und statische Ausdrücke zu verwenden, lassen sich SQLJ und JDBC gemeinsam verwenden. JDBC Connections und SQLJ Connection Contexte sind gegenseitig konvertierbar,
ebenso SQLJ-Iteratoren und JDBC Result Sets.
306
Anwendungsschichten
Da Open SQL bzw. SQLJ zur Laufzeit über die Open SQL Engine in JDBCAnfragen umgewandelt werden, können beide dieselbe Datenbankverbindung und Transaktion nutzen. Hingegen können SQLJ und Native
SQL/JDBC oder Vendor SQL/JDBC nicht dieselbe Verbindung oder Transaktion nutzen, da Letztere nicht den gesamten Stack der Open SQL
Engine durchlaufen.
Exchange
Connections
Alle Connection-Context-Klassen besitzen einen Konstruktor, der eine
existierende JDBC-Verbindung als Argument beinhaltet. Eine über diesen
Konstruktor erstellte SQLJ-Verbindung teilt die zugrunde liegende Datenbankverbindung mit der JDBC-Verbindung, aus der sie hervorgegangen
ist. Wird der SQLJ-Verbindungskontext mit der close(boolean closeConnection)-Methode geschlossen, wird auch die zugrunde liegende
JDBC-Verbindung beendet. Wird allerdings der boolsche Wert true, zur
besseren Lesbarkeit auch in Form der Konstanten ConnectionContext.KEEP_CONNECTION, als Argument übergeben, wird beim Methodenaufruf close() lediglich das SQL Connection Context Object von der
darunter liegenden JDBC-Verbindung losgelöst, diese wird also nicht
geschlossen.
JDBC-Verbindung
mit SQLJ nutzen
Im folgenden Codebeispiel wird ein SQLJ Connection Context ctx von
der JDBC-Verbindung conn kreiert. Nun teilen ctx und conn dieselbe
Datenbankverbindung. Die INSERT- und DELETE-Anweisungen werden
beide auf dieser Verbindung ausgeführt und teilen sich dieselbe Transaktion.
#sql context MyCtx;
//...
Connection conn = ... ;
Statement stmt = conn.createStatemnt();
stmt.executeUpdate( "INSERT
INTO ZRM_RES_MASTER
(MANDT, RESID, RESTYPE, DESCRIPTION,
INV_NUMBER, LOC_ADDRESS)
VALUES (100, 1, 'R', 'Besprechungsraum 1.OG', null,
'0000100100')");
MyCtx ctx = new MyCtx(conn);
Beschaffungslogik und Persistenz
307
#sql [ctx] { DELETE FROM ZRM_RES_
MASTER WHERE RESID = 1 };
Listing 6.4 Connection-Sharing von JDBC nach SQLJ
JDBC-Verbindung
aus SQLJ-Context
Die Methode getConnection() des Interfaces ConnectionContext
erlaubt es, eine JDBC-Verbindung von einem zugrunde liegenden SQLJ
Connection Context zu erhalten. Im folgenden Beispiel wird die dem
SQLJ Connection Context ctx unterliegende JDBC-Verbindung außerhalb des SQLJ-Codes dem reinen Java-Code verfügbar gemacht. Nun teilen sich ctx und conn dieselbe Datenbankverbindung. Die INSERT- und
DELETE-Anweisungen werden beide auf dieser Verbindung ausgeführt
und teilen sich dieselbe Transaktion.
#sql context DemoCtx with (dataSource = "jdbc/DEMO");
// ...
DemoCtx ctx = new DemoCtx();
#sql [ctx] { INSERT
INTO ZRM_RES_MASTER
(MANDT, RESID, RESTYPE, DESCRIPTION,
INV_NUMBER, LOC_ADDRESS)
VALUES (100, 1, 'R', 'Besprechungsraum 1.OG', null,
'0000100100') };
Connection conn = ctx.getConnection();
Statement stmt = conn.createStatemnt();
stmt.executeUpdate(
" DELETE FROM ZRM_RES_MASTER WHERE RESID = 1");
Listing 6.5 Connection-Sharing von SQLJ nach JDBC
Austauschen
von Result Sets/
Iteratoren
Ebenso wie Datenbankverbindungen gemeinsam genutzt werden können, lassen sich auch Result Sets und Iteratoren gemeinsam nutzen und
können untereinander ausgetauscht werden.
JDBC Result Set
zu SQLJ
Ein JDBC Result Set kann sehr einfach mit einem SQLJ-CAST-Statement in
einen SQLJ-Iterator konvertiert werden. Innerhalb von Open SQL/SQLJ
kann das CAST-Statement auf jeden Result-Set-Iterator innerhalb des derzeitigen Sichtbereichs angewandt werden. Um eine Kompatibilität mit
SQLJ-Übersetzern anderer Hersteller zu gewährleisten, sollte das CASTStatement nur auf öffentlichen (public) Result-Set-Iteratoren angewendet
308
Anwendungsschichten
werden. Ist das SQLJ-ResultSetIterator-Objekt einmal erstellt, sollten
alle Operationen, um Daten zu beschaffen, über die Methoden dieses
Objekts abgewickelt werden.
Nachfolgend wird das JDBC Result Set rs in einen SQLJ Result Set Iterator über den CAST-Ausdruck umgewandelt.
#sql iterator NamedIterator (String name);
//...
NamedIterator namIter;
Connection conn = ...
Statement stmt = conn.createStatement();
ResultSet rs =
stmt.executeQuery("SELECT RESID FROM
ZRM_RES_MASTER"
);
#sql namIter = { CAST :rs };
while (namIter.next()) {
System.out.println(namIter.name());
Listing 6.6 Umwandlung eines JDBC Result Sets in einen SQLJ Result Set Iterator
Auf ähnliche Weise lassen sich SQLJ-Ergebnissätze innerhalb von JDBC
verwenden. Dazu verfügt jedes ResultSetIterator-Objekt über die
getResultSet()-Methode, um das letztlich zugrunde liegende JDBCResultSet-Objekt zu erhalten. Es zeigt sich auch an dieser Stelle ganz
transparent, dass SQLJ letztlich die zugrunde liegende JDBC-Schicht
lediglich vor dem Anwender maskiert.
Iteratoren von
SQLJ nach JDBC
Ist im umgekehrten Falle das JDBC-ResultSet-Objekt einmal erstellt,
sollte die Datenübernahme in das umgebende Java-Programm auch über
diese spezielle Objektinstanz geschehen, anstatt durch die zusätzliche
Instanziierung eines SQLJ-ResultSetIterator doppelten Aufwand zu
betreiben.
Im folgenden Beispiel wird auf dem SQLJ-ResultSetIterator-Objekt
namIter die im vorletzten Absatz beschriebene Methode getResultSet() gerufen, die dieses als JDBC-ResultSet zurückgibt.
Beschaffungslogik und Persistenz
309
#sql iterator NamedIterator (String name);
//...
NamedIterator namIter = null;
#sql [ctx] namIter = { SELECT RESID FROM
ZRM_RES_MASTER };
ResultSet rs = namIter.getResultSet();
while (rs.next()) {
System.out.println(rs.getString(1));
}
Listing 6.7 Übernahme eines SQLJ-Iterators durch JDBC
Objektrelationale Persistenz
Wir haben uns in den vergangenen Kapiteln bereits an mehreren Stellen
mit der Eigenschaft der Objektorientierung der Sprache Java beschäftigt.
Dabei wurde über eine Vielzahl an Details von Enterprise Beans großzügig
hinweggegangen. In diesem Abschnitt werden nun die Entity Beans
genauer betrachtet.
Substantive
von Geschäftsprozessen
Entity Beans modellieren Geschäftskonzepte, die als Substantive ausgedrückt werden können. Diese grundsätzliche Regel hilft Entwicklern bei
der Entscheidung, ob ein Geschäftskonzept ein Kandidat für die Implementierung als Entity Bean ist. Diese Art von Beans stellt im Gegensatz zu
Session Beans keine Geschäftsprozesse dar, sondern Geschäftsobjekte
bzw. fachliche Entitäten. Dabei beschreiben sie sowohl den Zustand als
auch das Verhalten von Objekten der realen Welt und ermöglichen es
dem Entwickler, die Daten und Geschäftsregeln, die zu bestimmten Konzepten gehören, einzukapseln. Somit repräsentieren diese Beans Daten in
der Datenbank, weshalb Änderungen an ihnen auch Änderungen in der
Datenbank nach sich ziehen.
Es hat viele Vorteile, Entity Beans zu verwenden, anstatt direkt auf die
Datenbank zuzugreifen. Die Daten werden in Objektform gebracht und
stellen somit einen einfachen Mechanismus für den Zugriff auf dieselben
und deren Veränderung dar, nämlich über die Methoden des jeweiligen
Beans. Der Entwickler »spricht« quasi nicht mit der Datenbank, sondern
über eine Methode – PersonObject.tellMeYourName() – mit Objekten. Bei entsprechender Verwendung wird die Implementierung erleichtert und der Code lässt sich einfacher verstehen. Bedenken Sie die
310
Anwendungsschichten
Unmengen von – teilweise verschachtelten – SQL-Anweisungen. Zudem
erhöht man so die Chancen, wieder verwendbare Software zu schreiben.
Dabei ist jedoch zu beachten, dass ein Entity Bean sämtliche Funktionalitäten beinhaltet, um Datenkonsistenz und Einfachheit gegenüber dem
Entwickler zu bieten.
Wird ein neues Bean erzeugt, muss ein neuer Datensatz in der Datenbank
eingefügt werden und eine Bean-Instanz mit diesen Daten verbunden
werden. Wird das Bean verwendet und ändert sich sein Zustand, müssen
diese Änderungen mit den Daten in der Datenbank synchronisiert werden: Einträge müssen hinzugefügt, geändert oder entfernt werden. Die
Kommunikation zwischen Anwendung und Datenbank findet also weiterhin statt, ist jedoch dem Entwickler verborgen. Diesen Kommunikationsvorgang, die von einer Bean-Instanz repräsentierten Daten mit der
Datenbank zu koordinieren, nennt man Persistenz.
Man unterscheidet zwei Arten von Entity Beans, die nach verschiedenen
Konzepten diese Persistenz realisieren. Container Managed Persistence
sowie Bean Managed Persistence.
Container Managed Persistence (CMP)
Bei Container Managed Persistence wird die Persistenz automatisch vom
EJB-Container verwaltet. Dieser weiß, wie die Instanzattributwerte des
Beans auf die Datenbank- bzw. Tabellenfelder in der Datenbank abgebildet werden, und übernimmt das Einfügen, Ändern und Löschen der
Daten, die in der Datenbank zu Entitäten gehören.
Aus Sicht der Entwicklung sind CMP Entity Beans einfacher zu programmieren, da Sie sich auf die Implementierung der Geschäftslogik konzentrieren und die Verantwortlichkeit für Persistenz an den Container delegieren können. Bei Inbetriebnahme eines solchen Beans müssen Sie durch
ein Mapping definieren, welche Felder vom Container verwaltet werden
sollen und wie diese auf die Datenbank abgebildet werden. Ist diese
Arbeit einmal getan, erzeugt der Container die notwendige Logik, um
den Zustand der Bean-Instanz automatisch zu speichern.
Entwicklersicht
Felder, die auf die Datenbank abgebildet werden, heißen Container-verwaltete Felder. Sie können beliebige primitive Java-Typen oder serialisierte Objekte beinhalten. Der Vorteil der CMP liegt darin, dass das Bean
unabhängig von der zugrunde liegenden Datenbank, die später seinen
Zustand speichert, entwickelt werden kann. Container-verwaltete Beans
können sowohl relationale als auch Objekt-Datenbanken verwenden.
Beschaffungslogik und Persistenz
311
Der Zustand des Beans wird unabhängig definiert, was die Flexibilität und
somit die Möglichkeit der Wiederverwendung erhöht.
Nachteilig wirkt sich prinzipiell bei der Container-verwalteten Persistenz
aus, dass Sie komplizierte Abbildungswerkzeuge benötigen, um zu definieren, wie die Felder auf die Datenbank abzubilden sind. In manchen
Fällen genügt es allerdings auch, jedes Feld in dem Bean auf eine Spalte in
der Datenbank abzubilden oder das Bean in eine Datei zu serialisieren.
Oft wird es aber auch komplizierter, beispielsweise könnte der Zustand
eines Beans anhand eines komplexen relationalen Datenbank-Joins definiert sein. Für die Definition des Mappings stehen jedoch zahlreiche
Funktionen im SAP NetWeaver Developer Studio zur Verfügung.
O/R Mapping
Enterprise-BeanVorgaben
Regeln für die
CMP Felder
Das Mapping wird von SAP als O/R Mapping (Object/Relational Mapping)
bezeichnet. Dabei sind so genannte O/R-Mapping-Regeln (rules) zu
beachten, die gewisse Mapping-Regeln und darüber hinaus definieren,
welche Java-Datentypen auf welche JDBC-Typen abgebildet werden.
Wird das O/R Mapping innerhalb des NetWeaver Developer Studios
erstellt, werden diese Anforderungen erfüllt; wird jedoch von diesem
Schema abgewichen, bietet das Developer Studio zusätzlich eine O/RMapping-Verifikation an.
O/R-Mapping-Regeln
Jede Entity-Bean-Klasse entspricht einer separaten Tabelle in der Datenbank. Um die Integrität der Daten zu gewährleisten, können verschiedene Bean-Klassen nicht in ein und derselben Tabelle abgebildet werden.
Ein CMP-Feld, das ein atomares Attribut repräsentiert, wird auf eine einzelne Spalte gemappt. Folgende JDBC-Typen werden für die entsprechend korrespondierenden CMP-Felder akzeptiert:
Java-Datentypen
Mögliche JDBC-Datentypen
Standard-JDBC-Datentyp
java.lang.String
VARCHAR, CHAR, LONGVARCHAR, CLOB
VARCHAR
byte[]
VARBINARY, BINARY,
LONGVARBINARY, BLOB
VARBINARY
java.lang.Byte[]
VARBINARY, BINARY,
LONGVARBINARY, BLOB
VARBINARY
Short
SMALLINT
SMALLINT
Tabelle 6.1 Mapping-Regeln für CMP auf JDBC
312
Anwendungsschichten
Java-Datentypen
Mögliche JDBC-Datentypen
Standard-JDBC-Datentyp
java.lang.Short
SMALLINT
SMALLINT
Int
INTEGER
INTEGER
java.lang.Integer
INTEGER
INTEGER
Long
BIGINT
BIGINT
java.lang.Long
BIGINT
BIGINT
Float
REAL
REAL
java.lang.Float
REAL
REAL
Double
DOUBLE, FLOAT
DOUBLE
java.lang.Double
DOUBLE, FLOAT
DOUBLE
java.math.BigDecimal
DECIMAL, NUMERIC
DECIMAL
java.util.Date
TIMESTAMP
TIMESTAMP
java.sql.Date
DATE
DATE
java.sql.Time
TIME
TIME
java.sql.Timestamp
TIMESTAMP
TIMESTAMP
java.sql.Clob
CLOB
CLOB
java.sql.Blob
BLOB
BLOB
Boolean
SMALLINT
SMALLINT
java.lang.Boolean
SMALLINT
SMALLINT
Byte
SMALLINT
SMALLINT
java.lang.Byte
SMALLINT
SMALLINT
java.io.Reader
VARCHAR
VARCHAR
java.io.InputStream
VARBINARY
VARBINARY
Tabelle 6.1 Mapping-Regeln für CMP auf JDBC (Forts.)
Beziehungen werden durch Referenzierung zwischen Primärschlüsselspalten und Fremdschlüsselspalten realisiert.
Regeln für
Referenzfelder
Eine oder mehrere verschiedene Fremdschlüsselspalten werden pro
Beziehung definiert. Gibt es n Verbindungen zwischen zwei Beans, müssen folglich n Mappings zwischen Primär- und Fremdschlüsselspalten
existieren. Dabei muss der JDBC-Datentyp des Fremdschlüssels mit dem
des Primärschlüssels übereinstimmen.
Beschaffungslogik und Persistenz
313
Weiterhin darf eine Spalte vom Typ »unique key« nicht Teil eines Fremdschlüssels sein. Sie dürfen also eine Fremdschlüsselspalte nicht als »unique« oder Primärschlüssel – der somit dann auch den Status »unique«
erhielte – definieren.
1-zu-1-Beziehung
Bei der Realisierung einer 1-zu-1-Beziehung sind die Fremdschlüssel in
einer der beiden Tabellen, die an dieser Beziehung teilhaben, enthalten.
1-zu-n-Beziehung
In einer 1-zu-n-Beziehung sind die Fremdschlüssel in der Tabelle, die zu
dem Bean gehört, das die n-Seite der Beziehung repräsentiert.
n-zu-m-Beziehung
Zur Realisierung einer n-zu-m-Beziehung ist es unabdingbar, dass Sie eine
Zwischentabelle implementieren, die Fremdschlüssel zu beiden Primärschlüsseln der an der Beziehung beteiligten Objekte enthält. Die Spalten
müssen vom selben JDBC-Typ wie auch die Primärschlüsselspalten sein.
Einschränkungen
Die Validierung des O/R Mapping ist nicht in der Lage, folgende Fehler zu
erkennen und entsprechend zu behandeln:
왘 Eine Spalte ist als logischer Fremdschlüssel definiert, ist jedoch ein
echter Fremdschlüssel.
왘 Eine Spalte ist ein Primärschlüssel, ist jedoch als Fremdschlüssel defi-
niert.
Die Container Managed Persistence wird auch oft deklarative Persistenz
ganannt. Sie ist sehr einfach zu verwenden, wenn das Objektmodell der
persistenten Daten kompliziert ist. Es müssen keine SQL-Abfragen programmiert werden – Sie können innerhalb der Entwicklungsumgbung das
O/R Mapping, die entsprechenden Tabellen und SQL-Statements automatisch generieren lassen.
Tutorium: Container-verwaltete Entity Bean erzeugen
Dieses Tutorium beschreibt die Vorgehensweise zum Anlegen eines Entity
Beans über den Wizard im SAP NetWeaver Developer Studio. Enterprise
Beans können ebenso über die Kontextmenüs des relevanten Projekts
angelegt werden.
Voraussetzungen
Ablauf
Ein EJB-Module-Projekt existiert bereits.
1. Wählen Sie File • New • Other.
2. Auf der linken Seite der ersten Wizard-Seite wählen Sie J2EE • EJB, auf
der rechten wählen Sie nun Enterprise Bean aus.
3. Klicken Sie Next.
314
Anwendungsschichten
4. Im Feld EJB-Name geben Sie einen Namen für das neue Entity Bean
ein.
5. Wählen Sie im EJB-Projekt-Feld das Projekt aus, in dem das Bean enthalten sein soll.
6. Im Feld Bean Type wählen Sie Entity Bean.
7. Im Feld Default Package geben Sie ein Paket an, oder falls keines existiert, legen Sie hier ein neues an.
8. Wählen Sie Generate default interfaces oder spezifizieren Sie selbst,
welche Interfaces generiert oder genutzt werden sollen, wie in Abbildung 6.6.
Abbildung 6.6 Bean-Interfaces auswählen
9. Wählen Sie Next.
10. Wählen Sie nun den Persistenztyp – Container Managed Persistence
oder Bean Managed Persistence, in unserem Fall nun Container
Managed Persistence. Nun können Sie Persistenzfelder hinzufügen
und entfernen, was später jedoch auch jederzeit noch möglich ist.
11. Wählen Sie Next.
12. Fügen Sie, wenn benötigt, Superklassen hinzu und klicken Sie auf
Next.
13. Fügen Sie Methoden hinzu, auch dies ist später in der Entwicklungsphase jederzeit möglich. Für jede Methode wählen Sie den Typ der
Methode und klicken auf Add. Geben Sie Namen und Rückgabetypen
der Methoden an und spezifizieren Sie die Parameter.
14. Wählen Sie Finish.
Beschaffungslogik und Persistenz
315
Ergebnis
Im J2EE Explorer des SAP NetWeaver Developer Studios werden Sie nun
ein Bild wie in Abbildung 6.7 vorfinden.
Abbildung 6.7 Ergebnis im J2EE Explorer
Sie können nun das Entity Bean im Quellcode editieren und die Felder für
die Container-verwaltete Persistenz (CMP-Felder) erstellen.
Ablauf
1. Wählen Sie im J2EE-Explorer-Fenster Ihr EJB-Projekt, dann ejb-jar.xml
und schließlich dasjenige Enterprise Bean, dessen Felder Sie erstellen
möchten.
2. Wählen Sie im Kontextmenü Open, im rechten Fenster werden die
Bean-Eigenschaften dargestellt.
3. Wählen Sie den Tab Fields.
4. Wählen Sie Persistent Fields und klicken Sie auf Add. Es erscheint nun
ein neues Persistenzfeld als Unterknoten innerhalb der Persistent
Fields-Baumstruktur.
5. Selektieren Sie das entsprechende Feld und geben folgende Daten ein:
왘 Name: Der Name des Feldes. Das SAP NetWeaver Developer Stu-
dio wird diesen Namen verwenden, um die korrespondierenden
get- und set-Accessormethoden zu erstellen. Dabei wird in JavaManier der erste Buchstabe des Feldnamens großgeschrieben, mit
entsprechend vorangestelltem set bzw. get.
왘 Fully Qualified Name: Der vollständig qualifizierte Name des Typs
des Feldes, der auch den Packetnamen beinhalten muss.
왘 Array: Wählen Sie diese Option, um zu spezifizieren, dass das Per-
sistenzfeld ein Array der spezifizierten Typen darstellt. Geben Sie die
Dimension des Arrays in das Feld ein, das erscheint, wenn Sie Array
option wählen. Die Werte dafür müssen zwischen 1 und 9 liegen.
316
Anwendungsschichten
Die Persistenzfelder sind nun über die entsprechende ejb-jar.xml-Deployment-Deskriptor-Datei beschrieben. Ihr O/R Mapping wurde automatisch im SAP-J2EE-Engine-spezifischen Deployment-Deskriptor persitent.xml beschrieben. Diese Datei konfiguriert den EJB-Container, um die
Container-verwaltete Persistenz zu übernehmen. In der persistent.xmlDatei sind dazu folgende Eigenschaften und Einstellungen enthalten:
Ergebnis
왘 Datenquelle und Datenbankhersteller
왘 die Art und Weise der Sperrmechanismen für die Entity Beans
왘 das bereits behandelte O/R Mapping
왘 die Deployment-Eigenschaften der Finder- und Select-SQL-Methoden,
die zur Performanceoptimierung der Entity Beans vom Container verwendet werden
Beim Deployment-Prozess wird der gesamte benötigte Code nun aufgrund der Informationen in den Deployment-Deskriptoren vom EJBContainer generiert.
Es lässt sich also feststellen, dass der Entwickler die Zugriffslogik nicht
mehr implementieren muss, sondern lediglich Attribute und Beziehungen
deklariert und konfiguriert.
Bean Managed Persistence (BMP)
Die Bean-verwaltete Persistenz ist wesentlich komplizierter als die Container-verwaltete Persistenz, weil Sie als Entwickler die Persistenzlogik
explizit in der Bean-Klasse programmieren müssen.
Sie müssen also die SQL-Abfragen selbst vollständig implementieren.
Dadurch gestattet dieses Modell eine hohe Flexibilität, selbst festzulegen,
wie der Zustand zwischen der Bean-Instanz und der Datenbank verwaltet
wird. Entity Beans, die durch komplexe Joins, eine Kombination von verschiedenen Datenbanksystemen oder mit anderen Ressourcen wie Altsystemen definiert werden, profitieren in aller Regel von der Bean-verwalteten Persistenz. Auch wenn das O/R Mapping des abstrakten
Schemas nicht den Anforderungen des Projekts entspricht, kann dieses
Programmiermodell Unterstützung bieten.
Mit der Container-verwalteten Persistenz lassen sich Objekte lediglich auf
eine Tabelle abbilden und man ist dort doch relativ eingeschränkt, wenn
es beispielsweise um echte verteilte Objekte geht, die eben nicht innerhalb einer Tabelle gehalten werden, sondern beispielsweise aus mehreren
Attributen aus verschiedenen Datenquellen zusammengesetzt werden
Beschaffungslogik und Persistenz
317
sollen. Hier bietet die Bean-verwaltete Persistenz deutlich mehr Möglichkeiten und Flexibilität.
Der Nachteil der Bean-verwalteten Persistenz besteht darin, dass mehr
Arbeit zur Definition des Beans notwendig ist. Sie müssen die Struktur
der Datenbank verstehen und die Logik entwickeln, mit denen die mit
einer Entität verbundenen Daten erzeugt, aktualisiert und entfernt werden. Dies erfordert höchste Sorgfalt im Umgang mit den generischen
Bean-Methoden, insbesondere ejbLoad() und ejbStore(). Auch die
im Home-Interface des Beans definierten Suchmethoden und das Mapping der Bean-Attribute auf die Datenbank müssen Sie explizit von Hand
entwickeln.
Eine Bean-verwaltete Anwendung ist nicht so datenbankunabhängig wie
eine Container-verwaltete Entität, jedoch für den Umgang mit komplexen Daten eher geeignet. Sie können innerhalb eines Entity Beans sowohl
reines herstellerspezifisches JDBC als auch die native Form sowie Open
SQL, also SQLJ, verwenden, um eine größtmögliche Datenbankunabhängigkeit zu gewährleisten.
Dennoch empfiehlt SAP innerhalb der objektrelationalen Entwicklung
grundsätzlich die Verwendung von Container-verwalteter Persistenz bzw.
Java Data Objects, auf die im Folgenden näher eingegangen wird. Aus
diesem Grund wird an dieser Stelle nicht weiter auf die Bean-verwaltete
Persistenz eingegangen, vielmehr empfiehlt sich für dieses Thema eine
spezielle Java- bzw. J2EE-Lektüre.
Java Data Objects (JDO)
Java Data Objects sind der zweite von SAP präferierte Weg zur Realisierung objektrelationaler Persistenz.
Der Java-Data-Objects-Standard ist eine viel versprechende Technologie
für persistente Java-Objekte. JDO ist zwar einer der zahlreichen JavaStandards und wird meist in Verbindung mit J2EE genannt, ist jedoch
weder Teil der J2EE-1.3- noch der 1.4-Spezifikationen. Doch aufgrund der
Vorzüge, die JDO gegenüber dem EJB-Konzept teilweise bietet, ist JDO
im SAP Web Application Server implementiert.
Während EJB Entity Beans das Komponentenmodell der J2EE-Architektur
zugrunde liegt, versucht der Java-Data-Objects-Standard, sich möglichst
nahe an das Objektmodell der Programmiersprache Java zu halten. JDO
erlaubt damit die direkte Persistierung fast jeder Java-Klasse, unabhängig
von der Architekturebene, in der sich deren Objekte befinden. JDO erfor-
318
Anwendungsschichten
dert also nicht das Container-Modell der J2EE-Umgebungen, sondern
erweitert vielmehr direkt die Sprache Java um Persistenz.
Zusätzlich ist es mit JDO möglich, auf Data Stores verschiedenster Typen
zuzugreifen – relationale Datenbanken, objektrelationale Datenbanken
oder dateibasierte Formate.
JDO basiert auf einer Bytecodetransformation der zu persistierenden
Klassen. Für jede Klasse, die persistent gespeichert werden soll, muss
eine XML-basierte Mapping-Datei angelegt werden, die ähnlich wie bei
CMP die Abbildung der Klassenattribute auf die Datenbanktabellen
beschreibt. Der so genannte Bytecode Enhancer wird daraufhin die
Zugriffsmethoden auf der Klasse überschreiben und durch die benötigten
SQL-Statements ersetzen.
JDO verfügt über einen Persistence Manager, der den Lebenszyklus,
Transaktionen, Abfragen und Identitäten der Persistenzobjekte verwaltet.
Die Abfragesprache ist JDOQL – Java Data Objects Query Language.
JDO lässt sich innerhalb einer J2EE-Umgebung mit JSPs, Session Beans
und Entity Beans (BMP) kombinieren, nicht jedoch mit Container-verwalteten Entity Beans (CMP).
Tutorium: JDO-Entwicklungszyklus
Die Entwicklung eines persistenten Objekts gestaltet sich für den Entwickler sehr strukturiert nach einem festen Schema. Die Entwicklungstools für JDO sind derzeit noch nicht in das Developer Studio integriert,
weshalb sämtliche Schritte momentan noch manuell durchgeführt werden müssen. Dieses Tutorium stellt anhand eines Mitarbeiterobjekts
einen Entwicklungszyklus dar, realisiert durch die Klasse Ressource:
1. Definition der Datenbanktabellen
Zielsetzung
Ablauf
2. Erstellen der zu persistierenden, reinen Java-Klassen
Die Klassen, die später die Persistenz realisieren sollen, müssen
zunächst, wie auch andere Java-Klassen im SAP NetWeaver Developer
Studio, angelegt und implementiert werden. Jede dieser Klassen definiert letztlich Objekte, die in einer Datenbank gespeichert und aus der
Datenbank bezogen werden können.
Wählen Sie für das Anlegen der Klassen New
der Klasse den Namen Ressource.
•
Java Class. Geben Sie
Die neuen Java-Dateien öffnen sich automatisch und Sie können Code
eingeben. Der folgende Codeausschnitt zeigt beispielhaft eine solche
Beschaffungslogik und Persistenz
319
Klasse, wir beschränken uns an dieser Stelle auf drei Attribute des Ressourcenobjekts:
public class Ressource {
// Class attributes: the persistent fields
//
of a ressource.
// Also defined inside the file Ressource.jdo
private int resId;
private String resType;
private String description;
// Required: a no-args constructor
public Ressource() {
this.resId = 1;
this.resType = "INITIAL";
this.description = "INITIAL";
}
// Constructor where the ID is set
public Ressource(int resId) {
this.resId = resId;
this.resType = "INITIAL";
this.description = "INITIAL";
}
// Implement the getter methods of the class
public int getResId() {
return resId;
}
public String getResType() {
return resType;
}
public String getDescription() {
return description;
}
// Implement the setter methods of the class
public void setResType(String type) {
320
Anwendungsschichten
resType = type;
}
public void setDescription(String desc) {
description = desc;
}
}
3. Definieren der Objektidentität
JDO sieht eine Identitätsklasse pro persistentem Objekt vor, die garantiert, dass eine einzelne JDO-Instanz mit einem Persistenzmanager
assoziiert ist, der ein bestimmtes Datenspeicherobjekt – die Datenbank
– repräsentiert.
Während der JDO-Standard drei Typen der Identität beschreibt, nämlich Anwendungs- (Application), Datenspeicher- (Data Store) und
nicht-dauerhafte Identität (Nondurable Identity), unterstützt die JDOImplementierung der SAP lediglich die Anwendungsidentität. In dieser
Form wird die JDO-Identität durch die Anwendung verwaltet und im
Datenspeicher gehalten. Die Instanzidentität entspricht meist dem Primärschlüssel.
Zur Realisierung der Identität muss für jede persistente Klasse eine
spezielle Objektidentitätsklasse erstellt werden. Diese wird als
static public inner class ID der zugehörigen Persistenzklasse
definiert. Für jedes Primärschlüsselfeld der Persistenzklasse erhält die
Objektidentitätsklasse ein korrespondierendes public instance-Feld
vom selben Namen und Datentyp.
Die Objektidentitätsklasse hat einen Konstruktor ohne Argumente wie
auch die Persistenzklasse. Weiterhin hat sie einen String-Konstruktor,
der wie eine toString()-Methode eine Instanz als String zurückgibt.
Zusätzlich muss sie eine hashCode()-Methode implementieren, die
die Primärschlüssel zurückgibt, sowie eine equals()-Methode, die
mit einem boolschen Rückgabewert zur Instanzüberprüfung bzw. zur
Überprüfung der Instanzzugehörigkeit von Objekten dient.
Der folgende Code zeigt beispielhaft die Identitätsklasse für das Ressourcenobjekt:
import com.sap.jdo.SAPJDOHelper;
static public class Id {
// public field corresponding to the primary key of the
Beschaffungslogik und Persistenz
321
// PC class
public int resId;
static { // establish the relation: Ressource$Id
// class is the identity class for the PC
// class Ressource.
SAPJDOHelper.registerPCClass(Ressource.class);
}
public Id() {
// required: a no-args constructor
}
public Id(int resId) {
this.resId = resId;
}
public Id(String string) {
// required: a string constructor
// defined as the counterpart of toString()
resId = Integer.parseInt(string);
}
public int hashCode() {
// required: implement hashCode()
return resId;
}
public String toString() {
// required: toString() defined
// as the counterpart of the string constructor
return Integer.toString(resId);
}
public boolean equals(Object that) {
// required: define equals()
if (that == null || !(that instanceof Id))
return false;
else
return resId == ((Id) that).resId;
}
}
322
Anwendungsschichten
4. Definieren der JDO-Metadaten
Nun können die XML-Metadaten für die JDO-Objekte definiert werden. Hierzu erhält jede Persistenzklasse eine zugehörige *.jdo-Datei,
die im selben Verzeichnis liegen muss.
왘 Wählen Sie hierzu New • File.
왘 Geben Sie das richtige Verzeichnis an, in dem die Datei abgelegt
werden soll, und geben Sie der Datei denselben Namen wie der
entsprechenden Java-Klassen-Datei, mit der Endung .jdo.
왘 Sie können nun die Definitionen eingeben. Das folgende Listing
zeigt beispielhaft den Code für die oben demonstrierte Java-Klasse:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
<package name="temp.persistence.gettingstarted.jdo">
<class name="Ressource"
identity-type="application"
objectid-class="Ressource$Id">
<field name="resId"
persistence-modifier="persistent"
primary-key="true"/>
<field name="resType"
persistence-modifier="persistent"/>
<field name="description"
persistence-modifier="persistent"/>
</class>
</package>
</jdo>
5. Definieren des O/R Mappings für die Persistenzklassen
Nun kann das Mapping erzeugt werden, dies geschieht wiederum über
eine entsprechende XML-Datei, die ebenfalls unter demselben Namen
im selben Ordner erstellt werden muss, jedoch mit der Dateiendung
*.map.
Das Format dieser Klasse wird durch die JDO Mapping Metadata
Document Type Definition (DTD) spezifiziert.
Beschaffungslogik und Persistenz
323
Das Anlegen der Datei vollzieht sich ebenso wie das der *.jdo-Datei,
nachfolgend wird das Mapping zu unserem Beispiel demonstriert:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map SYSTEM "map.dtd">
<map version="1.0">
<package name="temp.persistence.gettingstarted.jdo">
<class name="Ressource">
<field name="resId">
<column name="RESID" table="ZRM_RES_MASTER"/>
</field>
<field name="resType">
<column name="RESTYPE" table="ZRM_RES_MASTER"/>
</field>
<field name="description">
<column name="DESCRIPTION"
table="ZRM_RES_MASTER"/>
</field>
</class>
</package>
</map>
6. Bytecode-Kompilierung durch das Enhancer-Tool
Nachdem nun die entsprechenden Klassen und Deskriptoren erstellt
sind, müssen nun durch den JDO Enhancer die entsprechenden Klassen erzeugt werden, mit denen letztlich die Geschäftslogik der Anwendung kommunizieren soll.
Da die JDO-Enhancer- und Validierungstools noch nicht in das Developer Studio integriert sind, müssen diese Schritte manuell durchgeführt werden. Es empfiehlt sich die Verwendung des ANT-Build-Tools,
das als Plug-In für das Developer Studio bereitsteht.
왘 Erstellen Sie zunächst über das Kontextmenü Ihres Projekts eine
neue Datei mit dem Namen build.xml und speichern Sie diese im
Hauptverzeichnis ihres Projekts.
왘 Fügen Sie – im Falle unseres Beispiels – folgenden Code ein:
<project name="GettingStartedWithJDO" default="enhance"
basedir="..">
<property name ="sourceproject.dir"
value="GettingStartedJDOWeb"/>
324
Anwendungsschichten
<property name ="dictproject.dir"
value="GettingStartedPersistenceDic"/>
<property name ="src.dir"
value="${sourceproject.dir}/source"/>
<property name ="bin.dir"
value="${sourceproject.dir}/bin"/>
<property name ="catalog.dir"
value="${dictproject.dir}/gen_ddic
/dbtables/"/>
<property name ="enhancer"
value="com.sap.jdo.enhancer.Main"/>
<property name ="utility"
value="com.sap.jdo.sql.util.JDO"/>
<property name ="tssap"
value="C:/Program Files/SAP/JDT/eclipse
/plugins"/>
<property name ="jdo"
value="${tssap}/com.sap.ide.eclipse.ext
.libs.jdo/lib/jdo.jar"/>
<property name ="xml"
value="${tssap}/com.tssap.sap.libs
.xmltoolkit
/lib/sapxmltoolkit.jar"/>
<property name ="jdoutil"
value="${tssap}/com.sap.jdo.utils
/lib/sapjdoutil.jar"/>
<property name ="dictionary"
value="${tssap}/com.sap.dictionary.database
/lib/jddi.jar"/>
<property name ="logging"
value="${tssap}/com.tssap.sap.libs.logging
/lib/logging.jar"/>
<property name ="catalogreader"
value="${tssap}/com.sap.opensql
/lib/opensqlapi.jar"/>
<property name ="classpath"
value="${jdo};${jdoutil};${xml}"/>
Beschaffungslogik und Persistenz
325
<property name ="classpath.check"
value="${classpath};${dictionary};${logging};
${catalogreader};${bin.dir}"/>
<target name="enhance">
<antcall target="enhance.Ressource"/>
</target>
<target name="check">
<antcall target="check.Ressource"/>
</target>
<target name="enhance.Ressource">
<java
fork
="yes"
failonerror="yes"
classname ="${enhancer}"
classpath ="${classpath}">
<arg line ="-f -d" />
<arg value="${bin.dir}"/>
<arg value="${src.dir}/temp/persistence/
gettingstarted/jdo/Ressource.jdo"/>
<arg value="${bin.dir}/temp/persistence/
gettingstarted/jdo/Ressource.class"/>
</java>
</target>
<target name="check.Ressource">
<java
fork
="yes"
failonerror="yes"
classname ="${utility}"
classpath ="${classpath.check}">
<arg line ="-v -p" />
<arg value ="${sourceproject.dir}
/checker.properties"
/>
<arg value ="-c"/>
<arg value ="${catalog.dir}"/>
<arg value ="check"/>
<arg value ="temp/persistence/gettingstarted/jdo
326
Anwendungsschichten
/Ressource.class"/>
</java>
</target>
</project>
왘 Nun müssen Sie über das Kontextmenü des Projekts eine neue
Datei namens checker.properties erstellen, die ebenfalls im Hauptordner des Projekts enthalten sein muss. Fügen Sie dort folgenden
Code ein:
com.sap.jdo.sql.mapping.useCatalog=true
com.sap.jdo.sql.mapping.checkConsistency=true
com.sap.jdo.sql.mapping.checkConsistencyDeep=true
7.
In der Java-Perspektive des Developer Studios öffnen Sie nun das
Kontextmenü der build.xml-Datei und wählen Run ANT…
8. Im Tab Targets wählen Sie zusätzlich zu enhance die Option check.
9. Wählen Sie Apply und daraufhin Run. Das Ergebnis dieses Prozesses
wird über die Konsole des Developer Studios ausgegeben.
10. Nun implementieren die daraus generierten Klassen das Interface
javax.jdo.spi.PersistenceCapable und können nun von der
Geschäftslogik verwendet werden.
Die Abbildung 6.8 fasst den Entwicklungsprozess der JDO-Persistierung
nochmals zusammen.
Ergebnis
Obgleich die Vorgehensweise der Entwicklung mit JDO sehr strukturiert
abläuft, ist es doch nachteilig, dass die entsprechenden Werkzeuge für
den Umgang mit JDO noch nicht im Developer Studio integriert sind. Ein
Großteil der Entwicklungszeit muss in Arbeit investiert werden, die
eigentlich durch den Einsatz entsprechender Tools generisch abgearbeitet
werden könnte. So kommt es insbesondere bei größeren Projekten hier
schnell zu Fehlern, da man als Entwickler schnell die Übersicht über die
manuell erstellten Dateien verlieren kann.
Beschaffungslogik und Persistenz
327
Java-Klasse
schreiben
.java
.jdo
Klasse
kompilieren
Compiler
Datenbanktabellen
deklarieren
.class
Klasse um JDOMetadaten erweitern
Persistente Eigenschaften
deklarieren (XML)
Enhancer Tool
(IDE)
.class
.map
Objektrelationales Mapping
und Tabellen deklarieren (XML)
Persistente Java-Klasse
in der Applikation verwenden
Abbildung 6.8 JDO-Entwicklungszyklus
Das volle Potenzial des JDO-Ansatzes wird im SAP-Umfeld wohl erst ausgenutzt werden, wenn die entsprechenden Erweiterungen für das Developer Studio seitens der SAP nachgereicht werden.
Wie bereits angesprochen, empfiehlt SAP im Bereich der objektorientierten Persistenz die Verwendung von Container-verwalteter Persistenz oder
von JDO, Bean-verwaltete Persistenz wird derzeit völlig außer Acht gelassen. Die folgende Tabelle vergleicht nun die von SAP empfohlenen beiden Ansätze und gibt Vorschläge für den jeweiligen Einsatzbereich:
JDO 1.0
Entity Beans (CMP) 2.0
Nicht Teil des J2EE-Standards, wird in
den J2EE-Anwendungsservern also nur
optional unterstützt.
Teil des J2EE-Standards, die Unterstützung von CMP ist durch die J2EE-Spezifikationen gesichert.
CMP bietet ein komplettes Programmiermodell.
Bietet keine Unterstützung von direkten
Remote-Calls; diese können jedoch unter
Verwendung von Facade Session Beans
realisiert werden.
Direkte Remote-Aufrufe sind möglich,
trotz allem wird empfohlen, Entity Beans
nur lokal zu verwenden, während
Remote-Kommunikation an Facade Session Beans delegiert werden sollte.
Keine Sicherheitsfunktionalitäten implizit
vorhanden.
CMP-Spezifikationen enthalten Sicherheitsfeatures.
Tabelle 6.2 Vergleich zwischen JDO und Entitiy Beans
328
Anwendungsschichten
JDO 1.0
Entity Beans (CMP) 2.0
JDO ist sowohl in verwalteten Umgebun- Nur in verwalteten Umgebungen wie im
gen als auch in offenen Java-UmgebunEJB-Container lauffähig.
gen verfügbar.
Unterstützt Vererbung und Interfaces,
multiples Mapping und Mapping zu
Legacy-Tabellen.
Unterstützt keine Vererbung und Interfaces. Ein Bean kann nur auf einer einzigen Tabelle abgebildet werden.
Eine separate Klasse für die Primärschlüs- Eine Primärschlüsselklasse ist nicht notsel ist notwendig, derzeit ist diese Imple- wendig.
mentierung nicht automatisiert und entsprechend aufwändig.
Nicht zentral verwaltete Beziehungen
unter Objekten untereinander.
Zentral verwaltete Beziehungen unter
den Objekten untereinander.
JDOQL erlaubt dynamische Abfragen, die
immer Objekte zurückliefern. Allerdings
hat JDOQL eine Java-ähnliche Syntax und
ist weniger mächtig.
Mit EJBQL können nur statische Abfragen
ausgeführt werden, die unveränderliche
Datensets zurückliefern. EJBQL hat eine
mit OQL vergleichbare Syntax und ist
mächtiger als JDOQL.
Schnellere Deploymentzyklen.
Die Deploymentvorgänge sind sehr komplex, daher auch entsprechend langsamer.
Kenntnisse in der Sprache Java sind für
die Implementierungen im Wesentlichen
ausreichend.
Der Umgang mit EJBs erfordert umfangreiches Wissen über Objektorientierung
und verteilte Konzepte.
Tabelle 6.2 Vergleich zwischen JDO und Entitiy Beans (Forts.)
6.2
Middleware: Konnektivität zwischen
Applikationen
Bereits mehrfach wurde nun angesprochen, dass eine Konnektivität zwischen ABAP- und Java Personality grundsätzlich auf Anwendungsebene
erfolgen muss. Es ist einer Java-Anwendung normalerweise nicht möglich, auf direktem Wege – beispielsweise durch etwaige JDBC-Treiber –
auf die Datenbank der ABAP-Instanz zuzugreifen. Dies gilt auch im umgekehrten Fall für ABAP-Anwendungen.
Durchgriff auf
ABAP Personality
Aus Sicht der Persistenz sind derartige Verbindungen zur reinen Datenbeschaffung notwendig, aus Sicht der Geschäftslogik wird in vielen Projekten eine Kommunikation zwischen beiden Entitäten angestrebt, um
Geschäftsprozesse auf einer der beiden Identitäten auszuführen. In beiden Fällen findet der Austausch von Informationen und Daten auf der
Komponentenebene statt, basierend auf einer Middleware-Infrastruktur,
Middleware: Konnektivität zwischen Applikationen
329
Herunterladen