Persistenzmodelle in EJB-Architekturen - Software

Werbung
INSTITUT FÜR ANGEWANDTE INFORMATIK UND FORMALE
BESCHREIBUNGSVERFAHREN (AIFB)
Universität Karlsruhe (TH)
Persistenzmodelle in EJB-Architekturen
Diplomarbeit
Tilman Seifert
25. Januar 2002
Aufgabensteller:
Betreuer:
Prof. Dr. Wolffried Stucky
Dr. Roland Schätzle
Prof. Dr. Johannes Siedersleben, sd&m Research GmbH
Gerd Beneken, sd&m Research GmbH
Erklärung
Ich versichere hiermit wahrheitsgemäß, die Arbeit bis auf die dem Aufgabensteller
bereits bekannte Hilfe selbständig angefertigt, alle benutzten Hilfsmittel vollständig
und genau angegeben und alles kenntlich gemacht zu haben, was aus Arbeiten anderer
unverändert oder mit Abänderung entnommen wurde.
München, den 25. Januar 2002
Danksagung
Denen, die zum Gelingen dieser Arbeit beigetragen haben, danke ich herzlich: Herr
Siedersleben und das gesamte sd&m Research-Team haben ein sehr gutes Umfeld
für diese Arbeit geschaffen. Die Betreuung durch Gerd Beneken war exzellent; er
hat mit seinen vielen Ideen, Hinweisen und Hilfestellungen einen wesentlichen Teil
zum Erfolg beigetragen. Roland Schätzle hat mich mit seinen wertvollen kritischen
Anmerkungen auf Kurs gehalten und hat in der hektischen Abgabephase Geduld und
Flexibilität bewiesen.
Außerdem gilt mein Dank meinen Geschwistern und den Hardtwald-Läufern“ für ihre
”
Hilfe bei schwierigen Entscheidungen und nicht zuletzt meinen Eltern sowie meiner
Tante Elke Seifert, die mich unterstützt und mir mein Studium überhaupt ermöglicht
haben.
ii
Inhaltsverzeichnis
1 Einleitung
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Überblick über die Arbeit . . . . . . . . . . . . . . . . . . . . . . . . .
1
1
2
2
2 Architektur betrieblicher Informationssysteme
2.1 Charakteristika betrieblicher Informationssysteme . . . . . . . . . . . .
2.2 Komponentenorientierte Software-Entwicklung . . . . . . . . . . . . .
2.2.1 Eigenschaften einer Komponente . . . . . . . . . . . . . . . . .
2.2.2 Entwurf von Komponenten . . . . . . . . . . . . . . . . . . . .
2.2.3 Abgrenzung zur objektorientierten Software-Entwicklung . . .
2.3 Quasar als komponentenorientierte Architektur . . . . . . . . . . . . .
2.3.1 Softwarekategorien . . . . . . . . . . . . . . . . . . . . . . . . .
2.3.2 Elemente von Quasar . . . . . . . . . . . . . . . . . . . . . . .
2.3.3 Kritik/Einordnung . . . . . . . . . . . . . . . . . . . . . . . . .
2.4 Aufbau einer Standard-Architektur betrieblicher Informationssysteme
2.4.1 Anwendungs-Komponenten . . . . . . . . . . . . . . . . . . . .
2.4.2 Anwendungs-Fälle . . . . . . . . . . . . . . . . . . . . . . . . .
2.4.3 Anwendungs-Entitäten . . . . . . . . . . . . . . . . . . . . . . .
2.4.4 Anwendungs-Entitäten-Verwalter . . . . . . . . . . . . . . . . .
2.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
4
4
5
5
6
6
7
9
9
10
10
10
11
11
12
3 Standardprobleme beim Datenbankzugriff
3.1 Objekt-relationale Abbildung . . . . . . . . . . . . . . . . . . . . .
3.1.1 Abbildung von Attributen . . . . . . . . . . . . . . . . . . .
3.1.2 Abbildung von Beziehungen zwischen Objekten . . . . . . .
3.1.3 Objektidentität . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.4 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Transaktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.1 Transaktionsstrategien . . . . . . . . . . . . . . . . . . . . .
3.2.2 Abbildung von Transaktionen auf die Benutzer-Interaktion
3.2.3 Implementierung der fachlichen Transaktionen . . . . . . .
3.3 Performanz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4 Wartung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
13
13
14
14
14
16
16
18
19
20
21
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
iii
Inhaltsverzeichnis
4 Enterprise Java Beans
4.1 Die EJB-Spezifikation . . . . . . . . . . . . . . . . . . . .
4.2 Infrastruktur und Produkte . . . . . . . . . . . . . . . . .
4.3 Enterprise-Beans . . . . . . . . . . . . . . . . . . . . . . .
4.3.1 Aufbau einer Bean . . . . . . . . . . . . . . . . . .
4.3.2 Session-Beans . . . . . . . . . . . . . . . . . . . . .
4.3.3 Entity-Beans . . . . . . . . . . . . . . . . . . . . .
4.3.4 Lebenszyklen von Session- und Entity-Beans . . .
4.3.5 Transaktionen . . . . . . . . . . . . . . . . . . . .
4.4 Persistenz von Entity-Beans . . . . . . . . . . . . . . . . .
4.4.1 Container Managed Persistence (CMP) . . . . . .
4.4.2 Einsatz eines OR-Mapping-Werkzeugs für CMP . .
4.4.3 Bean Managed Persistence (BMP) . . . . . . . . .
4.4.4 Java Data Objects (JDO) . . . . . . . . . . . . . .
4.4.5 Weitere Aspekte . . . . . . . . . . . . . . . . . . .
4.5 EJB 2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5.1 Erweiterungen in EJB 2.0 bezüglich der Persistenz
4.5.2 Bewertung der Änderungen . . . . . . . . . . . . .
4.5.3 Der Wechsel von EJB 1.1 auf EJB 2.0 . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
22
23
24
24
27
29
29
31
32
32
34
35
37
37
37
38
40
41
5 Persistenzoptionen von A-Entitäten
5.1 Persistenz über Entity-Beans . . . . . . . . . . . . . .
5.1.1 Kritik am naiven Einsatz von Entity-Beans . .
5.1.2 Zusammenfassung . . . . . . . . . . . . . . . .
5.2 Persistenz über native Java-Objekte . . . . . . . . . .
5.2.1 Ausprogrammieren mit JDBC . . . . . . . . . .
5.2.2 Zugriffsschichten: QDI und TopLink . . . . . .
5.2.3 Exkurs: Objektorientierte Datenbanken . . . .
5.2.4 Datenbankzugriff über Entity-Beans mit CMP
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
48
48
48
49
53
55
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6 Persistenz über ein neutrales Interface
6.1 Entwurf wartbarer Systeme mit EJB . . . . . . . . . . . . . . . . . . .
6.1.1 Innovationszyklen betrieblicher Informationssysteme . . . . . .
6.1.2 Entkoppelung des Anwendungskerns vom Datenbankzugriff über
ein neutrales Interface . . . . . . . . . . . . . . . . . . . . . . .
6.2 Anforderungen an das neutrale Interface . . . . . . . . . . . . . . . . .
6.3 Design des neutralen Interfaces . . . . . . . . . . . . . . . . . . . . . .
6.4 Implementierung des neutralen Interfaces . . . . . . . . . . . . . . . .
6.4.1 Spezifische JDBC-Lösung . . . . . . . . . . . . . . . . . . . . .
6.4.2 Einsatz einer Datenbankzugriffsschicht . . . . . . . . . . . . . .
6.4.3 Umsetzung mit CMP . . . . . . . . . . . . . . . . . . . . . . .
6.5 Beurteilung des Entwurfs . . . . . . . . . . . . . . . . . . . . . . . . .
6.5.1 Wartbarkeit des Codes . . . . . . . . . . . . . . . . . . . . . . .
6.5.2 Unabhängigkeit von der verwendeten Basistechnologie . . . . .
iv
57
57
57
57
60
61
62
62
63
65
66
66
67
Inhaltsverzeichnis
6.5.3
6.5.4
Effizienz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . 67
7 Fallstudie: Telekom-Billing-System
7.1 Überblick: Die Komponenten des TBS . . . . . . . . . . . . . .
7.1.1 Kundenverwaltung . . . . . . . . . . . . . . . . . . . . .
7.1.2 Rechnungsverwaltung . . . . . . . . . . . . . . . . . . .
7.1.3 Tarif . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Implementierung . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1 Überblick über die Klassen des Anwendungskerns . . . .
7.2.2 Das neutrale Interface . . . . . . . . . . . . . . . . . . .
7.2.3 Implementierung 1: Datenbankzugriff über Entity-Beans
7.2.4 Implementierung 2: Datenbankzugriff über das QDI . .
7.2.5 Vergleich der zwei Implementierungen . . . . . . . . . .
7.2.6 Praxiserfahrungen . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
69
70
70
73
75
75
75
78
79
79
80
80
8 Zusammenfassung
84
8.1 Erkenntnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.2 Vorteile des Ansatzes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.3 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Index
89
Literaturverzeichnis
90
v
Inhaltsverzeichnis
vi
1 Einleitung
1.1 Motivation
Die Entwicklung großer betrieblicher Informationssysteme ist nicht nur gekennzeichnet durch fachliche und technische Komplexität, sondern auch durch eine hohe Lebensdauer der Systeme. Deshalb kommen der Wartbarkeit und der Erweiterbarkeit
solcher Systeme eine hohe Bedeutung zu. Diesbezüglich werden zur Zeit hohe Erwartungen an die komponentenorientierte Software-Entwicklung geknüpft.
Einen Schritt in diese Richtung unternimmt die Spezifikation der Enterprise Java Beans (EJB) der Firma Sun Microsystems im Rahmen der J2EE-Gesamtspezifikation
(Java 2 Enterprise Edition). Sie verspricht, durch Komponentenorientierung einen hohen Grad an Wiederverwendbarkeit zu erreichen. Sie verspricht weiterhin, durch den
Einsatz von Applikationsservern für alle technischen Aspekte, wie z. B. Datenbankzugriff, Ressourcenverwaltung und Client-Server-Verbindungen, transparente Mechanismen anzubieten, welche es den Entwicklern ermöglichen sollen, sich voll auf die
fachliche Komplexität zu konzentrieren.
Die J2EE-Architektur ist zwar noch recht jung (erste Produkte erschienen 1997), aber
da die Verbreitung von Java als Programmiersprache und Plattform auch im Serverbereich deutlich zugenommen hat, lohnt es sich auf jeden Fall, die Praxistauglichkeit
auch für den Einsatz in großen Software-Projekten zu untersuchen.
Diese Diplomarbeit wurde erstellt in Zusammenarbeit mit der sd&m Research GmbH.
Die sd&m AG erstellt individuelle betriebliche Informationssysteme und verfügt über
Erfahrung mit großen Projekten für geschäftskritische Anwendungen mit den verschiedensten Hard- und Software-Plattformen, Programmiersprachen, Datenbanken,
Middleware, Applikationsservern etc. Die sd&m Research GmbH ist die F&E-Einrichtung von sd&m. Hier werden neue Technologien daraufhin untersucht, wie sie in
sd&m-Projekten eingesetzt werden können. Wissen und Erfahrungen aus den Projekten wird gesammelt und so aufbereitet, dass es für weitere Projekte verfügbar
wird.
So konnte diese Diplomarbeit von den vorhandenen Projekterfahrungen mit EJBApplikationsservern profitieren. Vorhandene Lösungen konnten im Detail betrachtet
und kritisch bewertet werden. Die auf diese Weise gesammelten Erfahrungen wurden
in ein beispielhaftes Projekt übernommen; dieses Beispielprojekt wird Gegenstand
der praktischen Betrachtungen sein.
1
1 Einleitung
1.2 Aufgabenstellung
Ein spezieller Aspekt, der in jedem betrieblichen Informationssystem zu lösen ist,
ist die Persistenz von Komponenten (und nicht nur von Klassen) in typischerweise
relationalen Datenbanken. In dieser Arbeit sollen die Möglichkeiten untersucht werden, die in EJB-Anwendungen zur Verfügung stehen. EJB bringt einen PersistenzMechanismus mit, der aber genau geprüft werden muss und nicht die einzige Möglichkeit darstellt. Insbesondere bei komplexen Datenstrukturen stößt er schnell an
seine Grenzen.
Es müssen auch Aspekte wie das Zusammenspiel mit vorhandenen Nachbar- oder Altsystemen, die Arbeit auf einem vorgegebenen Datenbankschema oder das Verhalten
bei hochgradig nebenläufigem Zugriff in die Überlegungen mit einbezogen werden.
Diese Diplomarbeit untersucht verschiedene Möglichkeiten, das Problem der Persistenz von Komponenten auf der Architekturebene zu behandeln. Ziel ist es, belastbare
Designvorschläge zu erarbeiten, die es ermöglichen,
• effizient auf eine Datenbank zuzugreifen und komplexe Daten einer Komponente
zu speichern,
• wartbare Systeme zu erhalten und
• von der aktuellen EJB-Spezifikation und den Eigenschaften eines Produktes
weitgehend unabhängig zu sein.
1.3 Überblick über die Arbeit
Kapitel 2 setzt sich mit den Charakteristika betrieblicher Informationssysteme auseinander, stellt Ideen zum komponentenorientierten Softwareentwurf vor und beschreibt
eine allgemeine Architektur für Informationssysteme. In Kapitel 3 werden die Probleme aufgezeigt, die in jedem objektorientierten Informationssystem auftreten, das
auf eine relationale Datenbank zugreift. Kapitel 4 beschreibt die EJB-Architektur in
der Version 1.1 und gibt einen Ausblick auf die kommende Version 2.0.
Kapitel 5 stellt den naiven Einsatz der EJB-Mechanismen zur Persistenz vor und setzt
sich kritisch mit ihnen auseinander. Es zeigt weitere Möglichkeiten, aus einer objektorientierten Anwendung auf eine relationale Datenbank zuzugreifen. Kapitel 6 stellt
eine Architektur vor, welche alle gewünschten Dienste von EJB in Anspruch nimmt,
gleichzeitig aber durch den Einsatz eines neutralen Interfaces möglichst unabhängig
von der aktuellen Spezifikation und von bestimmten Produkten bleibt. Anhand eines
Praxisbeispiels wird in Kapitel 7 diese Architektur mit zwei unabhängigen Implementierungen vorgestellt. Diese Implementierungen haben ihren Ursprung in realen
Projekten der sd&m-AG und haben sich im produktiven Einsatz bereits bewährt.
2
1.3 Überblick über die Arbeit
Sie werden hinsichtlich ihrer Entwurfs-Qualität und ihrer Performanz verglichen. Der
Ausblick nennt einerseits weitere mögliche Varianten der Implementierung und der
Architektur. Andererseits soll die Entwicklung der Applikationsserver-Technologie
insgesamt gewürdigt werden, um nicht den Blick auf J2EE zu verengen, sondern
auch zukünftige Entwicklungen realistisch einschätzen zu können.
3
2 Architektur betrieblicher
Informationssysteme
2.1 Charakteristika betrieblicher Informationssysteme
Betriebliche Informationssysteme zeichnen sich aus durch folgende Eigenschaften aus:
• Hohe Datenmengen
• Hohe Client-Lasten
• Anforderungen an Ausfallsicherheit: Sonst liegt der ganze Betrieb lahm.
• Anforderungen an Wartbarkeit, Änderungsfreundlichkeit und Erweiterbarkeit:
Typischerweise haben betriebliche Informationssysteme eine sehr hohe Lebensdauer.1
• Verteilte Systeme: Kopplung von heterogenen Systemen. Häufig wird die Verarbeitung verteilt auf Host-Rechner, Applikationsserver und Clients, die jeweils
mit unterschiedlichen Systemen arbeiten.
Diese Eigenschaften stellen besondere technische Herausforderungen an den Entwurf
eines solchen Systems.
2.2 Komponentenorientierte Software-Entwicklung
Ziel des Software Engineering ist es, die Übersichtlichkeit, Wartbarkeit und Wiederverwendbarkeit von (Teil-) Systemen zu erhalten, auch wenn die Funktionalität
immer komplexer wird. Große Hoffnungen hat hier die Objektorientierung mit der
Einführung von Geheimnisprinzip, Schnittstellenkonzept und Vererbung geweckt. Es
hat sich aber gezeigt, dass Klassen zu klein sind zur Strukturierung großer Systeme.
Anforderungen an ein System ändern sich immer wieder und erfordern kontinuierliche Pflege; zunehmende Vernetzung erfordert Wiederverwendbarkeit auch über technische Grenzen wie Betriebssysteme und Programmiersprachen hinweg. Aus diesen
Forderungen ist der Begriff der Komponente entstanden.
1
4
Projekte der sd&m AG sind z. T. 10 Jahre und länger im Einsatz.
2.2 Komponentenorientierte Software-Entwicklung
2.2.1 Eigenschaften einer Komponente
Was genau ist unter einer Komponente zu verstehen? Es existiert keine einheitliche
Definition für Komponenten; es gibt Erklärungen dafür, was man von einer Komponente erwarten darf.
Eine häufig zitierte Definition stammt von Szyperski [Szy99, S. 34]:
• A software component is a unit of composition with contractually specified
”
interfaces and explicit dependencies only.
• A software component can be deployed independently and
• is subject to composition by third parties.“
Eine weitere Definition findet sich in [Si01] von Siedersleben et al.:
• Sie ist eine sinnvolle Einheit des Entwurfs, der Implementierung und damit
”
der Planung.
• Sie implementiert eine oder mehrere Schnittstellen. (Hintergrund ist hier z. B.
eine Komponente, die dem Administrator eine andere Sicht zeigt als dem nor”
malen“ Anwender, Anm. d. Autors)
• Die Implementierung ist austauschbar.“
2.2.2 Entwurf von Komponenten
Szyperski stellt sich Komponenten vor, die binär ausgeliefert werden und sofort verwendet werden können. Sie müssen konfigurierbar sein, um das Zusammenspiel mit
anderen Komponenten festzulegen. Das setzt einen sogenannten deployment-Vorgang
voraus, der entweder auf einem Applikationsserver stattfindet oder auch auf Betriebssystemebene geschehen kann. Siedersleben et al. hingegen setzen auf Quelltextebene
an, um Softwaresysteme sauber zu strukturieren; sie zielen damit auf die Wartbarkeit
von Systemen ab.
In beiden Fällen sind die zentralen Fragen des komponentenorienterten Designs:
• Welche Teile des Systems können geeignet in einer Komponente zusammengefasst werden? Was ist eine passende Größe (häufig findet sich hier der Begriff
Granularität) für eine Komponente? Die Forderung an Komponenten, nur sehr
schmale Schnittstellen und nur minimale Abhängigkeiten zu anderen Komponenten zu haben, führt – ins Extrem getrieben – entweder zu trivialen Komponenten oder zu Monolithen.
5
2 Architektur betrieblicher Informationssysteme
• Wie sieht eine gute Schnittstelle für eine Komponente aus? Welche Beziehungen bestehen zu anderen Komponenten? Das beschreibt die Außensicht auf die
Komponente.
• Wie wird die Komponente intern aufgebaut? Das ist die Innensicht der Komponente. Während die Außensicht konstant bleibt, kann die Innensicht einer
Komponente geändert oder die Implementierung ausgetauscht werden.
Siedersleben et al. bringen noch einen weiteren wichtigen Aspekt mit ein: Die Variabilitätsanalyse. Sie beschreibt, für welche künftigen Änderungen die Komponente
vorbereitet ist und für welche nicht. Sie belegt die immer postulierte und selten untermauerte Änderungsfreundlichkeit von Softwaresystemen.
2.2.3 Abgrenzung zur objektorientierten Software-Entwicklung
Doch was unterscheidet den komponentenorientierten Ansatz vom objektorientierten
Ansatz? Auch beim Entwurf von Klassen steht die Frage nach der Schnittstelle im
Mittelpunkt; es geht um das Geheimnisprinzip und die Unterscheidung von Innenund Außensicht.
Objektorientierung strebt Wiederverwendbarkeit über Vererbung an. Das Paradigma
lautet: Nimm, was schon da ist und erbe davon, so dass es ganz genau für deine
”
Zwecke einsetzbar ist.“ Dieser Ansatz führt zu relativ komplexen Frameworks und
großen Klassenbibliotheken, die zwar einerseits sehr mächtig sind, andererseits aber
nur schwer zu beherrschen sind, da zu viel Wissen über die internen Strukturen und
Abhängigkeiten verlangt wird. Der Einarbeitungsaufwand ist in der Regel hoch.
Der komponentenorientierte Ansatz stellt fest, dass Wiederverwendung deshalb zu
teuer werden kann. Daher kommt zu der nicht ganz neuen Forderung nach schmalen Schnittstellen noch die nach geringen und genau definierten Abhängigkeiten von
anderen Komponenten hinzu. Wie im vorherigen Abschnitt erläutert, kann diese
Forderung auf binär auslieferbare oder auch auf quelltextorientierte Komponenten
angewandt werden. Erst dann können Komponenten nach dem Lego-Prinzip“ zu”
sammengebaut werden.
Komponentenorientierter und objektorientierter Ansatz sind keine gegensätzlichen
Ideen, sondern betrachten Wiederverwendung und die Zerlegung von Programmen
auf unterschiedlichen Ebenen und ergänzen sich: Objektorientierung ist der Weg,
Komponenten zu programmieren.
2.3 Quasar als komponentenorientierte Architektur
Eines der Forschungsprojekte bei sd&m Research ist die Qualitäts-Software-Archi”
tektur“ ( Quasar“). Quasar setzt dabei auf drei Ebenen an:
”
6
2.3 Quasar als komponentenorientierte Architektur
1. Quasar formuliert eine Reihe von Prinzipien, die sich bei der Architektur bewährt haben und die immer wieder eingesetzt werden können. Man könnte hier
auch von Architektur-Patterns“ sprechen.
”
2. Quasar spricht von Komponenten. Der Begriff mag in mancherlei Hinsicht unscharf definiert sein – sicher ist aber, dass Komponenten über schmale Schnittstellen verfügen. Quasar definiert solche Schnittstellen für Komponenten. Wichtig ist hierbei, dass die Schnittstellen auf Langlebigkeit hin entworfen wurden
und die Implementierung austauschbar bleibt.
3. Für diese Schnittstellen existieren konkrete Implementierungen, die in Projekten direkt verwendet werden können.
Aus dieser Herangehensweise wird die Herkunft von Quasar deutlich: Die Ideen stammen aus der Entwicklung von großen Softwaresystemen mit langer Lebensdauer. Es
fließen daher neben dem Software Engineering auch Erfahrungen aus dem Projektmanagement mit ein.
2.3.1 Softwarekategorien
Dijkstra stellt die Forderung nach Separation of concerns auf [Dij76]; Parnas formuliert in [Par72] Kriterien zur Modularisierung von Programmen: Software, die sich
unterschiedlich schnell ändert, wird in unterschiedliche Module aufgeteilt.
In [Si01] werden diese Ideen aufgegriffen, um eine Klassifikation von Softwarebausteinen zu entwickeln. Vorteil dieser Einteilung ist: Die Prinzipien sind zwar altbekannt,
aber diese sehr griffige Formulierung ist doch neu und hilfreich: Bestehender bzw.
geplanter Programmcode lässt sich ganz einfach anhand dieser Kriterien überprüfen,
ob er leicht oder schwer wartbar sein wird.
Informationsysteme befassen sich einerseits mit der fachlichen Anwendung und bauen
andererseits auf einer technischen Basis auf, denn sie können im luftleeren Raum nicht
laufen. Jeder Baustein lässt sich daher einer der vier folgenden Kategorien zuordnen.
Er kann sein:
• unabhängig von Anwendung und Technik,
• bestimmt durch die Anwendung, aber unabhängig von der Technik,
• unabhängig von der Anwendung, aber bestimmt durch die Technik oder
• bestimmt sowohl durch Anwendung als auch durch Technik.
Anwendungsbestimmter Code kennt Begrife wie Kunde“, Auftrag“ oder Rech”
”
”
nung“. Technikbestimmter Code kennt (mindestens) ein technisches API wie z. B.
7
2 Architektur betrieblicher Informationssysteme
JDBC oder OCI. Abkürzend wird anwendungsbestimmter Code mit A“ bezeichnet,
”
technikgetriebener Code mit T“, neutraler Code mit 0“ und Bausteine, die sowohl
”
”
von Anwendung als auch von Technik bestimmt sind, mit AT“. Zusätzlich erhält man
”
die Kategorie R“, die sich mit der externen Repräsentation von Objekten befasst.
”
0-Software ist ideal wiederverwendbar, für sich alleine aber ohne Nutzen. Klassenbibliotheken, die sich mit Strings und Behältern befassen (z. B. in Java die Klassen im
Paket java.util), sind Beispiele für 0-Software.
A-Software kann immer dann wiederverwendet werden, wenn vorhandene Anwendungslogik ganz oder teilweise benötigt wird.
T-Software kann immer dann wiederverwendet werden, wenn ein neues System dieselbe technische Komponente einsetzt (JDBC, ODBC, MFC, CICS).
AT-Software befasst sich mit Technik und Anwendung zugleich. Sie ist schwer zu
warten, widersetzt sich Änderungen, kann kaum wiederverwendet werden und ist
daher zu vermeiden, es sei denn, es handelt sich um R-Software.
R-Software ist eine milde Art von AT-Software. Sie befasst sich mit der Transformation zwischen fachlichen Objekten und externen Repräsentationen (Zeilen einer
Datenbank-Tabelle, ein Bildschirmformat, XML). R-Software kann z. B. mit Hilfe
von awk - oder perl -Skripts generiert werden.
Bei der Kombination von Software-Bausteinen gelten einfache Regeln: 0-Software
kann ohne Einfluss auf die Kategorie zu A oder T hinzugefügt werden. Kombination
von A mit A liefert A, und T mit T liefert T; A mit T liefert AT oder im günstigsten
Fall R, wenn die Software nur zur Repräsentation dient und stereotypen Charakter
hat.
Die Entscheidung, welcher Code als T-Software und welcher – ähnlich der gewählten
Programmiersprache – als Basisfunktionalität und damit 0-Software behandelt werden sollte, ist häufig nur im praktischen Kontext zu entscheiden. So kann z. B. eine
Schnittstelle, die einen allgemeinen Zugang zu einer Datenbankverwaltung ermöglicht, als T-Software betrachtet werden, weil sie von datenbanktechnischen Begriffen
wie Transaktionen und Operationen wie Insert/Delete/Update spricht. Sie kann aber
auch als 0-Software bezeichnet werden, da sie die spezielle Datenbank-Technik verbirgt; wichtig ist dabei die Austauschbarkeit der Implementierung.
In der Praxis gilt: Jede Basis-Software, die potentiell bei einer späteren Portierung
oder Migration nicht mehr zur Verfügung steht oder ersetzt werden soll, sollte gekapselt werden. Ein weiteres Argument für eine Kapselung technischer APIs ist eine
Vereinfachung der Nutzungsschnittstelle. Eine Kapselung hinter einer abgespeckten
Schnittstelle ermöglicht ggf. eine normierte Nutzung und eine geringere Einarbeitungszeit als die direkte Nutzung einer komplexen T-Software.
8
2.3 Quasar als komponentenorientierte Architektur
2.3.2 Elemente von Quasar
Wie zu Beginn dieses Abschnittes erwähnt, zeigt Quasar, wie eine Komponentenbildung mit den Zielen schmale, aber aussagekräftige Schnittstellen und saubere Kapselung von technischen Abhängigkeiten aussehen kann.
Nach diesen Prinzipien wurde das Quasar Database Interface, QDI entwickelt. Es
existieren einerseits die Schnittstelle und andererseits zwei Implementierungen (eine
Dummy-Implementierung und eine auf JDBC aufbauende, für Oracle-, für MySQLund für ODBC-Datenbanken einsetzbare). Die Schnittstelle beschreibt ein sogenanntes Workspace-Konzept, mit dem transaktionsorientiert jeweils mit einer Menge von
Objekten gearbeitet werden kann, und das zu jedem Zeitpunkt eine konsistente Sicht
auf die verwalteten Objekte ermöglicht. Das QDI wird in Abschnitt 5.2.2 genauer
beschrieben.
Daneben existieren noch Ideen und Implementierungen für GUI-Darstellungen (QUI,
Quasar User Interface) und eine Sammlung nützlicher Datentypen (QDT, Quasar
Data Types).
2.3.3 Kritik/Einordnung
Um die Quasar-Ideen nutzbringend einsetzen zu können, ist eine kritische Betrachtung notwendig:
Quasar ist kein Framework – diese Feststellung ist wertfrei, es ist weder ein Vornoch ein Nachteil. Gamma2 kommentiert zum Thema Frameworks: Frameworks sind
”
gut, solange man nicht gegen sie programmiert. Wenn man gegen ein Framework
programmiert, wird man diesen Kampf verlieren.“
Der in Quasar verwendete Komponentenbegriff arbeitet auf Quelltextebene und ist
nützlich, um eine wohlstrukturierte Implementierung zu erstellen, die den Forderungen nach Wartbarkeit durch A/T-Trennung und durch schmale Schnittstellen
sowie durch Austauschbarkeit von Implementierungen gerecht wird. Er entspricht
aber nicht den Ideen, die z. B. Szyperski in [Szy99] zugrunde legt (s. Abschnitt 2.2):
Quasar-Komponenten sind nicht binär auslieferbar und nicht konfigurierbar.
Sie haben (bewusst) keinen Produkt-Charakter; vielmehr zielen sie darauf ab, für
Projekte die richtigen Komponentenschnitte vorzuschlagen. Die Implementierung des
QDI ist zur Zeit am weitesten gediehen und wird auch in produktiven Projekten eingesetzt. Dennoch wird es nicht im Sinne eines Produktes oder vollständigen Werkzeuges
gepflegt, sondern als ausgereifter proof of concept betrachtet.
2
auf der sd&m-Konferenz 2001 in Bonn
9
2 Architektur betrieblicher Informationssysteme
2.4 Aufbau einer Standard-Architektur betrieblicher
Informationssysteme
Abbildung 2.1 stellt die Komponenten eines betrieblichen Informationssystems dar,
die von der Spezifikation motiviert werden. Sie sind also nur durch die fachlichen
Anforderungen bestimmt und unabhängig von der verwendeten Technik, sei es in
Bezug auf die Darstellung in der GUI, die zugrundeliegende Netztopologie oder die
eingesetzte Datenbank.
Abbildung 2.1: Grobarchitektur eines betrieblichen Informationssystems
2.4.1 Anwendungs-Komponenten
In Abbildung 2.1 sind drei Anwendungs-Komponenten (A-Komponenten) dargestellt:
Das Bestellwesen, die Kundenverwaltung und die Auftragsverwaltung. Sie bestehen
jeweils aus Anwendungs-Fällen, Anwendungs-Entitäten und Anwendungs-EntitätenVerwaltern, die im Folgenden beschrieben werden.
2.4.2 Anwendungs-Fälle
Anwendungs-Fälle (A-Fälle, AWF) sind die Use Cases von UML. Sie legen fest, welche
Operationen der Anwendungskern zur Verfügung stellt. A-Fälle werden im Dialog,
im Batch und von Nachbarsystemen gerufen. In Abbildung 2.1 sind das die A-Fälle
Bestellung bearbeiten, Bestellung aufnehmen, Kundenliste bearbeiten, Kunde suchen
und bearbeiten, Auftrag anlegen und bearbeiten.
10
2.4 Aufbau einer Standard-Architektur betrieblicher Informationssysteme
A-Fälle als Schnittstelle der A-Komponente
Die A-Fälle sind der Zugang zur Anwendung. Die Gesamtmenge der A-Fälle einer
A-Komponente definieren, was die Komponente leistet, und zwar ohne Bezug auf den
konkreten Nutzer: Dem A-Fall ist es egal, ob seine Daten am Bildschirm angezeigt
oder in ein Nachbarsystem übertragen werden. Nur so ist gewährleistet, dass ein und
derselbe A-Fall im Batch und in verschiedenen Dialogen verwendbar ist.
A-Fälle garantieren den sachgemäßen Zugriff auf A-Entitäten und A-Verwalter. So
können z. B. Vorbedingungen geprüft werden, bevor bestimmte Operationen auf AEntitäten durchgeführt werden. Wenn solche Prüfungen nichttrivial sind, ist es wichtig, sie an genau einer Stelle auszuprogrammieren, und dafür sind A-Fälle der richtige
Ort.
2.4.3 Anwendungs-Entitäten
Anwendungs-Entitäten (A-Entitäten, AE) sind die Pfeiler, an denen eine Anwendung
hochgezogen wird: Im Beispiel sind das Kunde und Auftrag (vgl. [BrSi00, S. 137ff]).
A-Entitäten werden beschrieben durch Attribute, wobei jedes Attribut entweder elementar ist (String, Integer, ..) oder zu einem fachlichen Datentyp (A-Datentyp) gehört. A-Entitäten besitzen in der Regel die üblichen get- und set-Operationen sowie Instanz-Operationen wie z. B. kunde.addiereUmsätze oder auftrag.stornieren. Sie
haben einen Lebenszyklus, der in der Regel durch ein Zustandsmodell beschrieben
wird. Die UML-Begriffe Assoziation und Aggregation gelten für A-Entitäten genauso. A-Entitäten sind persistent. Sie können in einer oder mehreren Datenbanken,
Nachbarsystemen oder auch flachen Dateien liegen.
Der Entwurf von A-Entitäten wird weder von Klassenstrukturen noch von Datenbankmodellen geleitet, sondern ist rein fachlich motiviert. Sie entstehen mit der fachlichen Spezifikation, noch bevor das technische Konzept festgelegt wird.
Verwandte Begriffe
Die Begriffe Business Object und Geschäftsobjekt bedeuten je nach Autor mehr oder
weniger dasselbe wie A-Entität. Im Interesse einer einheitlichen Begriffsführung wird
in diesem Dokument der Begriff A-Entität verwendet.
2.4.4 Anwendungs-Entitäten-Verwalter
Operationen, die man keiner einzelnen Instanz zuordnen kann, werden VerwalterOperationen genannt. Die Anwendungs-Entitäten-Verwalter (A-Verwalter, AV) implementieren diese Operationen. Jeder verwaltet mindestens eine A-Entität, aber oft
11
2 Architektur betrieblicher Informationssysteme
macht es Sinn, einen A-Verwalter für die ganze A-Komponente vorzusehen. Beispiele
für Verwalter-Operationen sind z. B.:
• Operationen zum Erzeugen von Instanzen,
• Operationen, die alle Instanzen einer A-Entität betreffen,
• Suche nach unterschiedlichen Kriterien (Abfragen, Queries)
Dazu braucht man in der Regel Verwalter-Daten, z. B.:
• Anzahl der vorhandenen Instanzen
• Summen von Attributwerten
• Konstanten, die für alle Instanzen gelten (z. B. Default-Werte).
A-Verwalter haben die Aufgabe, Abfragen an die Datenbank anzustoßen. Die Umsetzung der Anfragen erfolgt in der Regel in einer Zugriffsschicht. Datenbankabfragen
stellen oft einen Flaschenhals bezüglich der Performance einer Anwendung dar; AVerwalter übernehmen den Teil der Abfrage-Optimierung, der nur unter Kenntnis
der fachlichen Anforderung und der fachlichen Semantik durchgeführt werden kann.
Hier ist als Erstes die Entscheidung zwischen lazy und eager loading anzuführen.
2.5 Zusammenfassung
Dieses Kapitel hat auf einer sehr allgemeinem Ebene betriebliche Informationssysteme charakterisiert und eine Architektur vorgestellt, die sich an den fachlichen Anforderungen orientiert. Es hat einen Komponentenbegriff eingeführt, der in den folgenden Kapiteln verwendet wird.
Ein betriebliches Informationssystem benötigt neben den anwendungsorientierten
Elementen eine technische Grundlage, um Datenbankzugriff, Anbindung an Nachbarsysteme und Implementierung der GUI zu realisieren. Mit der Umsetzung dieser
allgemeinen Architektur mit Hilfe eines EJB-Applikationsservers beschäftigen sich
die folgenden Kapitel.
12
3 Standardprobleme beim
Datenbankzugriff
Betriebliche Informationssysteme, auch wenn sie individuell für die speziellen Anforderungen eines Kunden entworfen werden, müssen sich mit Problemen beschäftigen,
die immer wieder in ähnlicher Form auftreten. Der Zugriff auf Datenbanken ist ein
solches Problem.
Bei der Verbindung einer objektorientiert programmierten Anwendung mit einer relationalen Datenbank ist der sogenannte objekt-relationale Paradigmenbruch (impedence mismatch) zu überwinden: Unter anderem müssen folgende Fragen beantwortet werden: Wie werden die Daten von der objektorientierten Darstellung ins relationale Datenbankschema abgebildet? Und wie werden Änderungen im Objektgeflecht
(Transaktionen auf Ebene der Anwendungslogik) auf Datenbanktransaktionen abgebildet?
3.1 Objekt-relationale Abbildung
Der sogenannten objekt-relationalen Abbildung (OR-Mapping) kommt eine größere
Bedeutung zu als die bloße Abbildung von Klassen auf Tabellen und von Attributen
auf Spalten. Die Objektidentität, die korrekte Abbildung von Beziehungen zwischen
Objekten und die Vererbung seien hier als wichtigste Schwierigkeiten genannt.
3.1.1 Abbildung von Attributen
Attribute mit Elementardatentypen (Zahlen, Strings, Datum- oder Zeitangaben) werden direkt oder mit einfacher Datentyp-Umwandlung auf Spalten abgebildet. Doch
für abhängige Objekte mit komplexeren Attribut-Datentypen gibt es schon mehrere
Möglichkeiten. Meist werden sie, mit Fremdschlüssel versehen, in eine eigene Tabelle
geschrieben. Man könnte sie auch en bloc als BLOB“ (binary large object) in ei”
ne Tabellenspalte schreiben oder – bei bekannter Anzahl – die vorhandene Tabelle
erweitern; solche Lösungen sind aber eher als Notnagel“ zu bezeichnen. Sie werden
”
hier dennoch erwähnt, weil sie in der Praxis überraschend häufig angetroffen werden.
13
3 Standardprobleme beim Datenbankzugriff
3.1.2 Abbildung von Beziehungen zwischen Objekten
Es ist zu unterscheiden zwischen 1:1, 1:n und n:m-Beziehungen. Bei 1:n und m:nBeziehungen kann die Kardinalität entweder variabel oder fest sein. Weiterhin ist
zwischen Komposition und Assoziation zu unterscheiden. Bei Komposition sind die
enthaltenen Objekte abhängig; z. B. zieht das Löschen eines Objektes das Löschen der
abhängigen Objekte nach sich. Assoziierte Objekte können unabhängig voneinander
existieren.
Typischer Fall für eine 1:n-Beziehung ist ein Objekt, das eine Reihe von abhängigen
Objekten enthält, z. B. in Form eines Containers. Die Abbildung auf die Datenbank
wird meist mit zwei Tabellen arbeiten, wobei die Tabelle für die abhängigen Objekte
einen Fremdschlüssel enthält, um auf den entsprechenden Datensatz in der ersten Tabelle zu verweisen. Man beachte, dass in diesem Fall die Verweisrichtung“ umgekehrt
”
zum OO-Modell ist.
3.1.3 Objektidentität
Es wird auf drei Ebenen von Identität gesprochen: Die fachliche Ebene, die Sicht des
OO-Modells sowie die Darstellung im relationalen Datenmodell. Die Wahrung der
Identität muss konsistent durch alle drei Ebenen erfolgen.
Die fachliche Identität ist in der Regel durch fachliche Attribute gegeben. Ein Objekt
hat als eindeutige Identität die Referenz. Die Identität ist von den Attributwerten
unabhängig; sie bleibt erhalten, auch wenn sich die Attributwerte ändern. In einer relationalen Datenbank wird die Identität durch das Konzept der Primärschlüssel gesichert. Eine Tabellenzeile wird durch den Primärschlüssel, der aus einer oder mehreren
Spalten gebildet wird, eindeutig gekennzeichnet.
Für die Wahl des Primärschlüssels in der Datenbank und die Objekt-Identität im OOModell gibt es die Möglichkeit, entweder die Anwendungsdaten oder einen künstlich
erzeugten Primärschlüssel zu verwenden. Die erstgenannte Variante hat den Vorteil,
dass keine zusätzlichen Daten eingeführt werden. Die zweite ist jedoch meist einfacher
zu handhaben, z. B. wenn sich der fachliche Schlüssel über mehrere Spalten erstreckt
oder sich im Laufe der Zeit womöglich die Anforderungen an das System ändern.
3.1.4 Vererbung
Relationale Datenbanken kennen das Konzept der Vererbung nicht. Betrachten wir
das Beispiel in Abbildung 3.1: Eine Person kann in den Ausprägungen Angestellter
oder Kunde auftreten. Die Klasse für Personen enthält einige Attribute, die Angestellten und Kunden gemein sind, und die abgeleiteten Klassen für Angestellte und
Kunden enthalten weitere, spezielle Attribute. Für die Abbildung auf eine Tabellenstruktur gibt es drei Möglichkeiten:
14
3.1 Objekt-relationale Abbildung
1. Ein-Vererbungsbaum-eine-Tabelle: Eine Tabelle für Personen enthält die Attribute für Personen, die zusätzlichen Attribute für Angestellte und für Kunden
sowie ein Typfeld zur Unterscheidung zwischen Angestellten und Kunden.
2. Ein-Vererbungspfad-eine-Tabelle: Für jede konkrete Klasse existiert eine Tabelle. Die Attribute der abstrakten Klasse Person werden sowohl in der Tabelle
für Angestellte als auch in der für Kunden gespeichert.
3. Eine-Klasse-eine-Tabelle: Für jede Klasse existiert eine Tabelle, die jeweils die
dort vereinbarten Attribute aufnimmt. Die Tabellen für die Oberklasse enthalten außerdem ein Typfeld, die Tabellen für die Subklassen enthalten jeweils den
Primärschlüssel und die zusätzlichen Attribute.
Abbildung 3.1: Abbildung von Vererbung
Beim Entwurf einer Tabellenstruktur müssen die zum Teil gegensätzlichen Ziele
• Speicherplatzoptimierung,
• Geschwindigkeit der Datenbankabfragen,
• Zahl der benötigten Datenbankzugriffe zum Aufbau eines Objektes,
• Einfachheit der Tabellenstruktur bzw. möglichst klare Abbildung des modellierten Realweltausschnitts und
• Einfachheit der Abfragen
15
3 Standardprobleme beim Datenbankzugriff
gegeneinander abgewägt werden. Jede dieser drei Varianten hat Vor- und Nachteile
bezüglich dieser Ziele.
Mit diesem Handwerkszeug“ können auch komplexe Strukturen bzw. Objektgeflechte
”
abgebildet werden.
3.2 Transaktionen
Um Inkonsistenzen beim parallelen Zugriff zweier Clients auf A-Entitäten zu vermeiden, werden Zugriffe in Transaktionen durchgeführt. Ziel ist die Isolation eines
Vorganges mit definiertem Anfangs- und Endpunkt, der entweder komplett oder gar
nicht ausgeführt wird, um jeweils einen konsistenten Datenstand zu erhalten. Man
nennt dies die ACID-Eigenschaft einer Transaktion:
• Atomicity: Sie ist Atomar, also nicht mehr teilbar. Entweder alle Aktionen oder
keine werden ausgeführt.
• Consistency: Vor Beginn und nach Abschluss sind die Daten in einem konsistenten Zustand.
• Isolation: Transaktionen laufen unabhängig oder isoliert voneinander ab.
• Durability: Nach erfolgreichem Abschluss einer Transaktion sind die Änderungen im Datenbestand dauerhaft.
Eine Transaktion wird entweder mit rollback abgebrochen, d. h. dass alle Änderungen,
die innerhalb der Transaktion angestoßen wurde, verworfen werden, oder sie wird mit
commit abgeschlossen mit dem Ziel, alle Änderungen dauerhaft zu übernehmen. Je
nach Transaktionsstrategie kann dies immer durchgesetzt werden oder auch wegen
einer schneller abgeschlossenen Änderung einer Transaktion eines anderen Clients
scheitern.
3.2.1 Transaktionsstrategien
Zur Implementierung von Transaktionen werden Transaktionsstrategien verwendet.
Diese verwenden Sperren, sie werden daher auch als Sperrstrategien bezeichnet.
Transaktionsstrategien können auf zwei Wegen implementiert werden: pessimistisch
oder optimistisch [Sla99, Kapitel 13].
16
3.2 Transaktionen
Pessimistische Transaktionsstrategie
Die pessimistische Transaktionsstrategie sperrt während einer Transaktion alle Daten, auf die lesend oder schreibend zugegriffen wird. Die Sperren bestehen bis zum
Transaktionsende. Will eine zweite Transaktion auf die selben Daten zugreifen, wird
sie blockiert. Erst wenn die erste Transaktion ihre Daten geschrieben hat, kann die
zweite Transaktion die Daten lesen. Die pessimistische Transaktionsstrategie wird in
der Regel mit Sperren auf Datenbankebene implementiert.
Optimistische Transaktionsstrategie
Die optimistische Strategie arbeitet ohne Sperren. Sie erkennt Konflikte über eine
Validierungsphase. Die erste Transaktion, die Daten schreibt, gewinnt. Transaktionen, die danach die selben Daten schreiben wollen, werden abgebrochen (sie müssen
dann die Daten erneut lesen und ändern).
Die optimistische Transaktionsstrategie arbeitet in drei Phasen. Die letzten beiden
Phasen fallen häufig zusammen:
1. Lesephase: Daten werden geladen, ohne sie zu sperren. Der Zugriff erfolgt nur
lesend. Diese Daten liegen im Applikationsserver oder Client vor und können
dort geändert werden.
2. Validierungsphase: Vor dem Schreiben der Daten werden potentielle Konflikte mit anderen Transaktionen festgestellt. Dazu wird eine darunter liegende
Transaktion gestartet, und die Daten werden erneut geladen. Über einen Vergleich der Änderungen mit den geladenen Daten werden Konflikte erkannt. Bei
einem Konflikt wird die Transaktion mit einem Rollback abgebrochen.
3. Schreibphase: In der Validierungsphase wurden keine Konflikte erkannt, die
darunter liegende Transaktion ist noch offen. Alle Daten stehen im aktuellen
Zustand zur Verfügung, die Änderungen werden geschrieben und die darunter
liegende Transaktion wird mit commit bestätigt. Die darunter liegende Transaktion kann ihrerseits wieder optimistisch oder pessimistisch arbeiten.
Auswirkungen auf den Entwurf der Anwendung
Transaktionssstrategien sind ein weiteres Beispiel dafür, dass zwar die Eigenschaften
der Datenbank verborgen bleiben, nicht jedoch das Problem des parallelen Zugriffs an
sich: Bei Verwendung der pessimistischen Strategie muss der Anwendungsprogrammierer damit rechnen, dass seine Anwendung langsamere Antwortzeiten bietet oder
sogar Verklemmungen (Deadlocks) verursachen kann. Setzt er hingegen die optimistische Strategie ein, kann es sein, dass eine Transaktion nicht mit commit abgeschlossen
17
3 Standardprobleme beim Datenbankzugriff
werden kann. Damit muss die Anwendung umgehen können. Der Anwendungsprogrammierer wird zwar von der Programmierung der Transaktionsproblematik entlastet, er kommt aber nicht umhin, mit den Begriffen optimistische und pessimistische
Sperrstrategie umgehen zu können.
3.2.2 Abbildung von Transaktionen auf die Benutzer-Interaktion
Transaktionen tauchen auf jeder Ebene eines Informationssystem auf und müssen
aufeinander abgebildet werden: Für den Benutzer ist es beispielsweise eine Transaktion, ein Dialogfenster am Bildschirm zu öffnen und die eingetragenen Daten mit
dem OK-Knopf zu bestätigen. Auf den verschiedenen Ebenen werden unterschiedliche
Transaktionsbegriffe verwendet. Für die Gestaltung der Schnittstelle eines A-Falles
können vier Fälle unterschieden werden:
Die Einschritt-Transaktion
Ein Dialog kann eine A-Transaktion mit genau einem Aufruf an den A-Fall durchführen. Siehe Abbildung 3.2.
Abbildung 3.2: Einschritt-Transaktion
Die Mehrschritt-Transaktion ohne Benutzereingabe
Ein Dialog kann eine A-Transaktionen nur über mehrere Aufrufe des A-Falls durchführen. Zwischen den Aufrufen findet keine Benutzerinteraktion statt. Die Aufrufe
erfolgen also kurz hintereinander. Abbildung 3.3 stellt dies dar.
Die Mehrschritt-Transaktion mit Benutzereingabe
In komplexeren Dialogen wird, um eine A-Transaktion auszuführen, der A-Fall mehrfach aufgerufen (siehe Abbildung 3.4). Zwischen den Aufrufen findet eine Benut-
18
3.2 Transaktionen
Abbildung 3.3: Mehrschritt-Transaktion ohne Benutzereingabe
zereingabe statt. Diese kann beliebig lange dauern. Diese Transaktionen werden
Mehrschritt-Transaktionen mit Benutzereingabe genannt. Der Benutzer bleibt aber
am Client angemeldet und lässt den selben Dialog offen.
Abbildung 3.4: Mehrschritt-Transaktion mit Benutzereingabe
Die Lange Transaktion
A-Transaktionen, die sich über mehrere Sitzungen des Benutzers erstrecken, werden
lange Transaktionen genannt. Sie können beliebig lange dauern und überdauern auch
den Absturz eines Clients und eines Servers. Sie sind in der Regel persistent. Siehe
Abbildung 3.5.
3.2.3 Implementierung der fachlichen Transaktionen
Für die Umsetzung der fachlichen Transaktion (A-Transaktion) auf die Datenbanktransaktion kann die optimistische oder die pessimistische Strategie gewählt werden.
19
3 Standardprobleme beim Datenbankzugriff
Abbildung 3.5: Mehrschritt-Transaktion mit Benutzereingabe
Prinzipiell sind beide Strategien für alle vier vorgestellten Fälle möglich. Allerdings
ist – je nach Anwendung – nicht jede Kombination sinnvoll, denn der Einsatz der
pessimistischen Strategie führt durch das restriktive Sperrverhalten leicht zu Performanzengpässen, wenn nicht sogar zu Verklemmungen. Andererseits kann sie bei
kurzen Transaktionen sogar performanter sein, da commit-Aufrufe nicht scheitern
können und Transaktionen serialisiert werden.
Die Abbildung der fachlichen Transaktion einerseits zum Benutzer hin und andererseits zur Datenbank hin ist zwar techniscch unabhängig, dennoch empfiehlt sich
für die Praxis, die Wahl der Transaktionsstrategie auf die Abbildung zum Client hin
abzustimmen.
3.3 Performanz
Die Performanz einer Anwendung hängt von der Zahl der Datenbankzugriffe und
von der Menge der gelesenen Daten ab. Es macht einen großen Unterschied, ob viele
Objekte auf einmal gelesen werden können oder ob sie mit vielen separaten Leseoperationen geladen werden. Optimierungsziel ist, die Zahl der Datenbanaufrufe zu
minimieren sowie die pro Aufruf transportierten Daten zu verringern. Falls sich diese
Ziele widersprechen, ist ein geeigneter Kompromiss zu finden oder ein Mechanismus
zu entwickeln, mit dem im Einzelfall die Entscheidung getroffen werden kann. Eine
Auswahl von wichtigen Problemstellungen ist:
• Beim Zugriff auf Objektgeflechte ist zwischen lazy loading“ und eager loading“
”
”
zu entscheiden.
• Datenbank-Updates sollen nur dann ausgeführt werden, wenn die Daten tatsächlich geändert wurden.
20
3.4 Wartung
• Die Entscheidung zwischen optimistischer und pessimistischer Sperrstrategie
ist vor allem bei hoher Clientlast für die Performanz und das Verklemmungsverhalten von großer Bedeutung.
• Die Entscheidung, ob der Applikationsserver zustandslos oder zustandsbehaftet arbeitet, hat auch Einfluss auf die Abbildung der Transaktionen, da ein zustandsloser Applikationsserver nach jedem Aufruf durch einen Client die Transaktion abschließt. Ein zustandsbehafteter Server kann eine Transaktion bis zum
nächsten Aufruf durch den selben Client offen halten.
• In die selbe Richtung zielt auch die Frage, was mit Anfragen mit einer hohen Trefferzahl geschehen soll: Werden alle Treffer an den Client übermittelt?
Wenn die Ergebnisse hingegen portionsweise abgefragt werden, gibt es zur Realisierung auf Serverseite mehrere Möglichkeiten zur Realisierung, die je nach
Einsatzgebiet abzuwägen sind.
• Gleiches trifft auf Client-Anfragen zu, die einen umfangreichen Vorgang anstoßen und asynchron zurückkehren sollen, wie z. B. die Erstellung von großen
Reports.
3.4 Wartung
Ein Informationssystem mit einer langen Lebensdauer wird immer wieder Änderungen ausgesetzt sein. Mal ändern sich fachliche Anforderungen, mal ändern sich
technische Gegebenheiten wie z. B. die Anbindung an Nachbarsysteme. Auch wenn
versucht wird, das Datenbankschema möglichst selten zu ändern, ist es manchmal
unumgänglich. Ebenso können sich notwendige Änderungen an der Klassenstruktur
des Anwendungskerns ergeben.
Die Frage ist, was nach einer solchen Änderung passiert: Muss nach einer Änderung
des Datenbankschemas der gesamte Anwendungskern neu geschrieben werden, oder
reicht eine Anpassung in der sauber gekapselten Zugriffsschicht? Auch die andere
Richtung muss geprüft werden: Ist das Datenbankschema unabhängig genug vom
verwendeten Klassenmodell?
Parnas et al. [PCW83] stellen bezüglich der Änderungsfreundlichkeit von Software
die Forderung: It should be possible to make likely changes without changing any
”
module interfaces; less likely changes may involve interface changes, but only for
modules that are small and not widely used. Only very unlikely changes should
require changes in the interfaces of widely used modules.“
Die Erfüllung dieser Forderung kann nur durch die Architektur des Systems sichergestellt werden. Parnas et al. zeigen, dass das Geheimnisprinzip in diesem Sinne der
Wartbarkeit von Software dient.
21
4 Enterprise Java Beans
Die Programmiersprache Java wurde 1995 von Sun Microsystems vorgestellt und
hat seitdem große Verbreitung erlangt. Sie verdankt ihre Popularität einerseits der
leichten Erlernbarkeit und andererseits den zahlreichen verfügbaren Programmierschnittstellen (Application Programmer Interfaces, APIs), die aus der reinen Programmiersprache einen regelrechte Programmierplattform machen. Durch den Einsatz der sogenannten virtuellen Maschine (VM) bleiben Java-Programme unabhängig
von der eingesetzten Hardware, und mit Hilfe der APIs bleiben sie auch unabhängig
vom verwendeten Betriebssystem.
Ursprünglich wurde Java bekannt durch die Programmierung von Applets, die in
einem Web-Browser ablaufen können, sich also auf der Client-Seite befinden. Mittlerweile hat aber Java auch auf der Server-Seite verteilter Anwendungen Einzug gehalten. Hier spielen vor allem Servlets (bzw. Java Server Pages, JSP) und Enterprise
Java Beans (EJB) eine Schlüsselrolle und setzen sich gegenüber beispielsweise C++Anwendungen immer mehr durch.
Dieses Kapitel beschreibt zunächst die EJB-Spezifikation und die Systemarchitektur,
die sich daraus ergibt. Daran schließt sich eine kritische Bewertung unter den im
vorigen Kapitel eingeführten Kriterien für die Architektur von Informationssystemen
an. Das Kapitel schließt mit einem Ausblick auf die kommende EJB-Version.
Vergleiche zur Darstellung in diesem Kapitel auch [Sch01] und [DePe00].
4.1 Die EJB-Spezifikation
Enterprise Java Beans (EJB) ist ein Bestandteil der Java-2-Plattform, Enterprise
Edition (J2EE), welche die Grundlage für serverseitige Anwendungslogik darstellt.
Es handelt sich dabei nicht um ein Produkt, sondern um eine Spezifikation [Sun99],
die frei verfügbar ist. Sie beschreibt den Aufbau einer 3-Schichten-Architektur (3-tier
architecture) (siehe Abbildung 4.1.)
Für die mittlere Schicht spezifiziert sie einen Applikationsserver, welcher eine Ablaufumgebung für Anwendungs-Komponenten bietet. Die Spezifikation legt fest, wie
die Schnittstellen zwischen den Komponenten untereinander und zwischen Komponenten und dem Applikationsserver aussehen sowie die Leistungen und Dienste, die
der Applikationsserver zur Verfügung stellen muss.
22
4.2 Infrastruktur und Produkte
Abbildung 4.1: 3-Schicht-Architektur mit Applikations-Server
So wird ein Komponentenmodell definiert, das sich Folgendes zum Ziel setzt:
• Anwendungsentwickler benötigen weniger Know-How bezüglich systemnaher
Dienste (z. B. Multithreading, Transaktionssteuerung etc.)
• Unabhängigkeit von der Datenbank
• Damit höhere Produktivität der Anwendungsentwickler
Die EJB-Spezifikation greift ein weiteres Thema auf, das mit der Produktivitiät im
Entwicklungsprozess zusammenhängt. Für die verschiedenen Aufgaben wie Entwicklung der fachlichen Logik, Zusammensetzen der erstellten Komponenten, Wartung
der Basissysteme usw. werden verschiedene Rollen definiert, z. B. der Bean-Provider
für die Erstellung der Enterprise Beans und der Server-/Container-Provider, der den
Applikationsserver herstellt, in dieser Arbeit oft mit Hersteller“ bezeichnet. Für eine
”
vollständige Auflistung siehe [Sun99] oder [DePe00, Kapitel 4].
4.2 Infrastruktur und Produkte
Zur EJB-Architektur gehören der J2EE-Server, der EJB-Container sowie die Beans.
Der Container stellt dabei die Laufzeitumgebung und eine Reihe von Diensten für
die Enterprise Beans dar; der Server wiederum bietet eine Laufzeitumgebung für
Container. In der Regel läuft in jedem Server neben dem EJB-Container noch ein
Container für Servlets/JSPs. Die Spezifikation unterscheidet zwar zwischen Server
und Container, definiert aber keine Schnittstelle zwischen ihnen. Es wird vermutlich angenommen, dass Server und Container immer vom selben Hersteller geliefert
werden. Dies stimmt nur zum Teil (s. Abschnitt 4.4.2).
Dienste des Servers umfassen u. a.:
23
4 Enterprise Java Beans
• das Thread- und Prozessmanagement
• Unterstützung für Lastverteilung und Ausfallsicherheit
• Namens- und Verzeichnisdienst (JNDI-Service)
• Pooling von Betriebssystemressourcen
Der EJB-Container ist verantwortlich für:
• die Kontrolle des Lebenszyklus’ einer Bean
• Instanzen-Pooling und Aktivierung bzw. Passivierung von Beans
• Verteilung
• Persistenz
• Transaktionen
• Sicherheit
4.3 Enterprise-Beans
Während Server und Container sämtliche technischen“ Aufgaben übernehmen sol”
len, sind die Enterprise Beans dazu gedacht, sich vollständig auf die Anwendungslogik
zu konzentrieren. Diese fachliche Logik wird unterteilt in Anwendungsfälle und intelligente Anwendungsobjekte. (In der UML-Sprechweise werden häufig die Begriffe
Geschäftsvorfälle bzw. Geschäftsobjekte verwendet.)
Es gibt zwei Typen von Enterprise Java Beans1 : Session-Beans modellieren die Anwendungsfälle, und Entity-Beans modellieren die Anwendungsobjekte. Daraus ergeben sich entscheidende Unterschiede zwischen den beiden Bean-Arten in Bezug auf
Einsatzgebiete und auf Eigenschaften. Sie werden in Tabelle 4.1 kurz dargestellt und
nachfolgend genauer erklärt.
4.3.1 Aufbau einer Bean
Eine einzelne Enterprise-Bean setzt sich aus mehreren Elementen zusammen, die
zu einem Teil vom Bean-Entwickler geschrieben und zum anderen Teil beim Installations-Vorgang (Deployment) vom Container (oder Container-eigenen Tools) generiert
werden. Die Erstellung der folgenden Bestandteile muss dabei der Entwickler übernehmen:
1
ab Version 2.0 gibt es auch noch Message Driven Beans. Siehe dazu Abschnitt 4.5.
24
4.3 Enterprise-Beans
Manipuliert Daten
Repräsentiert Daten / ist
persistent
Zustandsbehaftet
Zugriff von x Clients
Kennt Transaktionen
Lebensdauer
Überlebt Servercrash
Was tun nach Servercrash?
Session-Bean
Ja
Nein
Entity-Bean
Ja
Ja
Ja / Nein
1
Ja
Eher kurz
Ja
n
Ja
Kann lang sein (wie Daten
in DB)
Ja + impliziter Rollback
Client erhält Exception
beim nächsten Aufruf
Nein
Client baut neue
Verbindug auf
Tabelle 4.1: Wichtige Charakteristika der Enterprise-Beans
• ein Remote-Interface 2 ,
• ein Home-Interface,
• eine Implementierungsklasse (Bean-Klasse) und
• ein Deployment-Deskriptor ;
• eine Primärschlüsselklasse für Entity-Beans ist optional möglich.
Die ersten vier Teile sind für Session- und für Entity-Beans erforderlich. Eine Primärschlüsselklasse muss nur für Entity-Beans erstellt werden, deren Primärschlüssel aus
mehreren Attributen zusammengesetzt ist. Zum Deployment werden die Bestandteile
einer Enterprise-Bean in einer jar-Archivdatei zusammengefasst. Diese Aufgabe kann
auch mit Hilfe von Deployment-Tools erledigt werden, die auch bei der Erstellung
des Deployment-Deskriptors helfen können.
Remote-Interface
Im Remote-Interface werden alle Methoden der Anwendungslogik aufgeführt, die von
der Enterprise-Bean nach außen hin angeboten werden. Das Remote-Interface wird
von einer vom Container (bzw. einem Tool) generierten Klasse implementiert, welche
über eine RMI-Verbindung3 mit dem J2EE-Server kommuniziert und die Methodenaufrufe weitergibt. Auf Serverseite werden die RMI-Aufrufe von einer ebenfalls
generierten Klasse entgegengenommen, die die entsprechenden Methoden der BeanImplementierung aufruft.
2
3
Ab Version 2.0 allgemein Komponenten-Interface
Remote Method Invocation, s. die einschlägige Literatur, z. B. [Öb01]
25
4 Enterprise Java Beans
Home-Interface
Das Home-Interface definiert Methoden, um Zugriff auf Beans zu erlangen. Für
Session-Beans sind das nur create-Methoden. Zustandslose Session-Beans enthalten im Home-Interface genau eine parameterlose create-Methode; zustandsbehaftete
Session-Beans können auch mit Parametern erzeugt werden und daher auch mehrere
create-Methoden enthalten.
Entity-Beans hingegen haben einen persistenten Zustand und repräsentieren typischerweise Objekte, die in einer Datenbank abgelegt sind. Sie enthalten daher noch
Methoden, die der Client zum Erzeugen, Suchen und Löschen eines Beans verwendet.
Es werden create-Methoden definiert, deren Signaturen mit jeweils einer ejbCreate- und einer ejbPostCreate-Methode in der Bean-Klasse übereinstimmen müssen
(siehe (B) in Abbildung 4.2). Zum Suchen nach Objekten werden find-Methoden
definiert. Die meisten Container bieten die Möglichkeit, für diese find-Methoden
die entsprechenden Suchanfragen an die Datenbank im Deployment-Deskriptor zu
hinterlegen; daraus werden die Abfragen konstruiert.
Das Home-Interface wird ebenfalls von einer vom Container generierten Klasse implementiert.
Bean-Klasse und Primärschlüssel
Die Bean-Klasse implementiert eines der beiden Java-Interfaces javax.ejb.SessionBean oder javax.ejb.EntityBean. Damit werden Methoden vorgegeben, die hier
implementiert werden müssen. Es handelt sich um sog. Callback-Methoden (siehe
(C) in Abbildung 4.2), die vom Container aufgerufen werden, um die Bean über
Zustandswechsel im Lebenszyklus zu informieren.
Daneben implementiert die Bean-Klasse die Methoden, welche im Remote-Interface
für die Anwendungslogik deklariert wurden (A). Hier kann der Compiler die Konsistenz nicht sicherstellen, aber die Tools zur Code-Generierung setzen konsistente
Deklarationen voraus.
Die Implementierung einer eigenen Primärschlüsselklasse ist optional. Zwingend notwendig ist sie nur bei Einsatz eines zusammengesetzten Schlüssels. Namen und Datentypen der Schlüsselattribute müssen zwischen Bean- und Primärschlüsselklasse
übereinstimmen (D).
Deployment-Deskriptor
Zusätzlich zum Java-Code muss der Deployment-Deskriptor, eine Beschreibungsdatei
im XML-Format, erstellt werden. Hier werden die vollständigen Namen der beteiligten Klassen und Informationen zum Primärschlüssel verzeichnet und benötigte Ressourcen wie Datenbankverbindungen oder auch andere Enterprise-Beans eingetragen.
26
4.3 Enterprise-Beans
Außerdem werden hier in deklarativer Form Transaktionsverhalten, Zugriffskontrolle
und persistente Attribute (bei Entity-Beans) festgelegt.
Code-Generierung
Bei der Installation eines Enterprise-Beans in einem EJB-Container (Deployment) erzeugen die Container-Tools zusätzliche Klassen. Mit Informationen aus dem Deployment-Deskriptor werden Implementierungen für die Home- und Remote-Interfaces
generiert (E). Da diese Klassen zur Laufzeit als Remote-Objekte via RMI angesprochen werden, sind für beide jeweils Stub- und Skeleton-Klassen zu erzeugen (F). Eine
Enterprise-Bean besteht also aus zwei Interfaces und sieben bis acht Klassen.
Abbildung 4.2 zeigt die Abhängigkeiten zwischen den Klassen und Interfaces und
zeigt, was vom Bean-Provider geleistet werden muss und was von Tools des ContainerHerstellers generiert wird.
4.3.2 Session-Beans
Session-Beans modellieren Anwendungsfälle (oder Geschäftsvorfälle) und sind genau
einem Client zugeordnet. Sie können also als Erweiterung des Clients auf dem Server
angesehen werden. Session-Beans werden unterschieden in stateful Session-Beans,
welche zwischen zwei Methodenaufrufen erhalten bleiben, genau dem aufrufenden
Client zugeordnet bleiben und insbesondere einen Zustand behalten können, und
stateless Session-Beans, die nur für einen Methodenaufruf einem Client zugeordnet
sind. Sie können also zwischen zwei Aufrufen keinen Zustand behalten.
Für die Entscheidung, ob Session-Beans zustandslos oder zustandsbehaftet eingesetzt
werden, sind Überlegungen auf zwei Ebenen wichtig: Einerseits die Übersichtlichkeit
und Klarheit des Clients und der Bean, und andererseits die Fähigkeiten des eingesetzten EJB-Containers hinsichtlich Lastverteilung und Fail-over-Sicherheit.
Bei interaktiven Anwendungen ziehen sich Anwendungsfälle in der Regel über mehrere Aufrufe an den Server hin; hier bieten sich stateful Session-Beans an, da der
Zustand behalten werden kann und die Aufrufe an sich einfacher sein können. Allerdings kann das bei hohen Clientlasten dazu führen, dass sehr viele Session-Beans
instanziiert werden müssen, obwohl nur eine geringe Zahl von Clients tatsächlich
Anfragen an den Server schickt. Dann müssen die zur Zeit nicht aktiven Beans ausgelagert werden. Der Verwaltungsaufwand für den Container steigt also.
Für viele Projekte wird aber zum entscheidenen Argument, ausschließlich stateless
Session-Beans einzusetzen, dass Fail-over-Sicherheit mit den gängigen Produkten zur
Zeit nur mit stateless Session-Beans gewährleistet werden kann, und dass auch Lastverteilung nur bedingt mit stateful Session-Beans möglich ist.
27
4 Enterprise Java Beans
Abbildung 4.2: Überblick über alle Klassen und Interfaces einer Bean
28
4.3 Enterprise-Beans
4.3.3 Entity-Beans
Entity-Beans dagegen repräsentieren Anwendungsentitäten (oder Geschäftsobjekte).
Verschiedene Clients können auf ein und dieselbe Entity-Bean-Instanz parallel zugreifen. Entity-Beans werden in der Regel in Datenbanken persistent gespeichert; der
Container bietet hierfür Dienste an. Dieser Aspekt ist Schwerpunkt dieser Arbeit
und wird in den folgenden Kapiteln ausführlich erörtert. Um dabei die Konsistenz
beim konkurrierenden Zugriff mehrere Clients zu gewährleisten, stellt der Container
Mechanismen zum Transaktionsschutz bereit (s. Abschnitt 4.3.5).
4.3.4 Lebenszyklen von Session- und Entity-Beans
Der EJB-Container ist für die Steuerung der Lebenszyklen aller Bean-Instanzen zuständig. Er kümmert sich um deren Instanziierung, das Management unterschiedlicher Zustände während ihrer Lebensdauer, wie auch um die Vernichtung von Instanzen. Zur Benachrichtigung der Beans über ihren jeweiligen Status werden auf ihnen
vom Container bei den Zustandsübergängen entsprechende Callback-Methoden aufgerufen (siehe (C) in Abbildung 4.2).
Da betriebliche Informationssysteme häufig mit einer großen Anzahl von Clients und
infolge dessen auch einer großen Anzahl benötigter Anwendungsentitäten umzugehen
haben, muss der Container in der Lage sein, ohne negative Beeinflussung der Performanz auch große Zahlen von Bean-Instanzen zu verwalten. Dazu kann er Enterprise
Beans in sogenannten Pools verwalten (Pooling). Eine bestimmte Anzahl von Beans
wird in einem Pool auf Vorrat gehalten, bei Notwendigkeit dem Pool entnommen und
gegebenenfalls mit den entsprechenden Werten belegt. Zusätzlich besteht für SessionBeans die Möglichkeit der Aktivierung und Passivierung einzelner Instanzen. Hierzu
werden die Beans in der Regel serialisiert im Dateisystem abgelegt.
Stateless Session-Bean
Die einfachste Lebenszyklusstruktur haben die stateless Session-Beans. Die beiden
einzigen Möglichkeiten nicht existent“ und bereit“ stellt Abbildung 4.3 dar. Im
”
”
Bereit-Zustand kann der Container diese Art der Beans im Pool verwalten, da sie
jeweils nur für die Dauer eines Methodenaufrufs einem Client zugeordnet werden
und ansonsten für alle Aufrufer gleich sind.
Stateful Session-Bean
Die Verwaltung von stateful Session-Beans gestaltet sich etwas schwieriger. Da diese
zustandsbehaftet und jeweils nur einem Client zugeordnet sind, gibt es von einer
Session-Bean-Klasse viele Instanzen. Da deren Anzahl mitunter sehr hoch werden
29
4 Enterprise Java Beans
Abbildung 4.3: Lebenszyklus eines stateless Session-Beans
kann, besteht hier für den Container zur Reduzierung der Resourcenauslastung die
Möglichkeit, eine Überführung in den Zustand passiviert“ vorzunehmen und die
”
Instanz dabei auszulagern (s. Abbildung 4.4).
Abbildung 4.4: Lebenszyklus eines stateful Session-Beans
Entity-Bean
Grundsätzlich sind Entity-Instanzen entweder nicht existent“ oder in einem Pool
”
bereit“. Da Entity-Beans Daten aus einer Datenbank repräsentieren, gibt es pro
”
Entität in der Datenbank nur eine Bean-Instanz. Diese wird durch die Belegung
einer Instanz aus dem Pool erzeugt und zeichnet sich dann, wie in Abbildung 4.5 zu
sehen, vor allem durch ihren aktuellen Status in Bezug auf die Datenbank aus.
Betrachtet man die Lebensdauer eines Entity-Beans, so muss man sauber unterscheiden zwischen der Lebensdauer der Entität, welche durch das Entity-Bean repräsentiert wird, und der aktuellen Lebensdauer der Bean-Instanz. Ein Entity-Bean kann
30
4.3 Enterprise-Beans
Abbildung 4.5: Lebenszyklus eines Entity-Beans
als Objekt im Pool bereit liegen und wird für einige Zugriffe mit den Werten einer bestimmten Instanz A aus der Datenbank belegt, wird wieder in den Pool zurückgelegt
und anschließend für die Repräsentation einer anderen Instanz B verwendet.
Interessant ist es, die Zeit zu betrachten, während der ein Entity-Bean genau eine bestimmte Instanz aus der Datenbank repräsentiert. In dieser Zeit existiert kein anderes
Entity-Bean, das die selbe Instanz repräsentiert. Möchten nun zwei Clients gleichzeitig mit diesem Bean arbeiten, so müssen die Zugriffe transaktionsgeschützt erfolgen.
Die EJB-Spezifikation sieht hierfür ein Transaktionskonzept vor, das im nächsten
Abschnitt besprochen wird.
4.3.5 Transaktionen
Die Handhabung der Transaktionen kann vom Bean selber übernommen werden oder
dem Container überlassen werden.
EJB sieht ein deklaratives Transaktionskonzept vor, d. h. dass der Transaktionsschutz im Deskriptor festgelegt wird. Vorteil ist, dass der Beanprogrammierer kein
Transaktions-Handling ausprogrammieren muss. Nachteil ist, dass er doch bei der
Implementierung des Beans implizite Annahmen treffen muss und die hoffentlich mit
den Angaben im Deskriptor übereinstimmen.
EJB-Transaktionen auf Datenbank-Transaktionen abzubilden ist nicht unbedingt trivial, zumal ja nicht festgelegt ist, wie mächtig das Transaktionskonzept der betreffenden Datenbank ist. Im Deployment-Deskriptor wird zunächst festgelegt, ob der
Transaktionsschutz deklarativ im Deployment-Deskriptor oder programmatisch in
31
4 Enterprise Java Beans
der Bean-Implementierung erfolgt. Im ersten Fall wird für jede Methode des RemoteInterfaces angegeben, mit welchem Isolationslevel sie aufgerufen werden muss. Der
Container verwendet einen Transaktionsservice4 , um den geforderten Transaktionsschutz bereitzustellen. Dies entlastet den Bean-Programmierer davon, sich um Details
der Transaktions-Implementierung zu kümmern. Allerdings muss er durchaus wissen,
dass ein zu laxer Transaktionsschutz u. U. zu fehlerhaftem Verhalten führen kann,
und dass zu strenger Transaktionsschutz einen Performance-Engpass bedeutet.
4.4 Persistenz von Entity-Beans
Ein besonderes Problem beim Design einer EJB-Anwendung ist die effiziente Speicherung der Daten in einer Datenbank. EJB bietet für Entity-Beans transparente
Mechanismen. Sie können entweder vom Container angestoßen und vom Entity-Bean
ausgeführt werden (Bean Managed Persistence, BMP), oder der Container kann dies
komplett übernehmen (Container Managed Persistence, CMP).
Die CMP der Version 1.1 zur Abbildung der Entity-Beans in die Datenbank erlaubt
nur sehr einfache Datenstrukturen. Es ist z. B. nicht möglich, abhängige Objekte
abzubilden (klassisches Beispiel: ein Auftrag mit beliebig vielen Auftragspositionen).
Beziehungen zwischen Entity-Beans werden von Hand ausprogrammiert oder über
proprietäre Funktionalität des Containers gelöst.
4.4.1 Container Managed Persistence (CMP)
Vor- und Nachteil zugleich ist bei CMP die Tatsache, dass die Klasse selbst nichts über
die Persistenz weiß; das OR-Mapping wird im Deskriptor beschrieben; der Container
erledigt die Arbeit. Im Entity-Bean wird die fachliche Logik implementiert, und die
technischen Abhängigkeiten, in diesem Fall die Abbildung in die Datenbank, wird
deskriptiv gelöst. So erhält man eine A/T-Trennung in Bean (A) und Deskriptor
(T).
Was auf den ersten Blick so einfach aussieht, stellt bei näherer Betrachtung eine
Reihe von Problemen:
Die Spezifikation legt nur fest, wie persistente Attribute im Deployment-Deskriptor
benannt werden. Die genaue Abbildung in die Datenbank wird nicht spezifiziert. Das
ist unter dem Gesichtspunkt der Portabilität ein Nachteil: Werden Entity-Beans auf
einen anderen Container portiert, muss der OR-Mapping-Deskriptor neu geschrieben werden. Dabei sind unter Umständen nicht nur anderslautende XML-Tags erforderlich, sondern womöglich sind die OR-Mapping-Mechanismen unterschiedlich
mächtig. Daraus folgt, dass auch das Bean anders entworfen werden muss, um die
4
die Dienste JTA und JTS
32
4.4 Persistenz von Entity-Beans
Möglichkeiten des Containers auszunutzen. Unterschiedlich mächtige OR-MappingMechanismen verhindern die Portabilität von Entity-Beans.
Die Standard-Mechanismen der gängigen Container sind relativ trivial: Sie bilden
jedes Bean auf eine Tabelle, jedes Attribut auf eine Spalte und jede Entität auf eine
Zeile ab. Komplexere Objektgeflechte, die komplexere Abbildungen erfordern, gehen
über die Spezifikation hinaus und sind nicht mehr portabel. Einige der gängigen Container können abhängige Objekte von nicht-trivialen Datentypen höchstens als BLOB
(binary large object) abspeichern. Hier taucht dann das Problem auf, dass Datenbanken wie z. B. Oracle nur eine BLOB-Spalte pro Tabelle unterstützen; außerdem
kann über diese Spalten nicht gesucht werden, und die Definition der abgespeicherten Klassenstruktur darf sich nie wieder ändern. Erst aufwändige Werkzeuge, wie im
nächsten Abschnitt beschrieben, leisten hier mehr.
Die Listings 4.1 und 4.2 zeigen Teile der Bean-Implementierung und den zugehörigen
Deployment-Deskriptor für ein Bean mit CMP.
Listing 4.1: Beispiel einer Bean-Implementierung mit CMP
import javax.ejb.EntityBean;
public class CustomerBean implements EntityBean {
public String customerId; // public nur wegen CMP-Zugriff
public String firstName;
public String lastName;
// ...
}
Listing 4.2: Zugehöriger Deployment-Deskriptor
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>CustomerBean</ejb-name>
<home>com.sdm.tbs.ac.customer.impl.CustomerHome</home>
<remote>com.sdm.tbs.ac.customer.impl.Customer</remote>
<ejb-class>com.sdm.tbs.ac.customer.impl.CustomerBean</ejbclass>
<persistence-type>Container</persistence-type>
<prim-key-class>com.sdm.tbs.ac.customer.impl.CustomerPK</
prim-key-class>
<reentrant>False</reentrant>
33
4 Enterprise Java Beans
<cmp-field>
<field-name>customerId</field-name>
</cmp-field>
<cmp-field>
<field-name>firstName</field-name>
</cmp-field>
<cmp-field>
<field-name>lastName</field-name>
</cmp-field>
</entity>
</enterprise-beans>
</ejb-jar>
Die mit CMP persistent gehaltenen Felder müssen als public vereinbart werden,
damit der Container darauf zugreifen kann. Unter dem Gesichtspunkt der Kapselung
ist das kritisch zu bewerten.
4.4.2 Einsatz eines OR-Mapping-Werkzeugs für CMP
Es ist auch möglich, den Persistenz-Mechanismus eines Containers zu ersetzen durch
ein Produkt wie z. B. TopLink [Top01] oder Versant [Ver01]. Auf diese Weise wird
CMP wesentlich mächtiger; Standardfälle wie die Abbildung von abhängigen Objekten werden deutlich besser unterstützt.
Dies ist möglich, weil die Hersteller solcher Werkzeuge sehr spezifisches Know-How
über die OR-Abbildung haben, über das die Applikationsserver-Hersteller nicht verfügen. Deshalb ist es auch sinnvoll, dass im EJB-Standard nicht so ein ausgefeiltes
OR-Mapping verlangt wird. Allerdings weicht man durch den Einsatz eines solchen
Werkzeuges vom Standard ab und erhöht so die Abhängigkeit von diesem Tool und
vermutlich auch vom verwendeten Applikationsserver, da die Schnittstelle zwischen
Server und Container nicht spezifiziert ist.
TopLink
Die mächtigen OR-Mapping-Fähigkeiten von TopLink für native Anwendungen sind
auch für EJB einsetzbar. Entity-Beans können mit abhängigen Objekten und ganzen
Geflechten mit Hilfe der bekannten grafischen Oberfläche und des damit erzeugten
Zugriffscodes bzw. XML-Deskriptors auf die Datenbank abgebildet werden. Diese
Möglichkeiten gehen deutlich über die üblichen Container-Fähigkeiten hinaus. Deshalb macht man sich schon mit dem Entwurf, der auf die Nutzung dieser Möglichkeiten abzielt, von dieser Produktkombination abhängig.
34
4.4 Persistenz von Entity-Beans
Versant enJin
Versant enJin ist eine objektorientierte Datenbank. Versant vertreibt einen Container,
der direkt mit WebSphere oder WebLogic zusammenarbeiten kann, und Entity-Beans
direkt in der Objektdatenbank ablegt. So unterliegt man beim Entwurf der Beans
nicht mehr den oben angeführten Einschränkungen, macht sich aber von der eingesetzten Datenbank abhängig. Siehe Abschnitt 5.2.3 für eine kurze Einordnung von
objektorientierten Datenbanken.
4.4.3 Bean Managed Persistence (BMP)
Die Spezifikation sieht vor, dass jedes Entity-Bean auch selbst die Speicherung in der
Datenbank übernehmen kann. Dafür implementiert es die Methoden ejbLoad und
ejbStore, die vom Container aufgerufen werden und in denen die Speicherung des
Objektes in der Datenbank durch das EntityBean selbst angestoßen oder vorgenommen wird.
Der naive Ansatz ist, hier direkt JDBC-Aufrufe einzubauen. Dies ist aber ineffizient,
da das Bean die Zahl der Aufrufe dieser Methoden nicht in der Hand hat, und es
ist auch kein gutes Design, da Anwendungslogik mit datenbanktechnischen Dingen
vermischt wird. In der Literatur (s. z. B. [DePe00], [Ro99] oder [Mon99]) wird BMP
auf diese Weise erklärt; gleichzeitig erfolgen in der Regel Warnungen, BMP wegen
der drohenden Unübersichtlichkeit und der nicht unbedingt höheren Effizienz nur
in ausgewählten Fällen einzusetzen. Dem ist uneingeschränkt zuzustimmen: Selber
JDBC-Aufrufe in ein Entity-Bean zu schreiben, ist sicher keine gute Idee. Listing 4.3
zeigt das Grobgerüst eines Beans, das BMP verwendet.
Listing 4.3: Beispiel eine Bean-Implementierung mit BMP
import javax.ejb.EntityBean;
public class CustomerBean implements EntityBean {
private String customerId;
private String firstName;
private String lastName;
private boolean dataLoaded;
// ...
public CustomerPK ejbCreate (String customerId)
{
this.customerId = customerId;
dataLoaded = false;
35
4 Enterprise Java Beans
return new CustomerPK (customerId);
}
public void ejbLoad ()
{
customerId = context.getPrimaryKey ();
// verwende den Primaerschluessel, um die Daten zu laden
// z. B. ueber JDBC-Aufrufe
dataLoaded = false;
}
public void ejbStore ()
{
// schreibe alle geaenderten Daten zurueck in die Datenbank.
}
public String getLastName ()
{
if (!dataLoaded)
doLoadData ();
return lastName;
}
private void doLoadData ()
{
// Der Primaerschluessel customerId ist bekannt.
// Nun die Daten aus der Datenbank laden, z. B. per JDBC.
dataLoaded = true;
}
}
Allerdings sind damit die Möglichkeiten von BMP noch nicht erschöpft – und das
wird in den genannten Standardwerken nicht erwähnt. Eine Alternative ist es, in
diesen Methoden ein Framework aufzurufen, das die Datenbankaufrufe durchführt.
Dies kann ein eigenes Konstrukt sein, das als einfacher Zwischenspeicher arbeitet und
nur notwendige Updates ausführt, eine eigene Zugriffsschicht wie das QDI, oder auch
ein vorhandenes Produkt wie TopLink.
In Listing 4.3 werden dann nicht mehr in der Methode ejbLoad die Daten aus der
Datenbank gelesen, sondern erst beim ersten Zugriff auf das Bean werden die erforderlichen Daten nachgeladen.
36
4.5 EJB 2.0
4.4.4 Java Data Objects (JDO)
In diesem Zusammenhang taucht immer wieder die Frage nach Java Data Objects
(JDO) auf. Hierbei handelt es sich ebenfalls lediglich um eine Spezifikation, nicht
um ein fertiges und benutzbar vorliegendes API. An dieser Stelle sei nur auf die
einschlägigen Internet-Seiten verwiesen, zu finden unter http://jcp.org/jsr/detail/
12.jsp oder http://java.sun.com/products/jdbc/related.html
Dort heißt es einerseits, JDO definiere keine eigenen Persistenzmechanismen, sondern
beziehe sich auf die entsprechenden APIs von J2EE. Andererseits sei JDO eine von
EJB unabhängige, parallele Spezifikation. Es handelt sich – grob gesagt – um das
objektorientierte Pendant zum relationalen JDBC.
JDO ist kein von EJB ausdrücklich motivierter Weg für die Persistenz. Deshalb soll
an dieser Stelle nicht zuviel Aufmerksamkeit darauf gelenkt werden.
4.4.5 Weitere Aspekte
Das Problem der Persistenz kann nicht losgelöst von einem Transaktionsmodell betrachtet werden. Zur Abbildung von Anwendungs-Transaktionen auf Applikationsserver-Transaktionen und von diesen auf Datenbank-Transaktionen siehe Abschnitt
3.2.1.
Der Applikationsserver stellt einen Transaktionsservice zur Verfügung. Ähnlich wie
bei der Persistenz können auch Transaktionen entweder deklarativ (im DeploymentDeskriptor) oder programmatisch (in der Bean-Implementierung) festgelegt werden.
Eine gute Einführung in das Thema Transaktionen mit EJB ist u. a. in [Ro99] zu finden; für eine detaillierte Betrachtung von Transaktionen sei auf [GrRe92] verwiesen.
4.5 EJB 2.0
Die Spezifikation 2.0 [Sun01a] wurde im Sommer 2001 verabschiedet. Im Moment
sind noch keine ausreichend gute Produkte erhältlich, die die Spezifikation 2.0 implementieren5 ; daher sind bislang noch kaum aussagegräftige Versuche möglich.
Wichtige Neuerungen umfassen u. a.:
• Message Driven Beans: JMS jetzt auch für EJB
Ähnlich den Session-Beans enthalten sie Logik für Anwendungsfälle. Im Gegensatz zu Session- oder Entity-Beans werden sie aber nicht über Home- und
Remote-Interfaces angesprochen, sondern ihnen wird eine JMS-Nachricht (Java Messaging Service, s. [Wrox01]) zugesandt; sie arbeiten also asynchron. Der
5
einzig der Bea WebLogic 6.1, der zur Zeit in einer Beta-Version vorliegt. Stand: November 2001
37
4 Enterprise Java Beans
Nachrichtendienst JMS ist nicht neu; neu ist lediglich die Möglichkeit, in einem
EJB-System JMS-Nachrichten empfangen zu können.
• Local interfaces
Bislang war jeder Aufruf an ein Enterprise Bean ein Remote-Aufruf; d. h. dass
alle Argumente serialisiert werden und der Aufruf über RMI erfolgt, selbst
dann, wenn das gerufene Objekt sich in der selben virtuellen Maschine befindet.
Nun ist es aber oft gar nicht sinnvoll, andere Objekte entfernt anzusprechen.
Beispielsweise hat sich das Fassade-Pattern durchgesetzt, das bedeutet, dass
Entity-Beans nur von Session-Beans verwendet werden und Clients nur mit
Session-Beans kommunizieren. Dann braucht kein Aufruf an ein Entity-Bean
mehr entfernt zu sein. Dies erhöht neben der Performanz auch die Sicherheit.
Der entscheidende Vorteil, der sich aus diesen Performanzüberlegungen ergibt,
ist die Tatsache, dass nun mehrere Beans gemeinsam entwickelt werden können,
die zusammen gehören. Nur eines von ihnen bekommt ein Remote-Interface, der
Rest arbeitet über local interfaces zusammen; so ist es eher möglich, fachlich
motivierte Komponentengrenzen mit Hilfe der EJB-Konstrukte auszudrücken.
Im Prinzip handelt es sich um eine konsequente Weiterentwicklung der Spezifikation,
die jetzt das mit einschließt, was einzelne Container-Hersteller schon als proprietäre Lösungen eingebaut hatten. Allerdings geht die Spezifikation nicht so weit wie
von manchen erwartet wurde. Es steht vielmehr zu erwarten, dass sich die EJBTechnologie weiter entwickeln wird.
4.5.1 Erweiterungen in EJB 2.0 bezüglich der Persistenz
Mit der Version 2.0 kommen Erweiterungen, die in [Wrox01, S. 149] enthusiastisch
angekündigt werden:
This new EJB 2.0 persistence model is not just a fine tuning of features
”
that were available in the EJB 1.1 specification. Instead, EJB 2.0 persistence is a revolutionary addition of a standards-based object-relational
mapping framework to Enterprise JavaBeans technology.“
Die Vorteile, die sich tatsächlich aus den Änderungen ergeben, sind nach Meinung des
Autors enttäuschend gering: Eine Revolution hat hier nicht stattgefunden. Trotzdem
müssen alle Entity Beans umgeschrieben werden, da die Änderungen, z. B. der Zugriff
auf CMP-Attribute, nicht abwärtskompatibel sind. Dennoch: Die Beschreibung der
objektrelationalen Abbildung im Deployment-Deskriptor wurde immer noch nicht
normiert.
38
4.5 EJB 2.0
Version 2.0 bringt interessante Neuerungen im Hinblick auf die Beziehung zwischen
Entity-Beans. Solche Beziehungen können mit dem neu eingeführten Konzept Con”
tainer Managed Relationsships (CMR)“ formuliert werden; elegant ist die transparente Eingliederung in den neuen CMP-Mechanismus.
Erstmals wird auch eine Abfragesprache spezifiziert: EJB-QL ist neu, orientiert sich
aber an den bisher verfügbaren proprietären Lösungen der Produkthersteller.
Auch lässt die Spezifikation 2.0 noch zentrale Design-Fragen offen, bzw. Funktionalität fehlt. Beispielsweise fehlt eine Festlegung auf ein optimistisches oder pessimistisches Transaktionskonzept. Dies bleibt nach wie vor den Container-Herstellern
überlassen.
Im Einzelne sind für die Persistenz besonders folgende Änderungen interessant:
Geänderte CMP
Der Zugriff auf die persistenten Attribute läuft nun über (abstrakte) Zugriffsmethoden und nicht mehr direkt auf die Attribute. Im Hinblick auf die Verwendung von
abhängigen Objekten und ihrer Persistenz enttäuscht die Spezifikation 2.0. Weitere
Änderungen sind zu erwarten (oder zu erhoffen).
Container Managed Relationships (CMR)
CMR geht einen wichtigen Schritt in die richtige Richtung. Die objekt-relationale
Abbildung wird erweitert um die Abbildung von Beziehungen zwischen A-Entitäten.
Die Einführung von CMR ist auch der Grund für die Änderung des Zugriffs auf CMPAttribute. In Verbindung mit den neu eingeführten local interfaces sind interessante
Konstruktionen möglich. Allerdings müsste dann aus jedem kleinen abhängigen Objekt (wie z. B. einer Auftragsposition, einer Adresse o. ä.) ein Bean gemacht werden.
Eine Query Language (EJB-QL)
Die meisten Container der Spezifikation 1.1 haben ihre eigenen, proprietären Abfragesprachen mitgebracht, mit deren Hilfe Abfragen an die Datenbank abgesetzt werden
können. Diese Abfragesprachen waren alle an SQL angelehnt; nach dem Muster dieser
Abfragesprachen ist die EJB-QL neu in die Spezifikation aufgenommen. Sie bringt also nicht mehr Funktionalität, sondern nur mehr Portabilität zwischen verschiedenen
Containern.
Es bleibt das Problem, dass die Abfragen im Deployment-Deskriptor formuliert werden. Sie sind aber durchaus auch Teil der Anwendungslogik; es kann also nicht von
einer A/T-Trennung in Implementierung (A) und Deployment-Deskriptor (T) gesprochen werden.
39
4 Enterprise Java Beans
4.5.2 Bewertung der Änderungen
Abhängigkeit vom Hersteller
Die Neuerungen in der EJB-Spezifikation sind entstanden aus den Praxis-Erfahrungen
mit EJB 1.1-Anwendungen. Daher verwundert es umso mehr, wieviel Freiheiten den
Container-Herstellern gelassen werden, bzw. wieviel Arbeit ihnen noch gemacht wird.
Bei der Spezifikation der Abfragesprache EJB-QL sind die Erfahrungen mit den jeweiligen proprietären Abfragesprachen eingeflossen. EJB-QL kann nicht mehr und
nicht weniger als die bisherigen Methoden, aber nun ist sie immerhin spezifiziert und
damit portabel.
Allerdings ist nicht spezifiziert, wie das OR-Mapping genau umzusetzen ist – die
Portabilität zwischen zwei Applikationsservern scheitert nach wie vor spätestens am
Deployment-Deskriptor. Das Problem hierbei sind aber wie bisher nicht einfach anderslautende XML-Tags, sondern die unterschiedlichen Möglichkeiten bei der Abbildung auf die Datenbank bestimmen den Entwurf der Entity-Beans. Für einen anderen
Container genügt also nicht ein neuer Deployment-Deskriptor, sondern womöglich ist
eine Portierung gar nicht möglich, da die Fähigkeiten der Container zu unterschiedlich sind.
Schwächen, die trotzdem erhalten geblieben sind
Eine große Schwäche von EJB ist erhalten geblieben: Die versprochene Trennung von
A-Software in der Bean-Implementierung und T-Aspekten im Deployment-Deskriptor
ist immer noch nicht erfüllt. Im Gegenteil: Die Relationen zwischen A-Entitäten sind
ein fachlicher Aspekt, der aber im Deployment-Deskriptor formuliert wird. Es ist
durchaus ein gutes Konzept, solche Dinge in deklarativer Form umzusetzen, allerdings
muss man sich dabei der Bedeutung des Deployment-Deskriptors als wichtigem Teil
der fachlichen Implementierung bewusst sein.
Der Performanz-Engpass, der durch die Remote-Interfaces entstanden war, wurde
erkannt und beseitigt. Beim Umgang mit A-Entitäten ist ein wichtiges Instrument zur
Performanz-Optimierung die Entscheidung zwischen lazy-loading und eager-loading.
Dazu hat der Programmierer aber mit CMR keine Möglichkeit. So ist ein neuer
potenzieller Flaschenhals entstanden, weil die an sich gute Idee nicht konsequent
zuende gedacht wurde.
Sowohl CMR als auch der neue CMP-Mechanismus laufen über abstrakte Zugriffsmethoden, welche in einer vom Container generierten Subklassee implementiert werden.
Denkbar wäre, im Deployment-Deskriptor deklarativ den Ladezeitpunkt zu bestimmen, also früh“, um viele Daten en bloc zu laden, oder spät“, um nur die benötigten
”
”
Attribute zu lesen. Der Container könnte dann die zugehörige Zugriffsmethode entsprechend ausgestalten. Weiterhin fehlt die Unterscheidung zwischen vollwertigen“
”
40
4.5 EJB 2.0
A-Entitäten und kleinen, abhängigen Objekten. Erst mit einer solchen Unterscheidung wäre der Entwurf von sauber geschnittenen Komponenten elegant möglich.
Da auch bisher Erfahrungen mit Schwächen der Spezifikation und der Produkte eingearbeitet wurden, ist damit zu rechnen, dass diese ausgebessert werden. Dann dürfen
wir uns auf einen weiteren Versionwechsel freuen.
4.5.3 Der Wechsel von EJB 1.1 auf EJB 2.0
Für die Durchführung von Projekten, die im Moment die Version 1.1 zur Verfügung haben, aber irgendwann auf 2.0 umsteigen werden bzw. müssen, sind folgende
Aspekte von Bedeutung:
• Ist es möglich oder sinnvoll, EJB 1.1 und EJB 2.0 gleichzeitig zu verwenden,
oder müssen vorhandene Anwendungen jetzt umgeschrieben werden? Beachte:
EJB 1.1-Anwendungen sind noch kein Jahr alt und dennoch Altsysteme!
• Welche Konstruktionen, die mit EJB 1.1 gebaut werden, sind mit 2.0 nicht mehr
nötig, weil die Probleme behoben sind? Welche müssen beibehalten werden?
• Welche müssen über Bord geworfen werden, weil sie nicht mehr funktionieren?
Die EJB-Spezifikation 2.0 legt ausdrücklich fest, dass alle EJBs der Version 1.1 noch
unterstützt werden müssen. Ein Entity-Bean kann entweder der Version 1.1 oder der
Version 2.0 entsprechen, muss dies auch explizit machen und kann auf dem Applikationsserver installiert werden.
Allerdings ist die Pflege eines Systems, das teils aus alten und teils aus neuen Beans
besteht, im Projektverlauf problematisch. Es wird eher vom Pflegeaufwand als von
den technischen Gegebenheiten der Druck entstehen, die alten Beans umzuschreiben,
um ein einheitliches System zu erhalten.
Die Spezifikation 1.1 hatte Schwächen, welche zu Hilfskonstruktionen geführt haben,
die nun nicht mehr in jedem Fall nötig sind. Ein Beispiel ist das Value Object-Pattern
[Alu01], dessen Verwendung angeraten wird, da Aufrufe über das Remote-Interface
zu teuer sind. Werden bestimmte Entity-Beans nur von Session-Beans aus aufgerufen, so können local interfaces verwendet werden, und es ist zu überlegen, ob nicht
völlig auf das Value Object verzichtet werden kann. Um Beans konsequent von 1.1
auf 2.0 umzuschreiben, reicht eine Änderung des Zugriffsmechanismus auf die CMPAttribute nicht aus, sondern auch derartige Konstruktionen müssen neu überdacht
werden.
41
5 Persistenzoptionen von A-Entitäten
Für die Implementierung von A-Entitäten gibt es verschiedene Möglichkeiten. Abbildung 5.1 stellt sie als Baum dar.
Abbildung 5.1: Persistenz-Optionen für A-Entitäten
Der linke Ast führt die Persistenzoptionen beim Einsatz von Entity-Beans auf; sie
werden im ersten Teil dieses Kapitels beschrieben. Der rechte Ast sieht die Implementierung von A-Entitäten als nativen Java-Klassen vor und wird im zweiten Teil
untersucht.
5.1 Persistenz über Entity-Beans
Der Entwurf eines betrieblichen Informationssystems beginnt mit der fachlichen Anforderungsanalyse und der fachlichen Spezifikation. Der erste Schritt ist also, nach
fachlichen Gesichtspunkten eine Komponentenbildung zu identifizieren, um Anwendungsfälle, Anwendungsentitäten und Anwendungsentitätenverwalter zu modellieren,
kurz: um festzustellen, wo welche fachliche Logik untergebracht werden muss.
Die EJB-Spezifikation schlägt vor, Anwendungsfälle als Session-Beans zu formulieren
und Anwendungs-Entitäten als Entity-Beans zu modellieren. Sie verspricht auch die
Trennung von fachlicher Logik von technischen Aspekten, indem die Anwendungslogik in den Beans ausprogrammiert wird, während die technischen Gegebenheiten
42
5.1 Persistenz über Entity-Beans
wie beispielsweise die Abbildung auf die Datenbank oder der Transaktionsschutz im
Deployment-Deskriptor deklarativ festgehalten wird.
Die Unterscheidung in A-Entität und A-Verwalter ist durch Home- und RemoteInterface gegeben. Die Kombination von Home- und Remote-Interface, der eigentlichen Implementierung in der Bean-Klasse und der Deployment-Deskriptor ergeben
zusammen ein Enterprise Bean. Sun nennt das eine Komponente. Damit scheinen
alle Forderungen erfüllt, um wartbare und effiziente Systeme zu bauen.
5.1.1 Kritik am naiven Einsatz von Entity-Beans
Macht man sich aber genauere Gedanken über die Bildung von Komponenten und
die Definition ihrer Schnittstellen, so stößt man bald auf Widersprüche zwischen
den Möglichkeiten, die EJB bietet und den in Kapitel 2 genannten Forderungen an
den Komponentenschnitt und die A/T-Trennung. Sun stellt gewisse Versprechungen
bezüglich der Leistungsfähigkeit der EJB-Architektur auf:
EJB stellt den Anspruch Write once, run anywhere“
”
In der Spezifikation steht [Sun99, S. 19]:
These applications may be written once, and then deployed on any server
”
platform that supports the Enterprise JavaBeans specification.“
Es folgt die Einschränkung, dass dies nur funktioniert, wenn keine herstellerspezifischen Erweiterungen ausgenutzt werden. Gleichzeitig bleibt aber die Spezifikation
so ungenau, dass die Hersteller eigene Lösungen entwickeln müssen. Dies passiert an
genau den Stellen, wo es schwierig wird, gute Lösungen zu entwickeln: Persistenz,
lokale oder entfernte Aufrufe, Transaktionsverwaltung, Sperrstrategien.
Als Beispiel sei das Sperrverhalten genannt: Entity-Beans repräsentieren jeweils genau eine Entität der Datenbank. Der konkurrierende Zugriff von mehreren Clients
auf ein Entity-Bean muss also transaktionsgeschützt geschehen. Dafür sind prinzipiell zwei Strategien denkbar: Das Bean kann entweder optimistisch oder pessimistisch
gesperrt werden.1
In der pessimistischen Variante werden alle Zugriffe auf ein Entity-Bean serialisiert,
so dass der erste Client Zugriff auf das Entity-Bean bekommt und bis zum Ende
der Transaktion behält; alle weiteren Clients, die auf das selbe Entity-Bean zugreifen
wollen, bleiben gesperrt – selbst wenn sie nur lesend zugreifen möchten. Diese Strategie wird von den meisten Applikationsservern verfolgt. Es gibt auch die optimistische
Strategie, welche für eine Datenbank-Identität bei konkurrierendem Zugriff mehrere
1
siehe hierzu Abschnitt 3.2.1.
43
5 Persistenzoptionen von A-Entitäten
Entity-Bean-Instanzen erzeugt und am Transaktionsende prüft, ob die Daten beim
Zurückschreiben in die Datenbank einen optimistischen Sperrkonflikt auslösen. Diese
Strategie beherrscht zur Zeit nur der Borland Application Server. Eine dritte Variante ist es, das Sperren von Entity-Beans auf das Sperrverhalten der darunterliegenden
Datenbank zu verlagern. Damit kommen die Fähigkeiten der Datenbank und nicht
mehr ausschließlich die des Containers zum Tragen. Diese Möglichkeit bietet z. B.
der Bea WebLogic Server in der Version 6.1.
Die Durchführung des Sperrmechanismus’ ist technischer Natur und soll für den Anwendungsprogrammierer nicht sichtbar sein. Die Entscheidung jedoch, welche Strategie verfolgt werden soll, ist für den Anwendungsprogrammierer wichtig, denn sie
bestimmt, wie sich die Anwendung unter hoher Clientlast verhalten wird.
Die EJB-Spezifikation schweigt sich jedoch über diesen Punkt aus und zwingt die
Hersteller dazu, sich entweder festzulegen auf eine Strategie oder über proprietäre
Wege eine Wahlmöglichkeit einzuführen. So wird sich die selbe Anwendung, auf zwei
unterschiedlichen Servern installiert, unterschiedlich verhalten. Das führt sogar dazu,
dass die Fähigkeiten des verwendeten Applikationsservers das Design der Anwendung beeinflussen. Die Spezifikation bleibt hinter dem Anspruch der Write once, run
”
anywhere“ weit zurück.
Abschnitt 4.4.1 führte diesen Kritikpunkt für die Persistenz mit CMP aus, weil auch
dort die Spezifikation zu ungenau bleibt.
EJB und die Trennung in A/T/0-Software
EJB beansprucht zwar für sich, dass der Entwickler sich ganz auf die fachliche Logik konzentrieren kann und die technischen Probleme vom Container gelöst werden.
Um dies zu erreichen, soll die ganze Anwendungslogik im Bean implementiert werden, während die technischen Abhängigkeiten im Deployment-Deskriptor festgehalten
werden. Beim Design von Beans werden aber technische Aspekte (nämlich die Abhängigkeit von der EJB-Technologie) mit solchen der Anwendungslogik vermischt.
Beispielsweise kann im Falle aufwändiger konstruierter Beans die Persistenz nicht
mehr deklarativ gelöst werden, sondern muss im Bean manuell programmiert werden — oder andersherum bestimmen die Möglichkeiten der deklarativen Lösung die
Gestaltung der Bean. Java-Code, der ein fachliches Problem beschreibt, kann nicht
ohne Weiteres in einem EJB-Container installiert werden. Ebenso kann ein Bean nicht
ohne Container, also ohne EJB-Umgebung, wiederverwendet oder getestet werden.
Performanz
Zumindest bis zur Version 1.1 diktieren Performanz-Überlegungen die Schnittstelle
der Beans. Jeder Aufruf einer fachlichen Methode ist ein Remote-Aufruf über den
RMI-Mechanismus. Das bedeutet, dass alle Parameter serialisiert werden und der
44
5.1 Persistenz über Entity-Beans
Aufruf über das Netzwerk geleitet wird, selbst wenn das gerufene Objekt sich auf
dem selben Rechner in der selben Virtuellen Maschine befindet. Das bedeutet einen
enormen Performanz-Engpass für jeden Aufruf einer Methode des Remote-Interfaces.
Vergleichsmessungen haben ergeben: Remote-Aufrufe sind ca. 1.000-mal langsamer
als lokale Methodenaufrufe.2
Aus diesem Grund schlägt Sun selbst das Value Object-Pattern vor [Alu01], um die
Schnittstelle des Entity-Beans möglichst schmal zu halten und möglichst wenige Aufrufe über die Remote-Schnittstelle zu tätigen. Nach diesem Entwurfsmuster enthält
das Entity-Bean im Remote-Interface nur noch zwei Methoden: getData(), welche
ein Transfer-Objekt zurückliefert, auf dem der Client, in der Regel ein Session-Bean,
die fachlichen Operation ausführt, und mittels der zweiten Methode setData (MyTransferObject obj) die Daten im Entity-Bean wieder setzen kann.
Der Einsatz des Value-Object-Musters führt also dazu, dass das Entity-Bean nach
technischen Gesichtspunkten entworfen wird, und dass es nur noch als Schnittstelle
zur Datenbank existiert. Sun tritt mit diesem Vorschlag von dem Versprechen zurück,
Entity-Beans seien intelligente Anwendungsobjekte mit transparenter Persistenz.
Ab der Spezifikation EJB 2.0 ist dieser Engpass durch die Einführung von local interfaces abgeschafft.
Die Performanz allgemein zu beurteilen, ist schwierig. Es spielen verschiedene Fakten
zusammen:
1. Java ist langsamer als beispielsweise C++. Es handelt sich hier aber nur“
”
um etwa einen linearen Faktor; unterschiedliche Vergleiche in Fachzeitschriften haben unterschiedliche Ergebnisse gebracht. Interessant bei PerformanzVergleichen sind aber nicht solche Faktoren, sondern Größenordnungen. Manche Implementierungen von virtuellen Maschinen können vielleicht ein wenig
mehr herauskitzeln, und für die Zukunft ist noch mehr zu erwarten.
2. Der Flaschenhals bei typischen Informationssystemen liegt nicht in der Ablaufgeschwindigkeit in einem Teilsystem, sondern vielmehr im Zusammenspiel
zwischen verschiedenen Systemen. Wenn ein großer Teil der Laufzeit innerhalb
der Datenbank liegt, macht die Geschwindigkeit der darauf aufbauenden Anwendung eben nur einen kleinen Teil aus.
3. Was zählt, ist nicht etwa ein Vergleich Java vs. C++“, sondern ein Vergleich
”
des gesamten Systems, also beispielsweise: Java mit Applikationsserver vs.
”
C++ mit ORB und Transaktionsmonitor“. Hier haben Applikationsserver die
Chance, durch geschickte Integration der Dienste für Transaktionssteuerung,
Persistenz, Datenbankverbindungs-Pooling, Caching und zentrale Ressourcenverwaltung einen deutlichen Effizienzvorteil zu erreichen.
2
Quelle: DV-Konzept eines erfolgreichen sd&m-Projektes, Karl-Heinz Wichert, Manuel Fehlhammer
45
5 Persistenzoptionen von A-Entitäten
Vergleiche sind also im Einzelfall anzustellen. Ein produktives Projekt bei sd&m
konnte mit einem EJB-Applikationsserver einen messbaren Effizienzvorteil (ca. Faktor vier) gegenüber der abgelösten C++-Anwendung erreichen. Es lassen sich sicherlich auch Praxisbeispiele mit umgekehrtem Ergebnis finden.
Technischer Komponentenbegriff in EJB
EJB versteht sich als Komponentenmodell“ (s. Abschnitt 2.2 zur Abgrenzung zwi”
schen objektorientiertem und komponentenorientertem Entwurf). Zur Identifizierung
von Komponenten sind die Wahl der Granularität der Komponenten und die Beschreibung ihrer Schnittstellen entscheidend. Die Schnittstelle beschreibt die Außensicht einer Komponente; die Größe einer Komponente zeigt sich z. B. in der Zahl
und Struktur ihrer Attribute. Beides sollte sich aus inhaltlichen Erwägungen, also
aus Überlegungen, die sich auf das fachliche Modell stützen, ergeben.
Die EJB-Spezifikation macht sich keine Gedanken über den Unterschied zwischen
einer Komponente und einem Objekt [Sun99, Kapitel 4]:
The enterprise Bean architecture is flexible enough to implement com”
ponents such as the following: An object that represents ...“
EJB stellt mit dem Applikationsserver eine Plattform zur Verfügung, auf der die
Beans eingesetzt werden können. Die Abhängigkeiten von anderen Beans werden im
Deployment-Deskriptor eingetragen, sind also explizit, wie in [Szy99] gefordert. Hier
hört aber der Vergleich mit dem Komponentenbegriff aus Abschnitt 2.2 schon auf. Da
Enterprise-Beans von der Größe her auf einzelne Objekte festgelegt sind, taugen sie
weder zur Strukturierung großer Systeme auf Quelltextebene, noch sind sie geeignet,
in binärer Form ausgeliefert und beliebig kombiniert eingesetzt zu werden.
Obwohl sie aber auf Objekte festgelegt sind, ist Vererbung von Enterprise Beans nicht
(oder nur über Verrenkungen) möglich. Enterprise Beans können also auch nicht mehr
frei nach den Ideen der Objektorientierung entworfen werden.
Sucht man nun nach Hinweisen zum Entwurf von Enterprise Beans, so finden sich
erstaunliche Hilfestellungen. So z. B. Ambler in [Am01]:
Good entity beans: Are coarse-grained. [They] implement important
”
business behavior. [They] are optimized to simplify and minimize database access, limiting the complexity of database joins, avoiding long-runnig
operations that access data, and minimize round-trips between the container and the database.“
Genau wie die EJB-Spezifikation fordert er, Entity-Beans dazu zu verwenden, die
Anwendungslogik aufzunehmen, gleichzeitig aber den Entwurf sowohl im Hinblick
46
5.1 Persistenz über Entity-Beans
auf die Größe als auch auf die Gestaltung der Schnittstelle nur von technischen
Überlegungen her zu motivieren. Dies widerspricht genau den in Kapitel 2 genannten
Prinzipien.
Es werden also sowohl Außen- als auch Innensicht der Beans aufgrund technischer
Überlegungen entworfen. Damit fällt es schwer, Entity-Beans noch als fachliche Komponenten anzusehen.
Abhängigkeit von der EJB-Version
Manche der hier genannten Probleme werden mit der Version 2.0 der EJB-Spezifikation gelöst. Allerdings bedeutet das, dass EJB-Systeme, die für Produkte der Version
1.1 entwickelt wurden, heute bereits Altsysteme sind – obwohl sie noch nicht mal
ein Jahr alt sind! In der Spezifikation ist zwar vorgesehen, auch mit 2.0 noch Beans
der Version 1.1 zu unterstützen. Allerdings ist nicht klar, wie lange dies noch zur
Spezifikation gehört. Es ist noch weniger klar, wie genau sich die Produkthersteller
an diese Vorgabe halten werden.
Vor allem aber ist fraglich, ob es für ein Projekt sinnvoll ist, manche Beans 1.1konform zu behalten, während neue Beans für EJB 2.0 entwickelt werden. Das ist
unübersichtlich und führt dazu, dass im Entwicklerteam Know-How über ein an sich
schon veraltetes System gepflegt werden muss. Für Projektarbeit ist dies ein kritischer
Punkt.
Entscheidet sich ein Projekt mit dem Einsatz eines Applikationsservers für EJB 2.0,
alle Beans nach 1.1 abzulösen, so müssen all diese Beans umgeschrieben werden.
Software, die nach der Änderung der technischen Basis ohne geänderte fachliche
Anforderungen angepasst werden muss, ist eindeutig T-Software.
Schwierigkeiten bei der praktischen Programmentwicklung
• Mit Home-, Remote-Interface, der Bean-Implementierung selbst und dem Deployment-Deskriptor müssen vier Dateien konsistent gehalten werden – ohne
dass der Compiler dabei Unterstützung leisten kann. Abbildung 4.2 zeigt die
komplexen Abhängigkeiten.
• Entwicklungszyklen sind recht lang: Selbst Hot-Deployment dauert von einigen Sekunden bis mehrere Minuten; bei WebLogic (dem Marktführer(!)) z. B.
funktioniert es nicht immer, und Herunter- und Hochfahren des Servers dauert
lang.
• Debugging ist schwierig, da die Beans unter der Kontrolle des Applikationsservers und nicht der Entwicklungsumbegung laufen. Hier helfen Debug-Ausgaben;
das Projekt Log4J der Apache Foundation leistet gute Dienste.
47
5 Persistenzoptionen von A-Entitäten
• Die EJB-Spezifikation ist noch im Fluss. Dies hat zur Folge, dass man entweder
mit einem Stand entwickelt, der bald abgelöst wird, oder mit einer Spezifikation, für die die Tool-Unterstützung (sowohl Entwicklungsumgebungen als auch
Applikationsserver) noch unzulänglich funktionieren. Die Hersteller von SEUs
und Applikationsservern sind zu beschäftigt damit, die neuen Versionen zu implementieren und können ihre Software nicht ausreifen lassen.
5.1.2 Zusammenfassung
Aus den Überlegungen der letzten Abschnitte kann folgender Schluss gezogen werden:
Entity-Beans sind T-Software, sie dürfen keine Anwendungs-Logik enthalten, um jede
Vermischung von A- und T-Software zu vermeiden und wartbare Systeme zu erstellen.
Allerdings eignen sich Entity-Beans durchaus, um Datenbankzeilen zu repräsentieren
und gleichzeitg transparenten Transaktionsschutz zu bieten. Deshalb können sie für
diesen Zweck gewinnbringend eingesetzt werden.
Die gleichen Überlegungen treffen auch auf die Aufteilung im Team zu: Beim naiven Einsatz von EJB muss jeder Entwickler Anwendungslogik und (EJB-) Technik
kennen. Wie dies zu vermeiden ist, zeigt der folgende Abschnitt.
5.2 Persistenz über native Java-Objekte
Im vorangegangenen Abschnitt wurden die Möglichkeiten untersucht, entsprechend
dem Vorschlag der EJB-Spezifikation die Anwendungsobjekte als Entity-Beans zu implementieren. Ein zweiter Weg besteht darin, den Anwendungskern nicht aus EntityBeans aufzubauen, sondern native Java-Klassen zu programmieren, die nicht an die
Restriktionen von Entity-Beans gebunden sind und eine längere Lebenserwartung
haben, da sie nur an fachlichen Anforderungen ausgerichtet sind, nicht auch noch
an technischen Gegebenheiten. Für die Implementierung der Persistenz von nativen
Java-Klassen existiert eine Vielzahl an Möglichkeiten, von denen die wichtigsten im
Folgenden beschrieben werden.
5.2.1 Ausprogrammieren mit JDBC
Man kann JDBC-Code von Hand schreiben. Dieser Weg wird in erstaunlich vielen
Projekten noch beschritten, obwohl er mühsam und fehleranfällig ist. Vorteil kann
sein, dass so gezielte, projektspezifische Optimierungen eingebaut werden können.
Zwei Varianten sind anzutreffen:
1. Jede Klasse kennt ihre Abbildung in die Datenbank, genauso wie sie beispielsweise ihre Repräsentation als String kennt. Das hat zur Folge, dass JDBC-Code
48
5.2 Persistenz über native Java-Objekte
überall im Programm verstreut wird; Lesbarkeit und Wartbarkeit leiden erheblich.
2. Datenbankzugriffs-Code wird zentralisiert; es handelt sich also um eine speziell für das Projekt entwickelte Zugriffsschicht. Das kann praktisch sein, wenn
entweder nur begrenzte Funktionalität erforderlich ist, oder ganz gezielte Optimierung des Datenbankzugriffs anders nicht möglich ist. Aber es drängt sich
die Frage auf, warum nicht gleich eine der in den folgenden Abschnitten beschriebenen Zugriffsschichten herangezogen wird. Es ist zu erwarten, dass eine
ausgereifte Zugriffsschicht – sie es ein Eigenbau oder ein kommerzielles Werkzeug – in der Performance nicht schlechter abschneidet, die Komplexität aber
ohne eine solche unnötig hoch ist.
Diesen Weg zu wählen, sollte sehr genau geprüft werden, denn sobald die A-Entitäten
komplex genug werden, kommt man hier wegen der Fehleranfälligkeit schnell an die
Grenzen.
5.2.2 Zugriffsschichten: QDI und TopLink
Für die Abbildung von nativen Java-Klassen auf eine relationale Datenbank existiert
eine Reihe von kommerziellen Werkzeugen wie z. B. TopLink, Avantis Persistency
Bridge, JavaBlend, Cocobase; die OpenSource-Community hält weitere Werkzeuge
bereit, z. B. DBGen, Jaws o. ä. Je nach Umfeld steht eventuell noch eine Eigenentwicklung zur Diskussion, wie z. B. bei sd&m Research das QDI. Eine Zugriffsschicht
ist in Abbildung 5.2 dargestellt.
Eine ideale“ Zugriffsschicht kapselt die Eigenschaften des Datenbanksystems so ge”
schickt, dass man ihr von außen nicht ansieht, dass sich dahinter eine relationale Datenbank verbirgt. Um diesem Anspruch gerecht zu werden, muss sie folgende Punkte
erfüllen. Die Anforderungen stammen einerseits aus dem Bereich der Objektorientierung, andererseits aus dem Datenbankbereich [He97, Kapitel 6], [Atk+89].
1. Transparenz: Klassen werden durch ein Schlüsselwort als persistent markiert.
Einzelobjekte können dynamisch als persistent markiert werden.
2. Datenbankobjekte werden mit einer objektorientierten Abfragesprache gelesen,
die in die Programmiersprache integriert ist und alle SQL-Funktionen unterstützt (auch datenbankspezifische Erweiterungen). Die Abfragen werden vom
Laufzeitsystem auf dynamisches SQL oder von einem Generator auf statisches
SQL abgebildet.
3. Vererbung ist uneingeschränkt möglich. Abschnitt 3.1.4 führt dieses Problem
aus.
49
5 Persistenzoptionen von A-Entitäten
Abbildung 5.2: 3-Schicht-Architektur mit Datenbank-Zugriffsschicht.
4. Beim Lesen aus der Datenbank werden die Fremdschlüssel-Beziehungen zwischen den Tabellenzeilen auf Objektzeiger abgebildet.
5. Die Objektidentität wird beachtet. Siehe dazu Abschnitt 3.1.3.
6. Daten werden automatisch gespeichert, die Anwendung gibt nur die Transaktionsgrenzen an.
7. Die Zugriffsschicht unterstützt die optimistische oder pessimistische Sperrstrategie. Siehe hierzu Abschnitt 3.2.1.
8. Die Zugriffsschicht verbirgt Tabellen- und Spaltennamen vor der Anwendung
und bildet Anwendungsdatentypen auf die elementaren Datentypen der Datenbank ab.
9. Ein DDL-Generator erzeugt die DDL (data definition language) aus der Definition der persistenten Klassen.
10. Man kann DDL und/oder DB-Zugriffe nachträglich optimieren, und zwar ohne
Eingriff in den Anwendungskern.
11. Die Zugriffsschicht kann zwischen Client und Server verteilt werden.
12. Die Leistungen der relationalen Datenbank sind uneingeschränkt verfügbar
(Views, Durchsatz, Mehrbenutzerfähigkeit, Berechtigungen, Abfragesprache,
Transaktionen, Zugriffsoptimierung, Recovery, Constraints, Trigger, Stored
Procedures, ...).
50
5.2 Persistenz über native Java-Objekte
Diese Anforderungsliste lässt freilich offen, wie die Zugriffsschicht diese Ziele erreichen soll. Klar ist, dass ihr irgendwie der Zusammenhang zwischen Objektmodell
und Tabellenmodell bekanntgegeben werden muss. Der Anwendung sollen zwar die
konkreten Eigenschaften der Datenbank verborgen bleiben sollen, nicht jedoch die
Tatsache, dass die Anwendungsdaten irgendwo abgelegt und von dort wieder gelesen
werden. Diese Bemerkungen schränken die Forderungen an eine ideale Zugriffsschicht
nicht ein.
Einsatz einer Datenbankzugriffsschicht
Prinzipiell ist abzuwägen zwischen den Kosten einer speziellen Lösung und denen
einer Zugriffsschicht. Eine spezielle Lösung wie in Abschnitt 5.2.1 verursacht Entwicklungs- und Wartungskosten. Bei Einsatz einer Zugriffsschicht sind Lizenzgebühren
fällig, bzw. bei Eigenentwicklung muss diese bezahlt werden; schließlich muss die
Einarbeitungszeit in das Werkzeug berücksichtigt werden.
QDI: Quasar Database Interface
Im Rahmen des Quasar-Projektes bei sd&m ist die Datenbankzugriffsschicht Quasar
”
Database Interface“ (QDI) entstanden. Der Entwurf des QDI wird geleitet durch die
Komponenten-Definition wie in [Si01]: Die Schnittstellen sind so schmal wie möglich
gestaltet, um technische Abhängigkeiten zu kapseln und um die Implementierung
austauschbar zu halten. Sie drücken alle wichtigen Konzepte aus, die zum Entwurf
eines datenbankbasierten Systems notwendig sind.
Was kann das QDI
Das QDI stellt als zentralen Teil der Schnittstelle das Workspace-Konzept zur Verfügung. Ein Workspace enthält die Methoden insert, update und delete zum Verwaltung von Objekten sowie beginTransaction, commit und rollback zur Verwaltung von
Transaktionen.
Dieses Konzept ermöglicht der Anwendung, den Transaktionsrahmen zu setzen; die
Implementierung des QDI bildet diese Transaktionen auf Datenbanktransaktionen
ab. So kann die Anwendung immer eine konsistente Sicht auf die Objekte behalten,
mit denen sie gerade arbeitet. Das QDI arbeitet mit einem optimistischen Transaktionskonzept, d. h. dass ein commit auch mit einem Fehler beenden kann. Darauf muss
die Anwendung zwar reagieren, aber der Workspace wahrt die konsistente Sicht auf
die bearbeiteten Objekte.
Das QDI bekommt das OR-Mapping deklarativ mitgeteilt und kapselt damit die
JDBC-Aufrufe. Es ist in der Lage, diese aus dem OR-Mapping selbst zu erzeugen
und nimmt so dem Programmierer einen erheblichen Teil der Arbeit ab.
51
5 Persistenzoptionen von A-Entitäten
Die Generalisierung der Datenbank-Eigenschaften ermöglicht die Austauschbarkeit
der darunterliegenden Datenbank genauso wie des verwendeten Treibers oder der
gesamten Implementierung. Ein Datenbank-Update oder ein neuer Treiber erfordert
keine Änderung der Anwendung; alle erforderlichen Anpassungen sind lokal auf die
QDI-Implementierung begrenzt.
Der Einsatz einer Schnittstelle, wie das QDI sie bietet, erleichtert auch die Strukturierung des Anwendungs-Codes. Betrachtet man den Einsatz irgendeiner Datenbank
als gegeben, so ist eine Schnittstelle, die nur die essentiellen Datenbank-Funktionen
nach außen trägt, 0-Software im Sinne der Software-Kategorien (vgl. Abschnitt 2.3.1).
Wie baut man es ein?
Bei der Verwendung einer Datenbank-Zugriffsschicht in einer objektorientierten Sprache sind prinzipiell vier Wege denkbar, um der Zugriffsschicht die Attribute zugänglich zu machen:
1. Erben von einer Superklasse bzw. Implementieren eines Interfaces mit Zugriffsmethoden auf die Attribute eines Geschäftsobjektes
2. Generische Mechanismen der Sprache (wie z. B. Reflection in Java)
3. Manipulation des Kompilats (der .class-Dateien bei Java, .o-Dateien bei C oder
C++)
4. Generierung des Zugriffscodes aus Metainformationen (z. B. JavaBlend )
Das QDI arbeitet nach der ersten Methode, um generische und sprachabhängige Konstrukte zu vermeiden. Deshalb muss jede Klasse der Anwendungslogik, die persistent
gemacht werden soll, ein Interface mit get- und set-Methoden zum Zugriff auf die
Attribute implementieren. Die Verbindung zwischen Klassenmodell und Datenbankschema erfolgt mit Hilfe eines sogenannten Repository, das einmalig zur Laufzeit
aufgebaut wird; Objekte werden mit Factories erzeugt.
Anfragen werden über ein Query-Objekt gestellt, das mit einem Workspace verbunden ist. Die gefundenen Objekte werden automatisch in diesen Workspace eingestellt.
So bilden Workspace und Query die Verbindung zur Datenbank.
Existierende Implementierungen
Als proof of concept“ wurde das QDI in verschiedenen Varianten implementiert:
”
• Als Dummy-Implementierung, die nur im Hauptspeicher und nicht gegen eine
Datenbank arbeitet
52
5.2 Persistenz über native Java-Objekte
• Auf JDBC aufbauend; zur Zeit mit Treibern für Oracle, MS Access und MySQL
• Auf JDBC in Verbindung mit dem Connection-Pool eines Applikationsservers
basierend
Die Tatsache, dass unterschiedliche Implementierungen erstellt wurden und tatsächlich austauschbar sind, zeigt die Mächtigkeit der Schnittstelle. Aktuell ist das QDI
in acht Projekten von sd&m im Einsatz und beweist seine Praxistauglichkeit.
TopLink
Das Produkt TopLink der Firma WebGain (vormals ObjectPeople) stellt leistungsfähige Werkzeuge für die objekt-relationale Abbildung für Java zur Verfügung. Es
arbeitet nicht-invasiv, d. h. im Programmcode der A-Entitäten muss der Einsatz von
TopLink noch nicht explizit vorgesehen sein. Statt dessen baut TopLink auf Reflection
auf.
Im Wesentlichen besteht TopLink aus einem grafischen Werkzeug (Workbench) zur
Eingabe der OR-Abbildung, und aus einem Laufzeitsystem, das die Zugriffe auf die
Datenbank ausführt und die benötigten Objekte erzeugt. Mit der Workbench werden
die Klassenstrukturen auf die Tabellenstrukturen abgebildet. Dieses Werkzeug kann
ein neues Datenbank-Schema erzeugen oder ein vorhandenes verwenden. Als Ausgabe
produziert es wahlweise einen Deskriptor im XML-Format, der die OR-Abbildung
beschreibt und vom Laufzeitmodul eingelesen wird, oder es erzeugt Klassen, welche
mit dem Projekt übersetzt werden und die OR-Abbildung zur Laufzeit vornehmen. So
bleiben Datenbankschema und Klassenmodell möglichst unabhängig voneinander; die
Klassen der Anwendungs-Entitäten bleiben unabhängig vom Werkzeug, da TopLink
nicht-invasiv vorgeht.
Zur Formulierung von Abfragen setzt TopLink mit der UnitOfWork ein Konzept
ein, das den Workspaces des QDI ähnelt, aber nicht genau die gleiche Semantik hat
bezüglich der Sicht auf die Objekte, die im Moment bearbeitet werden.
5.2.3 Exkurs: Objektorientierte Datenbanken
Mit der weiten Verbreitung der objektorientierten Programmiersprachen haben sich
auch objektorientierte Datenbanken entwickelt. Als Vorteile versprechen sie:
• Eine nahtlose Integration der Datenbank in die objektorientiert programmierte
Anwendung. Damit fällt das ganze OR-Mapping weg, und das spart natürlich
Entwicklungszeit und vermeidet mögliche Fehler, Kosten und ggf. Einarbeitungszeit für ein OR-Mapping-Tool.
53
5 Persistenzoptionen von A-Entitäten
• Das Datenbank-Schema wird durch das verwendete Klassenschema selbst beschrieben, also in der Programmiersprache der Anwendung. Der Designer muss
also nicht zwei, sondern nur noch ein Datenschema entwickeln.
• Alle Forderungen an eine ideale Zugriffsschicht“, s. Abschnitt 5.2.2, können
”
kompromisslos erfüllt werden, da ja kein Bruch mehr zwischen der Repräsentation in der Anwendung und in der Datenbank existiert.
• Effizienz: Eine allgemeine Aussage ist wohl nicht so einfach. Aber da in relationalen Datenbanken z. B. Vererbung nachgebildet werden muss und deshalb
in der Regel mehrere DB-Zugriffe erforderlich sind, um ein Objekt vollständig
auszulesen, sind OO-Datenbanken u. U. im Vorteil.
• Der wichtigste Vorteil von objektorientierten Datenbanken ist die Möglichkeit,
über Objektgeflechte zu navigieren. Diese Fähigkeit haben relationale Datenbanken nicht; das ist auch der Hintergrund, vor dem objektorientierte Datenbanken entstanden sind.
Neben den Vorteilen von OO-Datenbanken gibt es einige Argumente, die auch bei
Systemen mit objektorientierten Programmiersprachen nach wie vor für den Einsatz
von relationalen Datenbanken sprechen:
• Relationale Datenbanken sind besser verstanden
1. bei den Anwendungsentwicklern
2. auch bei den DB-Produkt-Herstellern. Daher sind RDBMS in der Regel
ausgereifter (stabiler und effizienter), die unterstützenden Tools (Monitore, Backup-Unterstützung, Schemamigration, Indexverwaltung, usw.) sind
besser (stabiler und besser auf Praxis-Anforderungen ausgerichtet)
3. bei Systemverwaltern und DBAs
Diese Argumente sind zwar nicht prinzipieller Natur, aber in der Praxis sind es
die schlagkräftigeren. Know-How bei den Anwendungsentwicklern ist ein sehr
wichtiger Punkt, spätestens aber die Qualität der Tools kann entscheidend sein
bei der Wahl des Systems.
• Sie sind vielseitiger einsetzbar. Es ist viel einfacher, mit verschiedenen Anwendungen in unterschiedlichen Programmiersprachen auf die DB zuzugreifen. Mit
OO-Datenbanken legt man sich auf eine Programmiersprache fest.
• Sehr oft ist man sowieso schon auf eine relationale Datenbank festgelegt.
Aus diesen Gründen haben objektorientierte Datenbanksysteme praktisch keine Bedeutung am Markt.
54
5.2 Persistenz über native Java-Objekte
Persönliche Einschätzung des Autors: Zur Zeit können OO-Datenbanken bei kleineren, vielleicht auch mittleren Systeme mit kürzeren Design- und Implementierungsphasen ( time to market“) durchaus Vorteile bringen. Für große Systeme allerdings
”
bieten sie genau nicht die Flexibilität, die sie mit der Objektorientierung versprechen.
5.2.4 Datenbankzugriff über Entity-Beans mit CMP
Dieser Ansatz folgt einer Idee des Fast-Lane-Reader -Patterns3 in [Sun01b] (s. Abbildung 5.3: Der Zugriff auf die Datenbank erfolgt über zwei Wege: Zum Lesen wird
nicht transaktionsgeschützt JDBC verwendet; zum Schreiben (sei es ein neu erzeugtes Objekt oder ein Update) werden Entity-Beans eingesetzt, um die Schreibaktionen
in den Transaktionskontext des aktuellen Aufrufes zu setzen. So kann das Lesen von
Objekten oder von Listen von Objekten ohne Sperren auf der Datenbank oder im
Applikationsserver erfolgen. Besonders bei Anwendungen mit hohen Client-Lasten
ist dies von hoher Bedeutung, denn z. B. Suchanfragen mit großen Ergebnismengen
werden nicht zum Performance-Engpass, weil nicht für alle gefundenen Datenbankeinträge gleich Entity-Beans erzeugt werden, sondern nur die Beans, mit denen der
Client tatsächlich arbeiten möchte. Lazy-loading-Strategien werden möglich und können fein gesteuert werden.
Zum schreibenden Zugriff wird der Transaktionsschutz des EJB-Containers ausgenutzt.
Fast-Lane Reader werden in der Regel so implementiert, dass vor dem Benutzer
verborgen bleibt, welcher Zugriffsweg gewählt wird. Sie liefern ein Value Object an
den Aufrufer zurück.
Abbildung 5.3: Fast-Lane Reader Pattern, wie es bei Sun [Sun01b] vorgestellt wird.
Vorteil dieses Weges ist, dass nur EJB-Hausmittel“ eingesetzt werden und einerseits
”
schneller, nicht gesperrter Zugriff erreicht wird, solange das von der Anwendung her
3
früher bekannt als Bimodal Data Access-Pattern
55
5 Persistenzoptionen von A-Entitäten
möglich ist, schreibender Zugriff aber dennoch transaktionsgeschützt stattfindet, ohne dass der Programmierer mit den Details des Transaktionsschutzes konfrontiert
wird. Nachteilig ist der doppelte Entwicklungsaufwand: Obwohl der Container verwendet wird, um auf die Datenbank zuzugreifen, muss die OR-Abbildung zusätzlich
in JDBC-Code gefasst werden. Dieser Nachteil kann aufgehoben werden bei einfach
strukturierten Daten, wenn es möglich ist, den Zugriffscode zu generieren.
Insofern wird das Data Access Object-Pattern (DAO) aus [Alu01] genau auf den
Kopf gestellt, da nicht Entity-Beans über ein DAO mit der Datenbank verbunden
werden, sondern umgekehrt Entity-Beans als DAO-Implementierung zur Abbildung
von A-Entitäten verwendet werden.
Die Idee der Entity-Beans als persistente Anwendungsobjekte wird damit untergraben, sie werden nur noch als Datencontainer verwendet. Diese Erkenntnis liegt im
Prinzip auch schon dem Value-Object-Pattern [Alu01] zugrunde.
56
6 Persistenz über ein neutrales Interface
6.1 Entwurf wartbarer Systeme mit EJB
Der prinzipielle Aufbau betrieblicher Informationssysteme wurde bereits in Kapitel
2 betrachtet. An dieser Stelle sei nochmals auf Abbildung 2.1 hingewiesen und an
die Begriffe Anwendungskern (AWK), Anwendungs-Entität (AE) und AnwendungsEntitäten-Verwalter (AV) erinnert. Diese Darstellung berücksichtigt aber nur die
fachliche Komponentenbildung, nicht jedoch die technischen Konstruktionen, die für
ein Funktionieren noch erforderlich sind: Die Speicherung der Daten in einer Datenbank, die Kommunikation mit mit Nachbarsystemen und die Darstellung der Benutzeroberfläche.
6.1.1 Innovationszyklen betrieblicher Informationssysteme
In betrieblichen Informationssystemen sind neben den A-Bausteinen auch T-Teile
erforderlich. Entscheidend ist, diese Teile sauber voneinander zu trennen. Informationssysteme unterliegen zwei unabhängigen Innovationszyklen: Sowohl die technischen
als auch die fachlichen Anforderung bzw. Gegebenheiten können sich ändern. Ist das
der Fall, so sollte nur ein genau definierter Teil des Quelltextes von den Änderungen betroffen sein. Werden hingegen A- und T-Aspekte vermischt (AT-Software),
so müssen diese Teile sowohl bei fachlichen als auch bei technischen Änderungen
überarbeitet werden. Dies ist zu vermeiden.
Daraus stellt sich die Frage, wie die technischen mit den fachlich motivierten Bausteinen zu verbinden sind, ohne A- und T-Software unzulässig zu vermischen und
gleichzeitig keine Performanz-Engpässe zu schaffen.
6.1.2 Entkoppelung des Anwendungskerns vom Datenbankzugriff über
ein neutrales Interface
Aus diesem Wunsch ergibt sich eine Architektur, wie sie in Bild 6.1 dargestellt ist.
Der Anwendungskern oder die Anwendungskomponente ist genau nach Abbildung 2.1
aufgebaut. Für jede Komponente existieren ein oder mehrere A-Verwalter, die alle
57
6 Persistenz über ein neutrales Interface
A-Entitäten dieser Komponente erzeugen, aus der Datenbank lesen und in die Datenbank schreiben. Für den Datenbankzugriff arbeiten sie gegen ein neutrales Interface,
das die technischen Eigenschaften der Datenbank vor ihnen verbirgt.
Abbildung 6.1: Entkopplung der Anwendungskomponente vom Datenbankzugriff
Das neutrale Interface ermöglicht es, den Datenbankzugriff an einer zentralen Stelle
zu konzentrieren. Es kann über einen der in Abschnitt 5.2 vorgestellten Wege in Form
eines Data Access Object (DAO) implementiert werden. So wird bezüglich des Datenbankzugriffs das Geheimnisprinzip (information hiding, [Par72]) umgesetzt, und
gleichzeitig stehen nach wie vor alle erwünschten Dienste des EJB-Applikationsservers
zur Verfügung.
In Abbildung 6.1 ist das neutrale Interface unabhängig von der Technik, aber von der
Anwendungslogik mitgeprägt, und das DAO wird als R-Software implementiert. Es
ist auch möglich, das neutrale Interface generisch als 0-Software zu implementieren,
das DAO ist dann reine T-Software.
Das neutrale Interface für eine Kundenverwaltung könnte wie in Listing 6.1 entworfen
werden.
Listing 6.1: Erste Vorstellung des neutralen Interfaces
public interface DAOCustomer {
// Suchen:
CustomerData findCustomerById (String id);
Collection findCustomerByName (String lastName);
58
6.1 Entwurf wartbarer Systeme mit EJB
:
:
// Aendern:
void updateCustomer (CustomerData customer) throws
MyOptimisticLockException;
// Loeschen:
void removeCustomer (String id);
void removeCustomer (CustomerData customer) throws
MyOptimisticLockException;
}
Der Anwendungskern wird in nativen Java-Klassen implementiert. Der Entwurf ist
frei von irgendwelchen Restriktionen, denen beispielsweise Entity-Beans ausgesetzt
wären. Er kann direkt nach den Prinzipien der Komponentenorientierung im Grobdesign bzw. der Objektorientierung im Feindesign erfolgen.
Mehrwert von EJB
Der Entwurf einer Architektur wie in Abbildung 6.1 wirft die Frage auf: Welcher
Mehrwert bleibt dann noch von EJB? Die Antwort ist einfach: Man bekommt einen
Applikationsserver, der u. a. folgende Dienste bietet:
• Ressourcen-Management: Pooling von Datenbankverbindungen, Pooling von
Objekten, um häufiges und teures Erzeugen und Löschen von Objekten zu
vermeiden, Threadverwaltung, um einer hohen Clientzahl gerecht zu werden.
• Erleichterung des Systembetriebs: Monitoring und Wartung des laufenden Systems, Lastverteilung, Ausfallsicherheit.
• ORB (Object Request Broker) zur Verteilung der Anwendung, MOM (Message
Oriented Middleware) zur asynchronen Kommunikation, Transaktions-Monitore, Web-Integration.
Diese Dienste müsste man ohne den Applikationsserver aus vielen Einzelprodukten
zusammenstellen, womit der Konfigurationsaufwand für das ganze System steigen
würde.
Mehrwert der vorgestellten Architektur
Parnas et al. gehen in [PCW83] auf die Kosten von Entwicklung und Wartung ein:
The primary goal of the decomposition into modules is reduction of software cost
”
by allowing modules to be designes and revised independently.“
59
6 Persistenz über ein neutrales Interface
Ein derart aufgebautes System erreicht genau dieses Ziel, indem es die Forderungen
von Parnas nach dem Geheimnisprinzip [Par72] und von Dijkstra [Dij76] nach der
Trennung von Zuständigkeiten auf Architekturebene umsetzt. Muss eine Änderung
oder Erweiterung in das System eingefügt werden, so ist nur ein lokal begrenzter Teil
des Systems davon betroffen.
Es ist leichter möglich, Optimierungen am Datenbankzugriff durchzuführen, denn
die Auswirkungen von Änderungen sind lokal begrenzt. A-Entitäten sind nicht von
einer Änderung im Datenbankschema oder -Zugriffsmechanismus betroffen. Ebenso
muss der Datenbank-Zugriff nicht überprüft werden, wenn fachliche Funktionalität
geändert wird. Im Idealfall bleibt völlig transparent, mit welchen Mitteln der Datenbankzugriff programmiert wird: Wird alles selbst erstellt, oder steht ein Werkzeug
(wie TopLink) zur Verfügung? Wenn sich eine solche Entscheidung im Projektverlauf
ändert, betrifft dies nur einen Teil des Programmcodes und nur einen Teil des Teams.
Diese Vorteile kommen nicht nur dem Programmcode an sich zugute, sondern haben
einen hohen Einfluss auf die Teamarbeit. Es ist leichter, das Team in Datenbank- und
Anwendungs-Experten aufzuteilen als in Teilteams für verschiedene Komponenten,
die sich dann alle um Datenbankzugriff und um Anwendungslogik kümmern müssen.
Daher trifft dieser Vorteil nicht erst in der Wartungsphase eines Projektes zu, sondern
schon während der Entwicklungszeit, da die Aufteilung des Teams entsprechend des
vorhandenen Wissens besser möglich ist.
Abbildung 6.1 sieht zwar etwas komplexer aus als die entsprechenden Darstellungen
für reine“ EJB-Architekturen, aber dieser Eindruck täuscht: Diese Architektur führt
”
nicht dazu, dass mehr Code benötigt wird. Sie legt lediglich fest, wohin der jeweils
für eine bestimmte Aufgabe benötigte Code geschrieben wird.
6.2 Anforderungen an das neutrale Interface
Der entscheidende Punkt dieser Architektur ist die Gestaltung des neutralen Interfaces. Dazu müssen zunächst die Anforderungen an ein solches Interface geklärt
werden:
• Der Anwendungskern wird von technischen Details des Datenbankzugriffs entkoppelt: Die Mächtigkeit der Datenbank bleibt genauso verborgen wie der eingesetzte Zugriffsmechanismus.
• Es enthält keine technik-getriebenen Elemente, ist also A- oder 0-Software.
• Der Anwendungskern darf nicht von den Einschränkungen der EJB-Vorgaben
betroffen sein.
• Die Dienste, die der EJB-Applikationsserver bietet, müssen aber dennoch zugänglich sein.
60
6.3 Design des neutralen Interfaces
6.3 Design des neutralen Interfaces
Diese Anforderungen erinnern an das Data Access Object-Pattern [Sun01b] (siehe Abbildung 6.2, die [Sch01] entnommen ist), welches auf dem Bridge-Pattern von Gamma
et al. [Gam94] beruht. Das DAO-Pattern will unterschiedliche Datenbank-Techniken
kapseln und vereinheitlichen; das neutrale Interface kapselt verschiedene Zugriffsmechanismen. Die Idee des DAO wird aufgegriffen und erweitert um das in Quasar
geforderte Konzept, an Schlüsselstellen immer gegen Interfaces und nicht gegen Klassen direkt zu programmieren. Nur durch die Definition eines technikunabhängigen
Interfaces kann der darauf zugreifende Code, in diesem Fall ein A-Verwalter, per
Konstruktion technikunabhängig sein. Die technikabhängigen Bausteine sind leicht
identifizierbar; es sind die Klassen, die das Interface implementieren.
Abbildung 6.2: Sequenzdiagramm für ein DAO
Für die Schnittstelle des DAO existieren zwei Varianten:
• Die Schnittstelle ist neutral (0-Software) und die Implementierung nur technikgetrieben mit generischem Zugriff auf die Anwendungsdaten (T-Software). So
ist das DAO-Pattern in [Alu01] beschrieben.
61
6 Persistenz über ein neutrales Interface
• Die Schnittstelle wird nur von der Anwendug motiviert (A-Software), die Implementierung ist technikgetrieben mit Wissen über die Anwendungsdaten (RSoftware). So findet sich das DAO-Pattern in [Sun01b]. Dies ist der in Abbildung 6.2 dargestellte Ansatz; Listing 6.1 ist nach dieser Variante aufgebaut.
Diese Entscheidung hat keine Auswirkungen auf die Architektur des Gesamtsystems,
wohl aber auf die Gestaltung des neutralen Interfaces. Änderungen am neutralen
Interface erfordern Anpassungen im A-Verwalter. Das ist zwar auch lokal begrenzt
und betrifft nicht den gesamten Anwendungskern, sollten aber dennoch vermieden
werden. Deshalb sollte diese Entscheidung früh getroffen werden, bevor mit der Codierung der A-Verwalter begonnen wird.
In [Sun01b] und auch in [Alu01] wird vorgeschlagen, das Business Object als EntityBean zu implementieren, welches über das DAO in die Datenbank geschrieben wird.
An dieser Stelle wird hier von dem Pattern abgewichen. Statt dessen sollen AEntitäten über das DAO in die Datenbank abgebildet werden. Es kann also die Idee
und der Ablauf aus Bild 6.2 übernommen werden, aber das Muster wird für native
Java-Objekte verwendet.
6.4 Implementierung des neutralen Interfaces
Das neutrale Interface ist dazu da, den Anwendungskern vom Datenbankzugriff zu
trennen. Für die Implementierung des Datenbankzugriffs gibt es eine Vielzahl an
Möglichkeiten. Hier wird auf Abschnitt 5.2 zurückgegriffen, der verschiedene Wege
zur Abbildung von nativen A-Entitäten auf die Datenbank im Detail vorgestellt hat;
ihre Einsatzmöglichkeiten zur Implementierung des neutralen Interfaces werden hier
diskutiert.
6.4.1 Spezifische JDBC-Lösung
Wie schon in Abschnitt 5.2.1 dargelegt, ist der naive Ansatz, für jede A-Entität das
OR-Mapping in JDBC-Code zu fassen und einzeln auszuprogrammieren.
Im generischen Fall (neutrales Interface als 0-Software, DAO als reine T-Software)
handelt es sich um nichts anderes als eine projektspezifische Zugriffsschicht, wie sie
auch sonst (d. h. ohne EJB) eingesetzt würde. Wird sie zu speziell gehalten, könnte die Pflege aufwändig werden. Ist der Ansatz dagegen zu allgemein, könnte die
Entwicklung teurer werden als die Verwendung einer vorhandenen Zugriffsschicht.
Listing 6.2 zeigt für eine Implementierung für das Interface in Listing 6.1. Muss solcher Code manuell entwickelt werden, schleichen sich schnell Fehler ein, die der Compiler nicht finden kann und die womöglich erst zur Laufzeit auftreten. Hier handelt
es sich um AT-Software, bestenfalls um R-Software.
62
6.4 Implementierung des neutralen Interfaces
Listing 6.2: Beispiel für eine JDBC-Implementierung
import java.sql.*;
public class DAOCustomerJDBC implements DAOCustomer {
public CustomerData findById (String customerId)
{
Connection dbc;
Statement stmt;
try {
dbc = myDataSource.getConnection (
"myLogin", "myPassword");
stmt = dbc.prepareStatement (
"SELECT id, version, lastname, firstname " +
"FROM customer WHERE id = ?");
stmt.setString (1, id);
ResultSet rs = stmt.executeQuery ();
if (rs.next ()) {
CustomerData cd = new CustomerData ();
cd.setId (rs.getString ("ID"));
cd.setVersion (rs.getInt ("VERSION"));
cd.setLastName (rs.getString ("LASTNAME"));
cd.setFirstName (rs.getString ("FIRSTNAME"));
// usw.
}
return cd;
}
finally {
dbc.close ();
}
}
// ...
}
6.4.2 Einsatz einer Datenbankzugriffsschicht
Der Vorteil von Zugriffsschichten wurde in Abschnitt 5.2.2 hervorgehoben.
63
6 Persistenz über ein neutrales Interface
Umsetzung mit QDI
Entstanden ist das QDI ohne die Idee, in Zusammenhang mit einem Applikationsserver verwendet zu werden. Aber durch die Verwendung des Datenbank-ConnectionPools eines J2EE-Servers kann das QDI problemlos mit dem Applikationsserver zusammenarbeiten und dessen Transaktionsverwaltung ausnutzen. Das QDI schreibt
mit seinem Workspace-Konzept nur die Daten in die Datenbank zurück, die tatsächlich geändert wurden. Der Applikationsserver kann am Ende seines Transaktionsrahmens entscheiden, ob die Datenbanktransaktion mit commit oder rollback
abgeschlossen wird.
Die Vorteile sind:
+ Bewährtes und mächtiges OR-Mapping
+ OR-Mapping an genau einer Stelle konzentriert
+ Weitere mächtige Konzepte, wie z. B. Unterstützung für große Treffermengen,
sind vorhanden und können verwendet werden.
Listing 6.3: Beispiel für eine Implementierung mit dem QDI
import com.sdm.quasar.qdi.*;
public class DAOCustomerQDI implements DAOCustomer {
public void initRepository (IQRepository rep)
{
QRecordDef customer = new QRecordDef (CustomerData.QNAME,
"CustomerTable", new CustomerDataFactory());
customer.setIdentifier (CustomerData.ID, "id", "id",
new QStringFieldDef (32));
customer.setEntryDef (CustomerData.VERSION, "version",
"version", new QIntegerFieldDef (32), 1);
customer.setEntryDef (CustomerData.FIRSTNAME, "firstname",
"firstname", new QStringFieldDef (32), 1);
customer.setEntryDef (CustomerData.LASTNAME, "lastname",
"lastname", new QStringFieldDef (32), 1);
rep.register (CustomerData.QNAME, customer);
}
public CustomerData findById (String customerId)
{
IQuery query = ws.createQuery(CustomerData.QNAME, 0);
64
6.4 Implementierung des neutralen Interfaces
query.addEquality("id", customerId);
ForwardIterator it = query.start();
if (it.atEnd()) {
return null;
}
CustomerData customer = (CustomerData) it.get();
return customer;
}
// ...
}
Das Code-Beispiel 6.3 zeigt in der Methode initRepository die Initialisierung der
OR-Abbildung. Sie wird vom QDI verwendet, um SQL-Befehle zu erzeugen und die
benötigten Objekte zu erzeugen. Die Beschreibung des OR-Mappings ist getrennt
von der Formulierung der Abfrage, welche sehr kompakt formuliert werden kann.
Umsetzung mit TopLink
Der Einsatz eines bewährten Werkzeuges wie TopLink hat einige Vorteile: Als Datenbankzugriffsschicht konzentriert es die komplette OR-Abbildung an einer Stelle; das
OR-Mapping ist sehr mächtig; es ist in der Lage, auch komplexe A-Entitäten direkt
in die Datenbank abzubilden.
Wenn schon ein Werkzeug wie TopLink zur Verfügung steht, drängt sich die Frage
auf, warum es nicht gleich als Container eingesetzt wird, um komplexer strukturierte
Beans zu erstellen. Hier sei deshalb nochmals auf die Abschnitte 5.1.2 und 6.1.2 verwiesen. Ziel ist es, die A-Komponenten frei von technischen APIs zu implementieren.
Mit TopLink als Container würde diese Ziel nicht erreicht.
6.4.3 Umsetzung mit CMP
Wird ein System als EJB-Anwendung erstellt, bietet sich die Implementierung des
DAO nach dem Fast-Lane Reader-Pattern an, das in Abschnitt 5.2.4 beschrieben
wurde. In Systemen, in denen es sinnvoll ist, einzelne Tabellenzeilen als Entity-Beans
darzustellen und keine aufwändigen Objektgeflechte benötigt werden, bewährt sich
diese Lösung im produktiven Einsatz. Bei solchen Systemen ist es möglich, sowohl die
benötigten Entity-Beans als auch den typischerweise benötigten JDBC-Zugriffscode
aus dem Datenbankschema zu generieren; nur noch an wenigen Stellen ist gezielte
Handarbeit erforderlich.
65
6 Persistenz über ein neutrales Interface
Abbildung 6.3: Sequenz-Diagramm der Implementierung mit dem Fast-Lane-ReaderPattern
Abbildung 6.3 zeigt einerseits, dass zum Lesen und zum Schreiben zwei verschiedene
Wege gewählt werden, und es zeigt andererseits, dass diese Tatsache hinter einem
DAO versteckt wird. Für den Client ist es transparent, dass Lesen und Schreiben
unterschiedlich implementiert sind.
6.5 Beurteilung des Entwurfs
Die verschiedenen Möglichkeiten, den Datenbankzugriff zu implementieren, werden
unter den Gesichtspunkten verglichen, die sich aus der Aufgabenstellung dieser Arbeit
ergeben:
• Wartbarkeit des Codes
• Unabhängigkeit von aktueller EJB-Spezifikation und von herstellerspezifischen
Features
• Effizienz bei komplexen Komponenten, komplexen Anwendungsfällen (Transaktionen), bei großen Datenbanken, bei vielen Benutzern
6.5.1 Wartbarkeit des Codes
Die Wartbarkeit von Programmcode ist umso leichter, je unabhängiger verschiedene
Programmteile voneinander sind, da notwendige Änderungen entweder der fachlichen
Anforderungen oder der technischen Gegebenheiten nur lokale Auswirkungen haben.
Diese Unabhängigkeit wird durch die hier diskutierte Architektur erreicht. Die Variante über den Fast-Lane Reader“ schneidet in diesem Punkt aber schlechter ab,
”
66
6.5 Beurteilung des Entwurfs
denn es müssen zwei unterschiedliche Wege zum Lesen und zum Schreiben von Daten
programmiert werden. Die Lösungen mit einer Zugriffsschicht sind kompakter.
Weiterhin hängt die Wartbarkeit schlicht von der Lesbarkeit des Quelltextes ab.
Auch hier liegen die Zugriffsschichten vorne, denn die Aspekte Objekterzeugung,
OR-Abbildung und Transaktionsabbildung sind klarer und kompakter zu formulieren. JDBC-Code, der für den Fast-Lane Reader erforderlich ist, vermischt diese Dinge etwas mehr. Dies wird bei komplexeren Abbildungen der Datenmodelle oder der
Transaktionen zu einem Nachteil.
Andererseits hängt die Lesbarkeit nicht nur von der Kompaktheit ab, sondern auch
davon, wieviel Kenntnisse der Leser (also der Programmierer) mitbringen muss. Der
Einsatz einer Zugriffsschicht verlangt in Entwicklung und Pflege detailliertes Wissen
über Benutzung, Nebeneffekte und Performance-Engpässe des verwendeten Produkts.
Die Lösung mit Entity-Beans könnte im Vorteil sein, da sie nur mit den Standardmitteln von EJB arbeitet.
6.5.2 Unabhängigkeit von der verwendeten Basistechnologie
Unabhängigkeit von der Basistechnologie ist im Prinzip auch ein Aspekt von Wartbarkeit. Die Applikation ist leichter über Versionswechsel der eingesetzten Basissysteme
(Datenbanken, Applikationsserver) zu pflegen, je besser hersteller- und versionsspezifische Besonderheiten konzentriert und gegen den Rest der Anwendung abgekapselt
sind. Auch hierfür leistet die vorgestellte Architektur ihren Beitrag. Das wichtige
Ziel, den anwendungsorientierten Programmcode unabhängig von Datenbank und
Applikationsserver zu halten, wird erreicht.
6.5.3 Effizienz
Welche Variante bessere Antwortzeiten ermöglicht, hängt in hohem Maße von den
Anforderungen an das System ab. Jede Variante kann so implementiert werden, dass
keine unnötigen Performance-Engpässe erzeugt werden. Sie verhalten sich aber unterschiedlich, und es kann keine allgemeine Reihenfolge angegeben werden. Abschnitt
7.2.6 zeigt ein Beispielszenario für die vorgestellten Implementierungen, das als Entscheidungshilfe für ein konkretes Projekt verwendet werden kann.
6.5.4 Zusammenfassung
Die Vorteile der hier dargestellten Architektur sind:
+ Die Anwendungskomponenten können frei entworfen werden, unabhängig vom
Datenbankschema, vom Zugriffsmechanismus und von den Fähigkeiten des Datenbanksystems. Komplexes OR-Mapping ist möglich mit Vererbung, abhängigen Objekten usw.
67
6 Persistenz über ein neutrales Interface
+ Die Teamaufteilung kann nach Expertenwissen erfolgen.
+ Im DAO gekapselter Datenbank-Zugriffscode: Optimierungen sind leichter zu
implementieren (lazy-/eager loading, Denormalisierung, usw.)
Als Nachteil könnte man anführen:
− Da der Ansatz komplexer aussieht als Lehrbuchbeispiele, ist zunächst eine psychologische Hemmschwelle zu überwinden, diesen Ansatz doch zu verfolgen. Es
könnte passieren, dass der Kunde erst vom Nutzen zu überzeugen ist.
Zur Wahl der Implementierung des DAO sind hier nochmal die drei vorgestellten
Varianten gegenübergestellt.
Ansatz
Spezielle
Lösung
JDBC-
Fast-Lane Reader
Zugriffsschicht
68
Indikatoren
sollte bei größeren Projekten nicht verwendet werden.
Einfaches OR-Mapping,
spezielle Optimierungen auf
JDBC-Ebene erwünscht,
keine Zugriffsschicht verfügbar
Komplexes OR-Mapping,
großes Projekt,
lange Lebensdauer
Vorteile
Nur Standardmittel von
EJB verwendet,
kein zusätzlicher Einarbeitungsaufwand in ein weiteres Werkzeug
Vielseitiges OR-Mapping
möglich,
keine Einschränkung beim
Entwurf der A-Entitäten,
Optimierungen und Features der Zugriffsschicht
nutzbar
7 Fallstudie: Telekom-Billing-System
Das Telekom-Billing-System (TBS) ist entstanden, um ein vereinfachtes, aber dennoch realitätsnahes Informations-System umzusetzen, das bei sd&m Research zu
Forschungs- und internen Schulungszwecken eingesetzt werden kann. Es setzt die
Ideen der Standard-Architektur Quasar ein. Für dieses System existieren verschiedene Implementierungen; im Rahmen dieser Diplomarbeit wurde eine weitere, auf EJB
basierende Implementierung neu erstellt. Parallel hierzu ist eine weitere Diplomarbeit
im Bereich GUI (Web und nativ) entstanden [Ha01], die auf dem hier entwickelten
Anwendungskern aufsetzt. So konnte gezeigt werden, dass dieser Ansatz in eine Gesamtarchitektur integrierbar ist. Mit dieser Anwendung sind auch die Screenshots in
diesem Kapitel entstanden.
Da es sich beim TBS um einen extra für diese Arbeit angefertigten Prototyp handelt,
wurde das Modell so gewählt, dass bestimmte technische Herausforderungen gezielt
gesucht und ihre Lösung demonstriert werden können. Dazu gehören:
• Wahl von übersichtlichen Komponenten, die aber größer als eine einzelne Klasse
sind
• Vererbung: OR-Mapping und polymorphe Suche
• Umgang mit Beziehungen und mit abhängigen Objekten
• Lazy vs. eager loading
• Gestaltung der Schnittstelle und der Transferobjekte
• Interaktive Anwendungsfälle und Batch-Verarbeitung
• Optimistisches Sperrkonzept
Die Liste ließe sich erweitern, z. B. um lang laufende Transaktionen und große Treffermengen. Im Interesse der Übersichtlichkeit wurde aber der Umfang des Prototypen
eingegrenzt.
Zunächst wird das fachliche Modell beschrieben, anschließend werden zwei unabhängige Implementierungen vorgestellt und mit ihren Vor- und Nachteilen einander
gegenübergestellt.
69
7 Fallstudie: Telekom-Billing-System
7.1 Überblick: Die Komponenten des TBS
Das Telekom-Billing-System ist dazu gedacht, für einen Telekommunikationsanbieter
Rechnungen zu erstellen. Eingangsdaten sind dazu die Gesprächsdaten (CallDataRecords), die von den Ortsvermittlungsstellen erfasst und an dieses System weitergegeben werden. Zum TBS gehören eine Kundenverwaltung, eine Rechnungsverwaltung
und eine Tarifkomponente. Abbildung 7.1 stellt die Komponenten schematisch dar.
Abbildung 7.1: Überblick über die Komponenten des TBS
7.1.1 Kundenverwaltung
Die A-Entitäten
Ein Kunde kann entweder Privat- oder Geschäftskunde sein. Im objektorientierten
Modell wird dies durch Vererbung ausgedrückt. Ein Kunde sei abstrakt und tritt
entweder in der Ausprägung Privatkunde oder Geschäftskunde auf. Damit wird eine
polymorphe Suche benötigt.
Gemeinsame Attribute der Kunden sind:
• Kundennummer: Dies ist die eindeutige ID (Primärschlüssel).
• Name
70
7.1 Überblick: Die Komponenten des TBS
• Telefonnummern: u. U. mehrere. Die Telefonnummer ist keine eindeutige Identifikation, da eine Nummer über die Zeit an mehrere Kunden vergeben werden
kann. Die Suche über die Telefonnummer ist aber möglich und führt zu einem
eindeutigen Ergebnis.
• Vereinbarter Tarif
Unterschiedlich bei Privat-/Geschäftskunden sind die folgenden Attribute:
Privatkunde
zwei Adressen: Heimat- und Rechnungsanschrift
Geschäftskunde
n Niederlassungen, 1 Stammsitz, also
n + 1 verschiedene Anschriften
Ansprechpartner: einer pro Geschäftskunde
Abbildung 7.2 zeigt die A-Entitäten der Kundenverwaltung als Klassendiagramm.
Abbildung 7.2: Klassendiagramm der A-Entitäten der Kundenverwaltung
Der A-Verwalter
Der A-Verwalter der Kundenkomponente ist sowohl für die Privatkunden als auch
für die Geschäftskunden zuständig.
71
7 Fallstudie: Telekom-Billing-System
Die Anwendungsfälle
Name des AWF
Kunde suchen
Kunde suchen
Parameter
Kunden-Nummer
Nachname
Kunde neu eintragen
Kunde löschen
Kunde ändern
KundenTransferstruktur
Kunden-Nummer
KundenTransferstruktur
Ergebnis
Kunde
polymorphe Liste
von Kunden, die
leer sein kann
Exception
Nicht gefunden
keine
Primärschlüssel
schon vorhanden
keine
Optimistischer
Sperrkonflikt
Die Anzeige eines Kunden ist in Abbildung 7.3 abgebildet.
Abbildung 7.3: Pflege eines Kundendatensatzes
Wird ein Kunde über seinen Nachnamen gesucht, so werden alle zutreffenden Kunden in einer Auswahlliste angezeigt (s. Abbildung 7.4). Es kann ein Kunde gewählt
werden, mit dem dann wie in Abbildung 7.3 weitergearbeitet werden kann.
Methoden, die z. B. den vereinbarten Tarif eines Kunden ermitteln, sind keine Anwendungsfälle, sondern Methoden auf der A-Entität bzw. Attribute des Transferobjektes.
72
7.1 Überblick: Die Komponenten des TBS
Abbildung 7.4: Suche eines Kunden über den Nachnamen
7.1.2 Rechnungsverwaltung
Die A-Entitäten
Eine Rechnung hat folgende Eigenschaften:
• Sie besitzt einen Abrechnungszeitraum,
• eine eindeutige Rechnungs-Nummer,
• ist einem Kunden zugeordnet und
• ist aufgeschlüsselt nach den Telefonnummern des Kunden.
• Auf Basis des vom Kunden gewählten Tarifs setzt sie sich zusammen aus
– der Grundgebühr und
– einer Liste von Rechnungsposten: den einzelnen Gesprächen.
Sie wird erzeugt aus den Gesprächsdaten, die von der der Ortsvermittlungsstelle in
die Datenbank eingetragen werden.1 Bei der Rechnungserstellung wird lesend auf
diese Daten zugegriffen.
1
Im Beispielsystem sind diese Daten natürlich zufällig generiert.
73
7 Fallstudie: Telekom-Billing-System
Abbildung 7.5: Klassendiagramm der Rechnungskomponente
Die einzige A-Entität der Rechnungsverwaltung ist die Rechnung. Weiterhin gibt es
noch die Rechnungsposten und die eben erwähnten Gesprächsdaten; das sind aber
abhängige Objekte und keine A-Entitäten. Abbildung 7.5 zeigt die Datenstruktur
innerhalb der Rechnungsverwaltung.
Die Anwendungsfälle
Neben den einfachen Anwendungsfällen, eine Rechnung zu suchen oder zu löschen
gibt es hier auch einen A-Fall mit etwas mehr algorithmischer Komplexität: Das
Erstellen der Rechnung. Dazu werden die Gesprächsdaten gesucht, die in den Abrechnungszeitraum fallen, um daraus die Rechnung zusammenzustellen.
Name des AWF
Rechnung suchen
Rechnung suchen
Parameter
Rechnungs-ID
Kunden-ID
Rechnung erstellen
Rechnung löschen
Kunden-ID, Zeitraum
Rechnungs-ID
Ergebnis
Rechnung
Liste von Rechnungen, die leer
sein darf
keins
keins
Exception
Nicht gefunden
keine
Rechnung schon
vorhanden
Nicht gefunden
CallDataRecord
Die Gesprächsdaten werden CallDataRecord genannt und enthalten die anrufende
und die angerufene Telefonnummer sowie den Beginn und die Dauer des Gesprächs.
74
7.2 Implementierung
Diese Informationen sind erforderlich, um den Preis eines Gespräches zu ermitteln.
CallDataRecords werden nicht als eigene Komponente aufgefasst. Die Annahme ist,
dass diese Daten von einem Nachbarsystem in unsere Datenbank eingetragen werden. Sie werden vielmehr nur von der Rechnungskomponente benötigt, welche auch
genau auf die CallDataRecords abgestimmt ist. Rechnungsposten haben z. B. eine
sehr ähnliche Struktur. Deshalb werden CallDataRecords der Rechnungskomponente
zugeordnet.
7.1.3 Tarif
Das Telekom-Unternehmen bietet verschiedene Tarife an, von denen jeder Kunde
einen wählt. Sie werden beschrieben durch:
• Name/Bezeichnung
• Grundgebühr
• Zeittaktung (z. B. 60/1 oder 60/60 usw.)
• Preise pro Tarifsegment (Tag/Abend/Nacht/Wochenende etc)
Die Tarifkomponente verwaltet alle Tarife; die Preisberechnung eines Telefongesprächs
erfolgt durch eine A-Entität des betreffenden Tarifs.
7.2 Implementierung
Die Implementierung besteht aus einem Anwendungskern mit Anwendungsfällen, AEntitäten und A-Verwaltern, den Transferobjekten und dem Zugriff auf die Datenbank. Es existiert eine Schnittstelle für das DAO; für das TBS wurde diese Schnittstelle fachlich gestaltet. Denkbar wäre auch eine neutrale Gestaltung.
Für das DAO werden zwei unabhängige Implementierungen vorgestellt: Eine mit
Entity-Beans und dem Fast-Lane-Reader-Pattern, die andere mit dem QDI. Des
weiteren existieren jeweils noch Dummy-Implementierungen, die es erlauben, den
Anwendungskern oder die GUI zu testen, während parallel die echte Datenbankanbindung entwickelt wird.
7.2.1 Überblick über die Klassen des Anwendungskerns
Anwendungsfälle
Jeweils für die Anwendungsfälle einer Komponente existiert ein stateless SessionBean. Dies stellt eine Vereinfachung gegenüber der vorgestellten Klassenstruktur dar,
75
7 Fallstudie: Telekom-Billing-System
da die Anwendungsfälle direkt im Session-Bean implementiert sind und nicht die
Aufrufe, wie in Abbildung 6.1 dargestellt, an eine native Klasse weitergereicht werden.
Diese Vereinfachung wurde vorgenommen, da keine der Methoden länger als zehn
Zeilen ist; die meisten enthalten nur eine einzige Zeile, die den Aufruf an den AVerwalter weitergibt.
Zur Schnittstelle des Anwendungskerns zum Client hin gehören neben den Anwendungsfällen noch die Transferobjekte. Sie sind reine Datencontainer (Value Objects),
die zwischen Anwendungskomponente und Client hin- und hergereicht werden. Für
jede Komponente gibt es Transferobjekte; in diesem Beispiel sehen sie sehr ähnlich
aus wie die A-Entitäten – nur ohne fachliche Methoden, lediglich mit get- und setMethoden für die einzelnen Attribute.
Die Anwendungsfälle sind in den Session-Beans CustomerUCBean, InvoiceUCBean
und TariffUCBean implementiert. Als Schnittstelle werden dem Client das Homeund das Remote-Interface bekannt gegeben, ebenso der JNDI-Name2 , um das Bean
zu finden.
A-Verwalter und A-Entitäten
Der A-Verwalter ist dafür zuständig, aus der Datenbank benötigte Daten über das
DAO-Interface zu lesen und A-Entitäten zu erzeugen. Diese Aufrufe sind in der Regel
trivial. Der aufwändigste Anwendungsfall ist die Erzeugung einer Rechnung, denn
hierzu müssen erst alle Telefonnummern eines Kunden herausgefunden werden, dann
alle betreffenden CallDataRecords gesucht werden; anschließend wird eine Rechnung
erzeugt und für jeden CallDataRecord eine Rechnungsposition. Die Tarifkomponente
berechnet zu jedem Gespräch den Preis; dies hängt vom Tarif ab, den der Kunde
gewählt hat. Für alle anderen Anwendungsfälle reicht der A-Verwalter die Aufrufe
lediglich an das DAO durch.
Die fachliche Logik verteilt sich also auf die Anwendungsfälle, die den Ablauf steuern,
die A-Entitäten, die bestimmte Operationen auf ihren Attributen beherrschen, und
die A-Verwalter, die für den Aufbau der A-Entitäten zuständig sind.
A-Verwalter sind mit AV<Komponente> benannt, also AVCustomer, AVInvoice und
AVTariff. Die A-Entitäten heißen analog AECustomer, AEInvoice und AETariff;
die Kundenkomponente arbeitet mit unterschiedlichen Typen von Kunden, die mit
AEPrivateCustomer und AEBusinessCustomer bezeichnet werden und von der abstrakten Klasse AECustomer erben.
Kundenverwaltung
Die Implementierung der Kunden mit Hilfe von Vererbung ermöglicht es dem AVerwalter, genau die richtigen Objekte zu konstruieren, während die meisten Anwen2
JNDI: Java Naming and Directory Interface, siehe z. B. [Ro99]
76
7.2 Implementierung
dungsfälle nicht zwischen Privat- und Geschäftskunden unterscheiden müssen und
deshalb mit der abstrakten Basisklasse AECustomer arbeiten können. Bild 7.6 zeigt
die Klassenstruktur der Anwendungskomponente.
Abbildung 7.6: Klassenstruktur der Kundenkomponente
An dieser Stelle wird nochmals deutlich, dass eine naive Implementierung auf EntityBeans basierend nicht möglich wäre. Hier wird die Kunden-Komponente genau der
fachlichen Motivation entsprechend mit den Mitteln der Objektorientierung umgesetzt.
Rechnungsverwaltung
Da jede Rechnung aus dem Rechnungskopf und den Einzelgesprächsnachweisen besteht, ist die Besonderheit in der Rechnungskomponente, mit diesen abhängigen Objekten richtig umzugehen. Je nach Anwendungsfall werden sie aus der Datenbank
gelesen oder nicht. Werden z. B. alle Rechnungen eines Kunden gesucht, so sind die
abhängigen Objekte nicht interessant. Erst wenn eine Rechnung vollständig angezeigt
werden soll, müssen alle Einzelposten geladen werden.
Der A-Verwalter (AVInvoice) muss also die Möglichkeit bieten, zwischen lazy loading
und eager loading zu unterscheiden. Das Erstellen einer Rechnung obliegt vollständig
dem A-Verwalter und ist eine umfangreiche Aufgabe. Erst prüft er, ob für den Abrechnungszeitraum schon eine Rechnung existiert. Wenn nicht, werden die CallDataRecords im entsprechenden Zeitraum aus der Datenbank gelesen, aus ihnen jeweils
ein InvoiceItem erzeugt und einem neue A-Entität AEInvoice zugeordnet.
Über die Kundenkomponente wird der vom Kunden gewählte Tarif ermittelt; von
der Tarifkomponente wird ein AETariff erfragt, das die Preisberechnung für jedes
Gespräch durchführt.
77
7 Fallstudie: Telekom-Billing-System
Tarifverwaltung
Die Berechnung der Gesprächspreise geschieht in einer Instanz eines AETariff. Alternativ wäre auch möglich gewesen, diese Methode in die Schnittstelle des Anwendungsfalles zu stellen. So wird ein AETariff an den AVInvoice weitergegeben. Diese
Entscheidung kann nach den Überlegungen zum Komponentenentwurf in Kapitel 2
durchaus diskutiert werden.
7.2.2 Das neutrale Interface
Listing 7.1 zeigt das neutrale Interface der Kundenkomponente. Es musste nach dem
ersten Wunsch, der in Listing 6.1 gezeigt wurde, nicht mehr geändert werden. Auf
dieser Grundlage ist die Implementierung ohne Probleme möglich.
Listing 7.1: Das neutrale Interface
package com.sdm.tbs.ac.customer3.impl;
import com.sdm.tbs.uc.ifc.CustomerData;
import com.sdm.tbs.uc.ifc.PhoneNumber;
import java.util.Collection;
import com.sdm.tbs.ac.util.TBSApplException;
import com.sdm.tbs.ac.util.TBSSystemException;
/**
* DAOCustomer
*
* Neutrales Interface fuer die Kundenverwaltung.
* Hier als A-Software formuliert.
* ts, 17.09.01
*/
public interface DAOCustomer
{
/* insert / create */
void insertCustomer (CustomerData customer)
throws TBSApplException, TBSSystemException;
/* find */
CustomerData findById (String customerId)
throws TBSApplException, TBSSystemException;
CustomerData findByPhoneNumber (PhoneNumber ph)
throws TBSApplException, TBSSystemException;
78
7.2 Implementierung
Collection findByName (String lastname)
throws TBSApplException, TBSSystemException;
/* weitere Suchanfragen denkbar */
/* remove */
void removeCustomer (CustomerData customer)
throws TBSApplException, TBSSystemException;
/* update */
void updateCustomer (CustomerData customer)
throws TBSApplException, TBSSystemException;
}
Für jede Komponente exisitert ein analog aufgebautes Interface. Im Folgenden werden
zwei Implementierungen dieses Interfaces vorgestellt.
7.2.3 Implementierung 1: Datenbankzugriff über Entity-Beans
Die Implementierung nach dem Fast-Lane-Reader -Pattern erfordert sowohl die Erstellung eines Entity-Beans als auch den JDBC-Code für den lesenden Zugriff. Man
beachte, dass kein JDBC für den Schreibzugriff benötigt wird. Insofern kann dies
nicht als Verdoppelung des Aufwandes betrachtet werden.
Erfolgt die Erstellung des JDBC-Codes manuell, so ist das mühsam und fehleranfällig. Ein produktives sd&m-Projekt generiert für eine Variante dieses Weges einen
großen Teil des Codes aus dem Datenbankschema. Damit ist der Datenbankzugriff
fast vollständig generiert, und die Abhängigkeit im Anwendungskern ist sehr gering,
und die Performanz dieser Lösung genügt den Anforderungen eines hochverfügbaren
Buchungssystems.3
Für das TBS entspricht die Implementierung ganz genau der Abbildung 6.3. Der
Quelltext wurde von Hand geschrieben und nicht generiert.
7.2.4 Implementierung 2: Datenbankzugriff über das QDI
Der Code ist kompakter, leichter lesbar und somit besser wartbar – zumindest für Programmierer, die das QDI kennen. In großen Projekten kann das Team entsprechend
aufgeteilt werden, so dass dies keine Einschränkung darstellt. Wie die Darstellung
der Praxiserfahrungen (siehe Abschnitt 7.2.6) zeigt, ist dieser Weg sehr empfohlen
und sollte von jedem Projekt geprüft werden.
Listing 6.3 zeigt einen Ausschnitt aus der Implementierung des DAO mit Hilfe des
QDI.
3
Es handelt sich um ein sd&m-Projekt im produktiven Einsatz, das ca. 80.000 Buchungen pro
Stunde durchführt.
79
7 Fallstudie: Telekom-Billing-System
7.2.5 Vergleich der zwei Implementierungen
Zunächst ist festzuhalten, dass hier zwei unabhängige Implementierungen für die
selbe Architektur erfolgreich umgesetzt werden. Die in [Si01] aufgestellte Forderung
nach der Austauschbarkeit der Implementierung wird damit erfüllt und unterstreicht
die Wartbarkeit des so erstellten Systems.
Es ist nicht pauschal zu beantworten, welche Implementierung besser“ ist als die
”
andere. Die Implementierung über Entity-Beans nach dem Fast-Lane-Reader-Pattern
hat sich neben dem hier implementierten Prototyp auch in der Praxis bewiesen. Aus
Architektursicht ist hier der Vorteil, dass mit den Standardmitteln von EJB eine
leistungsfähige Lösung erreicht wird. Dieser Ansatz empfiehlt sich für Projekte, die
kein komplexes Klassenmodell im Anwendungskern benötigen und keine weiteren
Produkte oder Komponenten integrieren möchten.
Die Implementierung mit Hilfe des QDI besticht durch die einfach umzusetzende Abbildung vom objektorientierten Klassenmodell auf das relationale Datenbankschema,
selbst wenn das Klassenmodell durch Vererbung und abhängige Objekte sehr komplex wird. Gleichzeitig stehen so mächtige Mechanismen wie lazy loading und ein
optimistisches Sperrkonzept zur Verfügung. Die Schnittstelle des QDI erreicht eine
Mächtigkeit und eine Performanz, ohne eine lange Einarbeitungszeit zu verlangen.
Liegt in einer Anwendungskomponente ein etwas komplexeres Klassenmodell vor, so
ist der Einsatz einer ausgereiften Zugriffsschicht auf jeden Fall anzuraten.
7.2.6 Praxiserfahrungen
Neben der qualitativen Beurteilung der Architektur- und Implementierungsvorschläge ist für ein Projekt auch die tatsächlich erreichte Performanz wichtig. Jedes Projekt hat unterschiedliche Anforderungen an die Antwortzeiten und den Durchsatz; sie
ergeben sich aus den speziellen fachlichen Aspekten. Die wichtigsten Fragestellung
bezüglich der Performanz sind u. a.:
• Wie hoch sind Clientlast und Datenaufkommen zu erwarten?
• Ist die Anwendung interaktiv, oder werden komplexe Vorgänge aus einem BatchSystem angestoßen? Sind also die Antwortzeiten von kürzeren, einfacheren Anfragen wichtig, oder kommt es auf den Gesamtdurchsatz bei komplexen Anfragen an?
• Sind die meisten Anfragen lesend und nur wenige schreibend, wie z. B. in einem Online-Shop, oder sind viele Anfragen schreibend, wie z. B. bei einem
Buchungssystem? Diese Frage beeinflusst die Wahrscheinlichkeit von Sperrkonflikten.
• Welche weiteren Besonderheiten weist die Anwendung auf?
80
7.2 Implementierung
• Was sind die wichtigsten Zugriffsmuster und Zugriffspfade?
• Kann sich das Profil der Anwendung ändern? Bei welchen Punkten sind am
ehesten Änderungen zu erwarten?
Da die Anforderungen von verschiedenen Projekten sehr unterschiedlich sein können,
wurde auch nicht versucht, eine genaue Performanz-Analyse durchzuführen. Statt
dessen soll hier lediglich gezeigt werden, dass die vorgestellten Ideen nicht nur bezüglich der Wartbarkeit sinnvoll erscheinen, sondern dass auch die Performanz einen
Einsatz in der Praxis erlaubt.
Welche Variante ein Projekt wählt, kann erst nach Beantwortung der oben angeführten Fragen entschieden werden. Gegebenenfalls sind erst spezielle Lasttests durchzuführen, die genau die kritischen Punkte durchtesten und aussagekräftige Vergleiche
ermöglichen.
Beispielszenario
In dem gewählten Beispielszenario soll die prinzipielle Tauglichkeit untersucht werden. Parallel mit der Entwicklung des Prototyps ist ein Testtreiber entstanden, der
einen Client simuliert und jeden Anwendungsfall in mehreren Konstellationen aufruft.
Er wurde so umgebaut, dass er mehrfach durchläuft und so nicht nur Einzelzugriffe,
sondern viele Zugriffe erzeugt. So wurde die Datenlast erhöht. Weiterhin wird er in
mehreren Threads gestartet, die gleichzeitig Anfragen abschicken, um eine gewisse
Clientlast zu simulieren. An dieser Stelle kann variiert werden, ob die unterschiedlichen Threads auf unterschiedliche Daten zugreifen oder auf die selben, um das
Verhalten in Konfliktsituationen zu beobachten.
Getestet wurde so, dass zehn Threads (fast) gleichzeitig gestartet wurden, die jeweils
einen Client simulieren. Jeder dieser Clients schickt eine Reihe von Anfragen an den
Anwendungskern ab, die jeweils relativ wenig komplexe Anwendungslogik erfordern,
aber intensiv auf der Datenbank arbeiten.
Ergebnisse
Diese Versuche sind bewusst nur sehr grob aufgebaut und stellen ein willkürliches
Szenario dar. Deshalb lassen sich daraus auch keine Aussagen ableiten, welche Lösung
die bessere“ sei. Man beachte, dass in diesem Prototyp noch keinerlei Optimierungen
”
betrieben wurden, wie z. B. Datenbankindizes gezielt auf die häufigsten Abfragen hin
zu optimieren, Datenbank-Denormalisierungen etc.
Die Verusche zeigen, dass selbst dieser Prototyp bei erhöhter Datenmenge und Clientzahl mit beiden Implementierungen noch zuverlässig funktioniert. Daraus lässt
sich zunächst der Schluss ziehen, dass die vorgestellte Architektur der Performanz
81
7 Fallstudie: Telekom-Billing-System
nicht im Wege steht. Der nächste Schritt ist, sich für eine Implementierung zu entscheiden und diese in Verbindung mit dem Datenbankschema so zu implementieren,
dass wirkliche Optimierungen erzielt werden.
Entwicklung eines Lasttests für ein konkretes Projekt
Ein Projekt, das den Einsatz eines EJB-Applikationsservers erwägt und eine Designentscheidung zu treffen hat, muss zunächst die Anforderungen an das System
untersuchen und die Engpässe identifizieren. Um die geeignete Implementierung auszuwählen, sind entweder die Argumente, die sich aus den Anforderungen ergeben,
schon kräftig genug, oder es muss ein gezielter Perfomanz-Test durchgeführt werden.
Hier kommt wiederum der Vorteil der hier vorgestellten Architektur zum Tragen,
dass die Anwendungslogik unabhängig vom Datenbank-Zugriffsmechanismus programmiert werden kann.
Unter Umständen ist es auch möglich, eine Komponente aus diesem Beispiel-Prototyp
auszuwählen und den Testtreiber entsprechend auszubauen, also die Zahl der simulierten Clients anzupassen, das Datenvolumen im gewünschten Umfang vorzugeben,
die Zahl der Anfragen pro Sekunde zu verändern, das Verhältnis zwischen lesenden
und schreibenden Anfragen einzustellen, die Zahl der provozierten optimistischen
Sperrkonflikte zu variieren usw.
Die Durchführung eines aussagekräftigen Lasttests ist nicht trivial: Das Verhalten
bei erhöhtem Datenaufkommen hängt nicht nur vom Zusammenspiel von Anwendung, Applikationsserver und Datenbank ab, sondern genauso von den CachingEigenschaften des Applikationsservers und der Datenbank. So können die Antwortzeiten über einen gewissen Bereich linear ansteigen und ab einem bestimmten Punkt
stark in die Höhe gehen. Das Gleiche gilt für die Erhöhung der Clientlast und das
Verhalten bei einer hohen Zahl an konkurrierenden Zugriffen.
Es sind ausführliche Vergleiche erforderlich, um alle gemessenen Effekte genau den
richtigen Ursachen zuschreiben zu können; so z. B. Testreihen, die die Zugriffe auf
die Datenbank direkt und über den Applikationsserver ausführen, Vergleiche von
Clientanfragen, die an die Datenbank weitergereicht werden mit Clientanfragen, die
von einem Dummy beantwortet werden, um genau herauszufinden, wieviel Laufzeit
im Applikationsserver und beim Datenbankzugriff benötigt wird.
Erst wenn die Messergebnisse genau ihren Ursachen zugeordnet werden können, sind
exakte Aussagen über die Leistungsfähigkeit einer Architektur und einer bestimmten Implementierung möglich. Ein aussagefähiger Lasttest ist nur projektspezifisch
durchführbar und würde den Rahmen dieser Arbeit sprengen. [Bri00, Kapitel 8] argumentiert, dass die Testumgebung erst möglichst nahe am echten Betrieb orientiert
werden muss, bevor die Messergebnisse zu Entscheidungen herangezogen werden können.
82
7.2 Implementierung
Weitere Entscheidungskriterien für Projekte
Selbstverständlich muss sich eine Designentscheidung neben den technischen Fragestellungen auch noch auf die verfügbaren Mitarbeiter und das vorhandene Know-How
stützen, und es muss die Variabilität der Anforderungen berücksichtigen.
83
8 Zusammenfassung
8.1 Erkenntnisse
Der Entwurf von betrieblichen Informationssystemen stellt hohe Anforderungen sowohl von technischer als auch von fachlicher Seite. Derartige Systeme müssen verteilt
arbeiten und mit hohen Datenvolumina und Clientlasten zurecht kommen. Gleichzeitig sind individuelle, fachlich unterschiedliche Lösungen zu implementieren, die zum
Teil komplexe Algorithmen verlangen. Sowohl die technischen Randbedingungen als
auch die fachlichen Anforderungen können sich ändern; ein betriebliches Informationssystem ist zwei unterschiedlichen Innovationszyklen ausgesetzt. Während sich
fachliche Anforderungen von Projekt zu Projekt und von Kunde zu Kunde deutlich
unterscheiden können, ähneln sich die immer wieder zu lösenden technischen Probleme, wie z. B. Datenbankzugriff, Transaktionskontrolle, Ressourcenmanagement usw.
Applikationsserver bieten eine Integrationsplattform für technische Dienste; sie entlasten den Anwendungsprogrammierer von den immer wiederkehrenden Problemen.
Sie stellen Programmierschnittstellen bereit, welche die benötigten technischen Dienste auf einer gewissen Abstraktionsebene verfügbar machen.
Die Spezifikation von Enterprise Java Beans (EJB) definiert eine solche Programmierschnittstelle für einen sogenannten Container, der den Enterprise Java Beans als
Laufzeitumgebung genau diese technischen Dienste zur Verfügung stellt. Ein Container läuft innerhalb eines EJB-Servers; Implementierungen für Container und Server
existieren von mehreren namhaften Firmen wie auch von der Open-Source-Gemeinde.
Die EJB-Spezifikation legt ein Komponentenmodell fest und verspricht, dass die Anwendungsprogrammierer sich damit rein auf die fachlichen Aspekte der Anwendung
konzentrieren können und die technischen Abhängigkeiten deklarativ lösen können.
Doch es zeigt sich, dass die EJB-Spezifikation und die verfügbaren Produkte sowohl
hinter dem Anspruch der Portierbarkeit als auch hinter dem der Trennung zwischen
fachlicher Logik und technischer Abhängigkeit zurückbleiben. Der Grund hierfür liegt
einerseits in der Spezifikation, die an vielen kritischen Punkten unklar bleibt und
den Produktherstellern Freiheiten lässt, bzw. ihnen die Arbeit auferlegt, vernünftige Lösungen zu finden. Andererseits setzen die Hersteller aufgrund unterschiedlicher
Erfahrungen mit Vorgängerprodukten unterschiedliche Schwerpunkte. Das Komponentenmodell von EJB ist weder zur Wiederverwendbarkeit von Enterprise Beans
84
8.2 Vorteile des Ansatzes
geeignet, noch leistet es einen wertvollen Beitrag zur Strukturierung von großen Softwaresystemen (siehe Kapitel 5.2).
Dies führt dazu, dass Anwendungen, welche sich zu sehr auf die speziellen Eigenschaften von EJB oder einem bestimmten EJB-Server oder -Container einlassen, nur
noch schwer wartbar sind: Enterprise Beans sollen fachliche Logik aufnehmen, ihr
Design aber wird dort allein von technischen Überlegungen bestimmt. Systeme, die
auf der Spezifikation 1.1 aufbauen, sind zwar gerade mal ein Jahr alt und dennoch
Altsysteme, denn mit der Spezifikation 2.0 ändern sich grundlegende Mechanismen.
EJB steht für die Lösung komplexer technischer Probleme und integriert Mechanismen zur Persistenz, zum Transaktionsschutz und zur verteilten Verarbeitung. Eine
solche Technologie ist naturgemäß komplex und kann nicht auf naive Weise eingesetzt
werden.
8.2 Vorteile des Ansatzes
Diese Diplomarbeit stellt zunächst dar, welche Anforderungen an ein betriebliches
Informationssystem gestellt werden und untersucht, welche Möglichkeiten EJB in diesem Zusammenhang bietet. Sie ordnet die Mächtigkeit dieser Möglichkeiten ein und
schlägt eine Architektur vor, welche einerseits die Dienste des Applikationsservers so
weit wie möglich in Anspruch nimmt, um nicht das Rad neu zu erfinden, andererseits
aber Wert darauf legt, sich nicht wiederum von der Technik des Applikationsservers
abhängig zu machen.
Diese Architektur orientiert sich streng an dem Prinzip, die Implementierung der
fachlichen Logik von der Implementierung der technischen Umsetzung zu trennen.
Sie nutzt die Dienste des EJB-Applikationsservers — allerdings nicht auf dem Weg,
den die Spezifikation und die meisten Lehrbücher vorschlagen, da dort die fachliche
Logik mit den Spezifika der EJB-Version und des verwendeten Applikationsservers
vermischt wird.
Die zentrale Idee ist die Entkoppelung der Anwendungslogik von den technisch motivierten Teilen über ein neutrales Interface. Das neutrale Interface formuliert ausschließlich fachliche Funktionalität; erst die Implementierung dieses Interfaces legt
sich auf eine konkrete technische Umsetzung fest. So bleiben die Auswirkungen einer
technischen Änderung lokal begrenzt, genauso wie sich eine Änderung der fachlichen Anforderungen nur auf die Programmteile mit rein fachlicher Logik beschränkt.
Dadurch wird nicht nur die Wartbarkeit des Systems dramatisch erhöht; auch die
Arbeitsteilung im Team wird einfacher und effektiver: Datenbankexperten können
genau abgestimmte Optimierungen durchführen, während sich ein anderer Teil des
Teams tatsächlich auf rein fachliche Probleme konzentrieren kann.
Zwei unabhängige Implementierungen des vorgestellten Beispiel-Prototyps und in
der Praxis bewährte Projekte zeigen, dass dieser Ansatz sinnvoll ist in dem Sinne,
85
8 Zusammenfassung
dass er den in der Einleitung genannten Zielen gerecht wird: Die fachliche Logik
kann unabhängig von der verwendeten Basistechnologie implementiert werden, und
Änderungen der fachlichen Anforderungen bleiben lokal begrenzt, fordern also keine
Überprüfung von Software-Teilen, die ausschließlich technisch motiviert sind.
8.3 Ausblick
Im direkten Anschluss an diese Arbeit
Diese Arbeit argumentiert für den Einsatz von Applikationsservern, warnt aber gleichzeitig davor, den Werbeversprechen zu glauben und von einer naiven Implementierung
gleichzeitig Wartbarkeit und Effizienz zu erwarten. Sie liefert einen Architekturvorschlag für reale Projekte; bevor aber eine spezielle Implementierung gewählt werden
kann, sind noch Untersuchungen im Hinblick auf die Generierbarkeit und ein genau
auf die Anforderungen eines Projektes hin abgestimmter Lasttest durchzuführen.
Solche Untersuchungen könnten im direkten Anschluss an diese Arbeit durchgeführt
werden, um neben dem Architekturvorschlag auch noch detailliertere Implementierungsvorschläge mit einer differenzierteren Bewertung vorzulegen. Solche Implementierungsvorschläge sollten auch den Einsatz von kommerziellen Werkzeugen mit einschließen.
Die vorgestellte Architektur reicht vom Datenbankzugriff bis zum Anwendungskern.
Eine vollständige Anwendung benötigt natürlich noch eine Benutzerschnittstelle. Interessant ist deshalb die Entwicklung einer Gesamtarchitektur, die einen Anwendungskern mit einer GUI verbindet. Eine solche Gesamtarchitektur wurde bei sd&m
Research entwickelt, aber noch nicht als solche veröffentlicht. Weitere wichtige Themen schließen z. B. Kriterien für den Entwurf von Anwendungs-Komponenten oder
die Transaktionsverwaltung mit ein. Auch zu diesen Themen laufen zur Zeit Forschungsprojekte bei sd&m Research.
Über den Tellerrand hinaus
Applikationsserver sind ein guter Weg, die technisch motivierten Dienste für Informationssysteme zu standardisieren und sie auf einem höheren Abstraktionsniveau anzubieten, um es den Anwendungsentwicklern ermöglichen, sich mehr auf die Anwendungslogik zu konzentrieren. Es liegt auf der Hand, dass sich die ApplikationsserverTechnologie weiterhin schnell entwickeln wird. Es wird neue EJB-Versionen geben,
die mit .Net, weiteren Corba-Versionen oder völlig neuen Technologien um die Gunst
der Entwickler und Kunden konkurrieren werden.
Persönliche Einschätzung des Autors, Stand Januar 2002: Probleme sind zur Zeit bei
der Integration verschiedener Werkzeuge zu sehen. Applikationsserver integrieren die
86
8.3 Ausblick
Bereiche Transaktionsschutz, Persistenz und Verteilung; aber in jedem Bereich gibt es
einzelne Werkzeuge, die besser sind als die in Applikationsservern verfügbaren Mittel.
Hier wird noch mehr Konsolidierung stattfinden, wie z. B. mit der Zusammenarbeit
von Bea WebLogic und TopLink schon geschehen. Das wird einerseits die Qualität
der verfügbaren Applikationsserver erhöhen, andererseits die Portabilität zwischen
ihnen erschweren. Es wird dann keine Applikationsserver-Experten geben, sondern
jeweils Produktexperten.
Die in Kapitel 6 vorgestellte Architektur zeigt, wie man die aktuell vorhandene Technik optimal ausnutzen kann, ohne sich zu sehr von ihr abhängig zu machen. So wird
erreicht, dass die Implementierung der fachlichen Logik in ihrer Lebensdauer nur von
den fachlichen Anforderungen abhängt. In dieser Arbeit wird das für EJB gezeigt;
ein solches Vorgehen wird auch für künftige Anwendungen ein sinnvoller Weg sein.
87
8 Zusammenfassung
88
Index
A/T-Trennung, 7
ACID-Eigenschaft, 16
Bean-Klasse, 26
Bimodal Data Access, 55
Clientlast
simulieren, 81
Datenlast
simulieren, 81
Deployment-Deskriptor, 26
EJB, 22
als T-Software, 47
Aufbau, 24
Deployment, 24
Entwurfsmuster, 55
Lebenszyklus, 29
Probleme, 42
Produkte, 23
Programmierung, 24
Spezifikation, 22
Version 2.0, 37
Entity-Beans
sind T-Software, 48
Fast Lane Reader, 55
Home-Interface, 26
impedence mismatch, 13
Java, 22
JDO, 37
Lebenszyklus, 29
Objektidentität, 14
OR-Mapping, 13
Paradigmenbruch, 13
Partitionierung
typisiert, horizontal, vertikal, 14
Primärschlüssel, 26
QDI, 51
Quasar, 6
Database Interface, 51
Softwarekategorien, 7
Remote-Interface, 25
RMI
Effizienzvergleich, 45
Softwarekategorien, 7
Sperrstrategien, 16
Spezifikation
Lücken, 32
TBS, 69
Teamarbeit, 60
TopLink, 53
Transaktionen, 16
Transaktionsstrategien, 16
Optimistisch, 17
Pessimistisch, 17
Transaktionsverhalten
deklarativ, 26
Versant
für CMP einsetzen, 35
virtuelle Maschine, 22
Zugriffskontrolle
deklarativ, 26
Zugriffsschicht
Zugriff auf A-Entitäten, 52
89
Literaturverzeichnis
[Alu01]
Deepak Alur, John Crupi, Dan Malks: J2EE Patterns, Prentice Hall,
2001
[Amb98]
Scott Ambler: Mapping Objects to Relational Databases, AmbySoft,
1998: http://www.AmbySoft.com/mappingObjects.pdf
[Am01]
Scott Ambler: Complex Persistence, Kapitel 14 des Buches Mastering EJB, ausgelegt zum public review auf dem Diskussionsforum
www.theserverside.com, Mai 2001
[Atk+89]
Malcolm P. Atkinson, François Bancilhon, David J. DeWitt, Klaus R.
Dittrich, David Maier, Stanley B. Zdonik: The Object-Oriented Database
System Manifesto, in Deductive and Object-Oriented Databases“, Pro”
ceedings of the First International Conference on Deductive and ObjectOriented Databases (DOOD’98), pp. 223-240
[Ben01]
Gerd Beneken, Johannes Siedersleben: Quasar: QDI Tutorial, Schriftenreihe sd&m Research, Februar 2001
[Bri00]
Chris Britton: IT Architectures and Middleware, Addison-Wesley, 2000
[BrSi00]
Peter Brössler, Johannes Siedersleben: Software-Technik, Hanser-Verlag,
2000
[BrWh96]
Kyle Brown, Bruce G. Whitenack: Crossing Chasms, A Pattern Language for Object-RDBMS Integration, White Paper, Knowledge Systems
Corp. 1995. A shortened version is contained in: John M. Vlissides, James O. Coplien, Norman L. Kerth (Eds.): Pattern Languages of Program
Design 2, Addison-Wesley, 1996
[DePe00]
Stefan Denninger, Ingo Peters: Enterprise JavaBeans, Addison-Wesley,
2000
[Dij76]
Edsger W. Dijkstra: A Discipline of Programming, Prentice Hall, 1976
[Gam94]
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Design
Patterns, Elements of Reusable Software, Addison-Wesley, 1994
90
Literaturverzeichnis
[GrRe92]
Jim Gray, Andreas Reuter: Principles of Transactionprocessing: Concepts and Techniques, Morgan-Kaufmann, 1992
[Ha01]
Tobias Hain: Anwendungskernanbindung nativer und webbasierter Benutzeroberflächen für betriebliche Informationssysteme, Diplomarbeit,
Universität Siegen, 2001
[He97]
Andreas Heuer: Objektorientierte Datenbanken: Konzepte, Modelle, Systeme, Addison-Wesley, 2. Auflage, 1997
[Ke97]
Wolfgang Keller: Mapping Objects to Tables: A Pattern Language,
in Proceedings of the 1997 European Pattern Languages of Pro”
gramming Conference, Irrsee, Germany“, Siemens Technical Report
120/SW1/FB1997
[Mon99]
Richard Monson-Haefel: Enterprise JavaBeans, O’Reilly, 1999
[Öb01]
Rickard Öberg: Mastering RMI, Wiley, 2001
[Par72]
David L. Parnas: On the Criteria to be used in Decomposing Systems
into Modules, Communications of the ACM, Vol. 15, No. 12; pp. 10531058, 1972
[PCW83]
D. L. Parnas, P. C. Clements, D. M. Weiss: Enhancing Reusability with
Information Hiding, Proceedings of ITT Workshop on Reusability, Stratford, CT, 1983
[Ro99]
Ed Roman: Mastering EJB, Wiley, 1999
[Sch01]
Manfred Schamper: Muster zum komponentenorientierten Entwurf betrieblicher Informationssysteme unter Verwendung von EJB, Diplomarbeit, Ludwig-Maximilian-Universität München, 2001
[Si01]
Johannes Siedersleben et al.: Was ist Quasar?, Schriftenreihe sd&m Research, März 2001
[Sla99]
Dirk Slama, Jason Garbis, Perry Russel: Enterprise Corba, Prentice Hall,
1999
[Sun99]
Vlada Matena, Mark Hapner: Enterprise Java Beans Specification, Version 1.1, Final Release, Sun Microsystems, 24.11.99
[Sun01a]
Linda G. DeMichiel, L. Ümit Yalçinalp, Sanjeev Krishnan: Enterprise
Java Beans Specification, Version 2.0, Final Release, Sun Microsystems,
14.08.01
[Sun01b]
Sun Microsystems: Java Blueprints: J2EE Patterns. Webseite: http://
java.sun.com/blueprints/patterns/j2ee patterns, November 2001
91
Literaturverzeichnis
[Szy99]
Clemens Szyperski: Component Software – Beyond Object-Oriented Programming, Addison-Wesley, 1999
[Top01]
TopLink von der Firma WebGain, Inc., USA. Webseite: http://
www.webgain.com, November 2001
[Ver01]
Versant enJin von der Firma Versant Corporation, USA. Webseite:
http://www.versant.com, November 2001
[Wrox01]
Professional EJB, Wrox, 2001
[ZiBe00]
Jürgen Zimmermann, Gerd Beneken: Verteilte Komponenten und Datenbankanbindung, Addison-Wesley, 2000
92
Herunterladen