TECHNISCHE UNIVERSITÄT CAROLO-WILHELMINA ZU BRAUNSCHWEIG Bachelorarbeit Funktionale Abhängigkeiten in PostgreSQL-Datenbanken Informationssystem Julian Timpner 4. September 2009 Institut für Informationssysteme Prof. Dr. Wolf-Tilo Balke betreut durch: PD Dr. habil. Karl Neumann Eidesstattliche Erklärung Hiermit erkläre ich an Eides statt, dass ich die vorliegende Arbeit selbstständig verfasst und keine anderen als die angegebenen Hilfsmittel verwendet habe. Braunschweig, 4. September 2009 Zusammenfassung Funktionale Abhängigkeiten sind ein wesentliches Konzept der relationalen Datenbanktheorie und der Normalisierung. Daher ist es für Studierende besonders wichtig, funktionale Abhängigkeiten trotz ihres hohen Abstraktionsgrades zu verstehen und ihren Nutzen für qualitativ hochwertige Datenbankentwürfe zu verinnerlichen. Zu diesem Zweck wird ein Informationssystem entwickelt, das funktionale Abhängigkeiten in einer Datenbank darstellen und manipulieren kann, um Studierenden ein interaktives Werkzeug für den Lernprozess zur Verfügung zu stellen. Darüber hinaus unterstützt das entwickelte System die Bewertung und automatische Normalisierung von Datenbankschemata durch geeignete Algorithmen. Diese Bachelorarbeit liefert die theoretischen Grundlagen der für das Verständnis nötigen Konzepte, sowie zu ausgewählten Aspekten der Implementierungssprache Java und JDBC. Relevante Problemstellungen für das Informationssystem werden aufgezeigt und Lösungsstrategien entwickelt, die in einem systematischen Entwurfsprozess umgesetzt und dokumentiert werden. Stichwörter Funktionale Abhängigkeiten, Normalisierung, Zerlegungsalgorithmus, Synthesealgorithmus, Informationssystem, Trigger, Integritätsbedingungen, PostgreSQL, Java, SQL, JDBC. Inhaltsverzeichnis Verzeichnis der Tabellen ix Verzeichnis der Abbildungen x Listings xi Verzeichnis der Abkürzungen xii 1. Einleitung 1 2. Allgemeine Grundlagen 2.1. Das relationale Modell 2.2. SQL . . . . . . . . . . 2.3. Java . . . . . . . . . . 2.4. JDBC . . . . . . . . . . . . . 3 3 4 5 6 3. Funktionale Abhängigkeiten 3.1. Grundlegende Definitionen . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Normalisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 14 4. Konzeptioneller Entwurf 4.1. Ziele . . . . . . . . . . . . . . . . . . . 4.2. Leistungsumfang . . . . . . . . . . . . 4.3. Lösungsansätze . . . . . . . . . . . . . 4.3.1. Darstellung von FAen (/F110/) 4.3.2. FA hinzufügen (/F200/) . . . . 4.3.3. Normalisierung (/F300/) . . . . . . . . . . 17 17 17 21 21 24 25 . . . . . . . 28 28 28 29 29 31 32 32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. Systementwurf 5.1. Projektdetails . . . . . . . . . . . . 5.2. Analyse der Produktfunktionen . . 5.3. Analyse von Funktionalität /F100/ 5.3.1. Grobanalyse . . . . . . . . . 5.3.2. Feinanalyse . . . . . . . . . 5.4. Komponentenspezifikation . . . . . 5.5. Technische Produktumgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii Inhaltsverzeichnis 6. Implementierung 6.1. Model-View-Controller . . . . . . . . . . . . . . . . 6.2. Klassendiagramme . . . . . . . . . . . . . . . . . . 6.2.1. Implementierung von Komponente Control 6.2.2. Implementierung von Komponente DBTools 6.2.3. Implementierung von Komponente FD . . . . 6.2.4. Implementierung von Komponente GUI . . . 6.3. User Interface Design . . . . . . . . . . . . . . . . . 6.4. Externe Bibliotheken . . . . . . . . . . . . . . . . . 6.4.1. Swing Application Framework . . . . . . . . 6.4.2. SwingLabs SwingX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 34 34 35 35 37 38 38 42 42 43 7. Zusammenfassung und Ausblick 7.1. Bewertung . . . . . . . . . . . 7.1.1. Szenario 1: Anzahl von 7.1.2. Szenario 2: Anzahl von 7.2. Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 44 44 45 47 . . . . . . . Relationen Attributen . . . . . . . . . . . . . . . . . . . . . . . . . . . Literatur 48 Sachwortregister 50 A. Detaillierter UML-Entwurf A.1. Analyse von /F110/ und /F120/ . A.2. Analyse von /F200/ . . . . . . . A.3. Analyse von /F210/ . . . . . . . A.4. Analyse von /F300/ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 52 53 55 59 B. Bedienungsanleitung 62 C. Schnittstellenspezifikation 66 viii Verzeichnis der Tabellen 7.1. Laufzeit in Abhängigkeit der Relationen . . . . . . . . . . . . . . . . 7.2. Laufzeit in Abhängigkeit der Attribute . . . . . . . . . . . . . . . . . 45 45 C.1. Schnittstellenspezifikation Normalizable . . . . . . . . . . . . . . . . C.2. Schnittstellenspezifikation IStatement . . . . . . . . . . . . . . . . . 66 67 ix Verzeichnis der Abbildungen x 2.1. Java Runtime Environment . . . . . . . . . . . . . . . . . . . . . . . 2.2. JDBC Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 7 4.1. Anwendungsfälle des Informationssystems . . . . . . . . . . . . . . . 4.2. fd_catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 24 5.1. 5.2. 5.3. 5.4. Statechart Projektdetails . . . Verteilung von /F100/ . . . . Sequenzdiagramm für /F100/ Komponentenentwurf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 30 31 32 6.1. 6.2. 6.3. 6.4. 6.5. Klassendiagramm Control Klassendiagramm DBTools Klassendiagramm FD . . . Klassendiagramm GUI . . GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 37 39 40 41 7.1. Heap Dump . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2. Objekt-Allokation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 46 A.1. A.2. A.3. A.4. A.5. A.6. A.7. A.8. Verteilung von /F110/ und /F120/ . . . . Sequenzdiagramm für /F110/ und /F120/ Verteilung von /F200/ . . . . . . . . . . . Sequenzdiagramm für /F200/ . . . . . . . Verteilung von /F210/ . . . . . . . . . . . Sequenzdiagramm für /F210/ . . . . . . . Verteilung von /F300/ . . . . . . . . . . . Sequenzdiagramm für /F300/ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 53 54 56 57 58 59 61 B.1. B.2. B.3. B.4. Systemstart . . . . . . . . . . . Verbindungsaufbau . . . . . . . Hinzufügen/Entfernen von FAen Normalisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 63 64 65 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listings 2.1. 2.2. 2.3. 2.4. Definition einer Relation in SQL SQL-Beispielanfrage . . . . . . Vorkompilierte SQL-Anfrage . . Transaktionen in SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 8 9 10 3.1. Closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Kanonische Überdeckung . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 4.1. SQL-Assertion zur Realisierung einer FA . . . . . . . . . . . . . . . . 4.2. Member . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3. SQL-Query zum dynamischen Kopieren . . . . . . . . . . . . . . . . . 22 25 25 7.1. Heap Space Exception . . . . . . . . . . . . . . . . . . . . . . . . . . 45 xi Verzeichnis der Abkürzungen API BSD DBMS FA FD GUI JDBC JRE JSP JSR MVC MVD NF SQL xii Application Programming Interface Berkeley Software Distribution Datenbankmanagementsystem Funktionale Abhängigkeit Functional Dependency Graphical User Interface Java Database Connectivity Java Runtime Environment JavaServer Pages Java Specification Request Model-View-Controller Multivalued Dependencies Normalform Structured Query Language 1 Einleitung Relationale Datenbanken wurden bereits 1970 von Edgar F. Codd in [Cod70] als ein Modell elektronischer Datenverwaltung vorgeschlagen. Im Gegensatz zu den damals vorherrschenden Datenmodellen, nämlich dem Netzwerkmodell und dem hierarchischen Modell, ist das relationale Modell sehr einfach strukturiert und vergleichsweise flexibel zu handhaben. Dies ist darauf zurückzuführen, dass nur einfache Tabellen (Relationen) verwendet werden, in denen die Zeilen die Datenobjekte definieren. Die gespeicherten Daten werden durch entsprechende Operatoren mengenorientiert verknüpft. Die Einfachheit dieses Ansatzes führte zu einer starken Verbreitung relationaler Datenbanken und heute sind diese ein etablierter Standard mit einer nahezu marktbeherrschenden Stellung. Daher verwundert es nicht, dass das relationale Modell in der Informatik ein integraler Bestandteil der Hochschulausbildung ist. In der relationalen Entwurfstheorie, die sich mit dem konzeptionellen Entwurf der Datenschemata beschäftigt, spielen dabei insbesondere die funktionalen Abhängigkeiten eine wichtige Rolle. Mit ihrer Hilfe lassen sich sogenannte Normalformen definieren, die die Grundlage für einen qualitativ hochwertigen Datenbankentwurf bilden. Die Konzepte der funktionalen Abhängigkeiten und der Normalisierung dürfen daher während der Ausbildung nicht vernachlässigt werden. Da sie jedoch relativ abstrakt sind, fällt ihre Vermittlung an Studierende oft nicht leicht. Besonders für Studierende am Anfang ihrer Ausbildung, die abstraktes Arbeiten nicht gewohnt sind, ist es daher wichtig, die Bedeutung der funktionalen Abhängigkeiten praktisch und zielorientiert vermittelt zu bekommen. Im Rahmen dieser Bachelorarbeit wird daher ein Werkzeug entwickelt, mit dem bestehende funktionale Abhängigkeiten in Datenbanken ausgelesen und angezeigt werden können. Auf dieser Basis bestimmt das Werkzeug den Normalisierungsgrad der Datenbank und ermöglicht es, durch Manipulation (d. h. Hinzufügen und Entfernen) der funktionalen Abhängigkeiten die Auswirkungen auf den Normalisierungsgrad zu verfolgen und so leichter nachzuvollziehen. Außerdem kann der Benutzer eine gewünschte (in der Regel höhere als die aktuelle) Normalform auswählen und diese durch geeignete Normalisierungsverfahren herstellen. Die Theorie und Implementierung dieser Verfahren sind Gegenstand von [Mei09]. Insgesamt bietet sich das Werkzeug somit neben dem Einsatz als computergestütztes Trainingssystem auch für die Beurteilung und Verbesserung von Datenbankschemata an. Im Folgenden wird dieses Werkzeug als Informationssystem bezeichnet. Die Art der Implementierung des entwickelten Informationssystems in Java bietet 1 1. Einleitung außerdem die Möglichkeit, Studierenden das Konzept der relationalen Anfragesprache SQL im Allgemeinen und ihren praktischen Einsatz über die Programmierschnittstelle JDBC1 im Speziellen zu vermitteln. Zu diesem Zweck lässt sich der Programmcode als Dokumentation heranziehen. Das Informationssystem arbeitet plattformunabhängig und ist im Wesentlichen in der Lage, mit einer Vielzahl von Datenbanksystemen verschiedener Hersteller zusammenzuarbeiten, sofern diese den SQL-99-Standard umsetzen. Da jedoch alle Datenbankhersteller jeweils spezifische Besonderheiten in ihren Systemen implementieren und nur teilweise zum SQL-Standard konform sind, kann nur für ein ausgewähltes Datenbanksystem, nämlich PostgreSQL, volle Unterstützung gewährleistet werden. PostgreSQL ist ein unter BSD-Lizenz stehendes, und damit freies, objektrelationales Datenbankmanagementsystem. Es wurde ursprünglich ab 1977 an der University of California, Berkeley von Michael Stonebraker als eines der ersten relationalen Datenbanksysteme (damals unter dem Namen Ingres) entwickelt. Heute ist PostgreSQL quelloffen und wird von einer aktiven Entwicklergemeinschaft fortwährend weiterentwickelt. PostgreSQL gilt als besonders standardkonform zu ANSI-SQL-92 und -99, und wurde nicht zuletzt deshalb ausgewählt. Der Aufbau dieser Arbeit stellt sich nach Kapiteln wie folgt dar: eine Einführung in die theoretischen Grundlagen des relationalen Modells, der Anfragesprache SQL sowie der verwendeten Implementierungssprache Java (inklusive der benutzen Datenbankschnittstelle JDBC) erfolgt zu Beginn der Arbeit in Kapitel 2. In Kapitel 3 wird die Theorie der funktionalen Abhängigkeiten sowie die darauf aufbauende Normalisierung ausführlich behandelt. In Kapitel 4 erfolgt der konzeptionelle Entwurf des Informationssystems, bestehend aus einer Beschreibung des Funktionsumfangs und Lösungsansätzen für die sich daraus ergebenden Fragestellungen. Die Analyse des Funktionsumfangs und die Ableitung der Komponenten des Informationssystem wird in Kapitel 5 vorgenommen. Kapitel 6 beschreibt daraufhin die resultierende Software-Architektur, verwendete Bibliotheken sowie den Entwurf der grafischen Benutzeroberfläche. In Kapitel 7 erfolgt ein abschließendes Resümee mit kritischer Beurteilung und ein Ausblick auf mögliche Weiterentwicklungen. 1 2 Java Database Connectivity 2 Allgemeine Grundlagen Dieses Kapitel führt in die Themen ein, die für das Verständnis der funktionalen Abhängigkeiten sowie der im Entwurf und der Implementierung eingesetzten Konzepte wichtig sind. Zuerst wird das relationale Modell vorgestellt, das die Grundlage für alle in dieser Arbeit behandelten Konzepte und Fragestellungen ist. Anschließend erfolgt eine Beschreibung der Anfragesprache SQL zum Auslesen und Manipulieren von Datensätzen. Schließlich wird gezeigt, wie die Datenbankschnittstelle JDBC der Java-Plattform eine einheitliche und herstellerunabhängige Realisierung von SQLAnfragen bietet. 2.1. Das relationale Modell Das Relationenmodell ist das heute am weitesten verbreitete Datenbankmodell, obwohl seine Ursprünge bereits auf eine Veröffentlichung von Codd aus dem Jahr 1970 [Cod70] zurückgehen. Diese Tatsache lässt sich auf seine Einfachheit und Exaktheit zurückführen, weshalb es sowohl in der Praxis etabliert, als auch seit langem Gegenstand weitreichender Forschungen ist. Aufgrund seines Bekanntheitsgrades soll hier nicht ausführlich darauf eingegangen werden, sondern nur die wesentlichen Aspekte und Definitionen für die weitere Arbeit vorgestellt werden. Somit kann eine einheitliche Schreibweise eingeführt und unterschiedlichen Interpretationen entgegengewirkt werden. Eine relationale Datenbank besteht aus einer Sammlung von Relationen mit eindeutigem Namen. Seien n Wertebereiche (auch Domänen genannt) D1 , D2 , . . . , Dn gegeben. Im Folgenden wird vorausgesetzt, dass diese Domänen nur atomare Werte enthalten. Praktische Beispiele hierfür sind Standarddatentypen wie integer, boolean oder string. Dann ist eine Relation R definiert als Teilmenge des kartesischen Produkts dieser n Domänen: R ⊆ D1 × D2 × · · · × Dn Ein Element der Menge R wird als Tupel bezeichnet. Ein Tupel besteht aus einer Menge von Attributwerten. Den Attributen werden die oben genannten Wertebereiche, die Domänen, zugeordnet. Für eine Relation R legt sein Relationenschema R die Anzahl und den Typ (d. h. die Domänen) der Attribute fest. R bezeichnet somit das Schema der Relation und R deren momentane Ausprägung (Instanz). Mit dom(A) wird die Domäne eines Attributs A bezeichnet. Eine Menge S = {R1 , R2 , . . . , Rm } , m ∈ ℕ heißt Datenbankschema. 3 2. Allgemeine Grundlagen Das Relationenschema R lässt sich um eine Menge B von Integritätsbedingungen erweitern, die festlegen, welche Tupel zulässig sind und welche nicht. Das Paar (R, B) wird als erweitertes Relationenschema bezeichnet. Ein Beispiel für Integritätsbedingungen sind Schlüssel, die als Spezialfall von funktionalen Abhängigkeiten in Kapitel 3 vorgestellt werden. 2.2. SQL SQL (Structured Query Language) ist die Datenbanksprache zur Definition, Manipulation und Abfrage in relationalen Datenbanksystemen. Sie ist von der internationalen Normungsorganisation ISO (International Standardization Organization) und der amerikanischen Normungsorganisation ANSI (American National Standards Institute) standardisiert und wird heute von allen großen kommerziellen und freien relationalen Datenbanksystemen unterstützt. SQL zeichnet sich durch eine einfache, der englischen Umgangssprache angelehnten Syntax aus und umfasst vier unterschiedliche Sprachteile: einen Daten- und Sichtdefinitionsteil, einen Anfrageteil, einen Datenänderungsteil und einen Teil, der für die Rechteverwaltung verantwortlich zeichnet. An dieser Stelle werden die genannten Sprachteile beispielhaft erläutert. Für weitergehende Informationen zum SQL-Sprachumfang siehe [SSH08, SKS05]. Data Definition Language Als sogenannte Datendefinitionssprache (engl. Data Definition Language, kurz DDL) stellt SQL eine Reihe von Anweisungen zur Verfügung, mit denen die Struktur von Daten definiert werden kann. Diese lassen sich nach der Drei-Ebenen-Schemaarchitektur folgendermaßen ordnen: • Auf der externen Ebene können mittels CREATE VIEW Sichten definiert werden. • Auf der konzeptionellen Ebene werden mit CREATE TABLE Relationen angelegt. Eine typische Anweisung, die die (fiktive) Relation Student mit den Attributen MatrNr und Name erstellt und im Katalog der Datenbank ablegt, ist in Listing 2.1 zu sehen. Dort werden hinter den Namen der Attribute deren Domänen, d. h. ihre Datentypen, sowie weitere Integritätsbedingungen (PRIMARY KEY, NOT NULL) angegeben. 1 2 3 4 CREATE TABLE Student ( MatrNr int NOT NULL PRIMARY KEY, Name varchar(50) NOT NULL ) Listing 2.1: Definition einer Relation in SQL 4 2.3. Java • Mit CREATE INDEX können auf der internen Ebene Zugriffspfade angelegt werden. Data Manipulation Language Die Data Manipulation Language, kurz DML, ist für die Manipulation der Daten (hinzufügen, ändern oder löschen von Datensätzen) zuständig. Das Einfügen eines Tupels in die zuvor erstellte Relation Student erfolgt beispielsweise mit: INSERT INTO Student(289000, ’Albert Einstein’); Data Query Language Die Data Query Language, kurz DQL, dient der Datenabfrage. So gibt der folgende Befehl den Namen des Studenten mit der Matrikelnummer 289000 aus: SELECT Name FROM Student WHERE MatrNr = 289000; Data Control Language Als letzter Teil dient die Data Control Language, kurz DCL, dazu, Zugriffsrechte an Benutzer zu vergeben, zurückzunehmen usw. REVOKE SELECT,UPDATE ON TABLE Student FROM einstein; Dieser Befehl entzieht dem Benutzer einstein das Recht, den Inhalt der Relation Student zu lesen oder zu verändern. 2.3. Java Die Implementierung dieser Arbeit ist in Java Version 6 geschrieben. Java ist eine objektorientierte Programmiersprache, die 1995 von Sun Microsystems als Kernkomponente der Java-Plattform veröffentlicht wurde. Obwohl sie seit Veröffentlichung kostenlos zu beziehen ist, steht sie seit Mai 2007 auch als freie Software unter der GNU General Public License zur Verfügung. In Java geschriebene Programme werden typischerweise in sogenannten Bytecode kompiliert. Dieser kann in einer speziellen Umgebung, der Java Runtime Environment (JRE) ausgeführt werden. Deren wichtigster Bestandteil ist die Java Virtual Machine (JVM), die die Programme ausführt, indem der Bytecode interpretiert wird. Die Java Runtime Environment schirmt Java-Programme dabei gegen die unterliegende Betriebssystem- und Hardware-Architektur ab, sodass sie ohne Anpassungen auf jedes beliebige System portierbar sind, sofern für dieses System eine JRE existiert. Neben der JVM beinhaltet die JRE auch eine umfangreiche Klassenbibliothek, womit Programmierern eine gut dokumentierte API (Programmierschnittstelle, engl. Application Programming Interface) angeboten wird. Abbildung 2.1 verdeutlicht die genannten Zusammenhänge. Ausführlichere Informationen zur Java-Programmierung sind in [SK08, Ull09], sowie unter http://java.sun.com zu finden. 5 2. Allgemeine Grundlagen Abbildung 2.1.: Java Runtime Environment 2.4. JDBC Ein Bestandteil der Standard-API von Java ist die Java Database ConnectivitySchnittstelle, kurz JDBC. Dieses ist eine einheitliche Schnittstelle zu relationalen Datenbanken verschiedener Hersteller und in ihrer Funktion vergleichbar mit ODBC unter Windows oder DBI unter Perl. Zu ihren Aufgaben gehört es, Datenbankverbindungen aufzubauen, zu verwalten und SQL-Anfragen an die Datenbank weiterzuleiten. Die zurückgelieferten Ergebnisse werden entgegengenommen und in eine für Java nutzbare Form umgewandelt. Dies ist insbesondere wichtig, da SQL und die meisten Programmiersprachen im Allgemeinen auf unterschiedlichen Verarbeitungsparadigmen beruhen: während SQL mengenorientiert arbeitet, fehlt den meisten Hochsprachen ein solches Konzept. Sie arbeiten hauptsächlich satz- oder objektorientiert. Dies führt zu einer umständlichen Anwendungsprogrammierung, im Englischen auch Impedance Mismatch genannt. JDBC versucht, diesen so gering wie möglich zu halten, indem es Programmierschnittstellen auf hohem Abstraktionsniveau bereitstellt. Um eine Datenbankverbindung via JDBC herzustellen, sind Datenbank-spezifische Treiber erforderlich, die meist vom Hersteller des jeweiligen Systems geliefert werden. Es existieren vier verschiedene Typen von Treibern, die in Abbildung1 2.2 dargestellt sind. Der in dieser Arbeit verwendete Treiber ist vom Typ 4, d. h. er ist komplett in Java geschrieben und bietet somit größtmögliche Plattformunabhängigkeit. Der Treiber wandelt die JDBC-API-Befehle direkt in Befehle des unterliegenden DBMS, in diesem Fall PostgreSQL, um und überträgt diese an den Datenbankserver, ohne beispielsweise auf eine Middleware-Schicht aufzubauen. JDBC besteht aus einer Vielzahl von Klassen und Schnittstellen, die im Package java.sql zusammengefasst sind. Die wichtigsten Klassen zur Anwendungsprogram1 6 Abbildung in Anlehnung an http://java.sun.com/products/jdbc/overview.html 2.4. JDBC Abbildung 2.2.: JDBC Architektur mierung sind dabei: • java.sql.DriverManager lädt die Treiber und stellt Verbindungen her. • java.sql.Connection repräsentiert eine Verbindung zum Datenbankserver. • java.sql.Statement ermöglich es, SQL-Anfragen über eine Verbindung auszuführen. • java.sql.ResultSet verwaltet die Ergebnis-Tupel einer Anfrage und stellt Zugriffsmethoden auf diese zur Verfügung. Im Folgenden werden die wesentlichen Aspekte der JDBC-Programmierung demonstriert (vgl. [SSH08, KE06]) und deutlich gemacht, welche Konstrukte in der Implementierung dieser Arbeit eine besondere Rolle spielen. Verbindungsaufbau Bevor ein Java-Programm mit der Datenbank kommunizieren kann, muss zunächst eine Verbindung aufgebaut werden. Im Java-Package java.sql ist hierfür der DriverManager enthalten, der die JDBC-Treiber verwaltet. Damit der DriverManager eine Verbindung aufbauen kann, muss der notwendige JDBC-Treiber (in diesem Fall für PostgreSQL) geladen werden. Dies erfolgt über den Ausdruck: Class.forName("org.postgresql.Driver"); 7 2. Allgemeine Grundlagen Danach kann die Verbindung (vom Typ Connection) hergestellt werden. Zu diesem Zweck stellt der DriverManager die Methode getConnection zur Verfügung, die als Parameter eine URL für die Adresse der Datenbank, einen Benutzernamen und ein Passwort erwartet. Der Aufruf dieser Methode liefert im Erfolgsfall ein ConnectionObjekt zurück: Connection con = DriverManager.getConnection(url, user, password); Anfrageausführung Mit dem erstellten Connection-Objekt kann nun eine SQLAnweisung erzeugt und abgesetzt werden. Dies soll anhand des folgenden kleinen Beispiels erläutert werden. 1 2 3 4 5 6 7 Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1"); while (rs.next()) { int x = rs.getInt("a"); String s = rs.getString("b"); float f = rs.getFloat("c"); } Listing 2.2: SQL-Beispielanfrage Zunächst muss man ein Statement-Objekt generieren, wofür Connection die Methode createStatement bereitstellt. Das so erzeugte Statement-Objekt stellt nun Methoden für Anfrage- und Änderungsoperationen zur Verfügung. Die Methode executeQuery erwartet als Argument einen SQL-Ausdruck und erzeugt ein Objekt vom Typ ResultSet. Das ResultSet stellt eine Iterator-Schnittstelle für den Zugriff auf die Menge der Ergebnistupel der übergebenen SQL-Anfrage dar, und bietet neben dem Zugriff mittels next() auf das nächste Element auch weitergehende Mechanismen an. Hierzu gehören u. a. erweiterte Möglichkeiten zur Navigation in der Ergebnismenge, z. B. durch frei positionierbare ResultSets. Dies mildert das beschriebene Problem des Impedance Mismatch etwas ab. In Listing 2.2 erfolgt zu Demonstrationszwecken der Funktionsweise nur eine einfache Iteration durch die Ergebnismenge in einer while-Schleife. Mit rs.getInt(“a“) greift man auf den Integer-Wert des Attributs a des derzeit aktuellen Ergebnistupels zu. Analog erfolgt der Zugriff auf die Attribute b und c, nur dass diese unterschiedliche Datentypen haben. Statt über den Namen des Attributs kann ein Zugriff auch über die Position des Attributs stattfinden, z. B. mit rs.getInteger(“1“). In diesem Fall erfolgt die Nummerierung in der Reihenfolge, in der die Attribute in der Select-Klausel der SQL-Anweisung angegeben sind. Nach der Verarbeitung der Ergebnismenge sollte noch sowohl das Statement, als auch die Verbindung mittels close() geschlossen werden, um den reservierten Speicher wieder freizugeben und die Performance zu verbessern. 8 2.4. JDBC Metadaten Das Informationssystem, das im Rahmen dieser Arbeit entwickelt wurde, hat die Besonderheit, dass es mit beliebigen und insbesondere vorher nicht bekannten Relationenschemata zusammenarbeiten muss. In diesen Fällen können die Attributnamen und -typen nicht fest kodiert werden, sondern müssen dynamisch aus dem Datenbankkatalog ausgelesen werden. Deshalb bietet das ResultSet auch Methoden, um auf die Metadaten zuzugreifen und so die Struktur der Ergebnistupel zu erfragen, die in einem ResultSetMetaData-Objekt zur Verfügung gestellt wird: ResultSetMetaData rsm = rs.getMetaData(); Über dieses Objekt lassen sich etwa die Anzahl der Attribute einer Relation als rsm.getColumCount() oder der Typ des i-ten Attributs als rsm.getColumnType(i) abfragen. Vorkompilierte SQL-Anweisungen Um die Effizienz der Bearbeitung von SQLAnweisungen zu erhöhen, ist es möglich, häufig auszuwertende Anfragen nur einmal zu übersetzen und wiederzuverwenden. Denn im Regelfall werden Anweisungen jedesmal von neuem übersetzt und optimiert, was zu Leistungseinbußen führen kann. Wenn Anweisungen mehrfach, jedoch mit unterschiedlichen Parametern aufgerufen werden, können PreparedStatements eingesetzt werden, um dieses Problem zu vermeiden. PreparedStatements enthalten statt Parameterwerten nur Platzhalter, die mit einem ? notiert werden. 1 2 3 PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?"); pstmt.setBigDecimal(1, 153833.00); pstmt.setInt(2, 110592); Listing 2.3: Vorkompilierte SQL-Anfrage Listing 2.3 zeigt diese Vorgehen.2 Das PreparedStatement wird nur einmal deklariert und über set-Methoden, die für jeden Datentyp existieren, können die einzufügenden Parameterwerte angegeben und ausgetauscht werden. Dabei entspricht der erste Parameter der set-Methode der relativen Position der ?-Zeichen im SQLAusdruck. Transaktionen Transaktionen sind ein wesentliches Konzept zur Sicherstellung der Integrität einer Datenbank. JDBC ist standardmäßig im sogenannten Auto-CommitModus, d. h. dass jedes einzelne SQL-Statement als Transaktion aufgefasst und committed wird, sobald es abgeschlossen ist. Häufig ist es jedoch notwendig, mehrere Statements in einer Transaktion zusammenzufassen und diese entweder vollständig oder überhaupt nicht auszuführen. Zu diesem Zweck lässt sich der Auto-CommitModus ausschalten, sodass der Commit explizit erfolgen muss. Nach Beendigung 2 vgl. http://java.sun.com/javase/6/docs/api/java/sql/PreparedStatement.html 9 2. Allgemeine Grundlagen der Transaktion sollte der Auto-Commit-Modus wieder eingeschaltet werden, um das Standardverhalten wiederherzustellen. Listing 2.4 zeigt dieses Vorgehen exemplarisch. 1 2 3 4 con.setAutoCommit(false); // Beliebige SQL-Statements con.commit(); con.setAutoCommit(true); Listing 2.4: Transaktionen in SQL 10 3 Funktionale Abhängigkeiten Funktionale Abhängigkeiten sind ein wesentliches Konzept der relationalen Entwurfstheorie und der Schwerpunkt dieser Arbeit. Dieses Kapitel enthält daher alle Definitionen und Algorithmen, die zum weiteren Verständnis notwendig sind, und stellt Normalformen als Gütekriterien eines relationalen Entwurfs vor. 3.1. Grundlegende Definitionen Sei R ein Relationenschema und α, β ⊆ R Mengen von Attributen. Eine funktionale Abhängigkeit (im Folgenden FA, oder FAen für den Plural) über R ist ein Ausdruck der Form: α→β Die FA stellt dabei eine Bedingung an die möglichen gültigen Ausprägungen (Instanzen) des Relationenschemas dar. Eine Instanz R erfüllt die FA α → β genau dann, wenn für alle Tupel t1 , t2 ∈ R gilt, dass ∀A ∈ α : t1 .A = t2 .A ⇒ ∀B ∈ β : t1 .B = t2 .B. In anderen Worten: wenn die Tupel t1 und t2 für alle Attribute in α die gleichen Werte besitzen, dann müssen sie auch in den Werten der Attribute in β übereinstimmen, um die FA α → β zu erfüllen. Im Folgenden bezeichnen griechische Buchstaben (α, β, . . . ) stets Mengen von Attributen, während lateinische Großbuchstaben (A, B, . . . ) für individuelle Attribute stehen. Ableitungsregeln und Hüllenbildung Sei F eine Menge von FAen über R, dann lässt sich zeigen, dass u. U. neben F auch bestimmte andere FAen gelten. Man sagt, F impliziert weitere FAen oder weitere FAen lassen sich aus F herleiten. Eine FA A → B mit A, B ∈ R wird von F genau dann impliziert, wenn jede Instanz R von R, die F erfüllt, auch A → B erfüllt. Man schreibt dann F |= A → B. Weiterhin ist die Hülle (engl. closure) F + von F definiert als Menge aller FAen α → β, die von F impliziert werden: F + = {F |= α → β} Um die Hülle F + einer Menge F zu bestimmen, können Herleitungs- oder Inferenzregeln angewendet werden. Die dabei mindestens benötigten Inferenzregeln sind die sogenannten Armstrong-Axiome. Seien α, β, γ ⊆ R: 11 3. Funktionale Abhängigkeiten • Reflexivität: β ⊆ α ⇒ α → β • Verstärkung: α → β ⇒ αγ → βγ • Transitivität: α → β ∧ β → γ ⇒ α → γ Armstrong konnte in [Arm74] beweisen, dass diese Axiome korrekt und vollständig sind. Korrektheit bedeutet, dass sie keine falschen FAen herleiten. Vollständig sind sie deshalb, weil sich mit ihrer Hilfe alle FAen, die F impliziert, herleiten lassen. Dennoch ist es in vielen Fällen hilfreich, einige weitere Regeln zur Verfügung zu haben. Deren Korrektheit lässt sich mittels der Armstrong-Axiome beweisen. • Vereinigungsregel: α → β ∧ α → γ ⇒ α → βγ • Dekompositionsregel: α → βγ ⇒ α → β ∧ α → γ • Pseudotransitivität: α → β ∧ γβ → δ ⇒ αγ → δ Kanonische Überdeckung Sei eine Menge F von FAen auf einem Relationenschema gegeben. Dann müssen bei jeder Aktualisierungsoperation auf dem Schema die Einhaltung aller FAen in F überprüft werden. Erfüllt die aktualisierte Relation nicht alle FAen, muss die Operation rückgängig gemacht werden. Unmittelbar einleuchtend ist, dass für große Mengen F der Aufwand der Überprüfung stark anwächst. Dieser lässt sich jedoch reduzieren, indem nicht alle FAen in F überprüft werden, sondern alle FAen in einer Menge G, die äquivalent zu F ist und weniger Elemente besitzt. Zwei Mengen F und G von FAen heißen äquivalent (Schreibweise F ≡ G), wenn ihre Hüllen gleich sind, d. h. wenn F + = G+ . Insbesondere ist in diesem Zusammenhang die kleinste noch äquivalente Menge FC interessant. FC wird kanonische Überdeckung von F genannt, falls folgende Eigenschaften erfüllt sind: • FC ≡ F • Keine FA in FC enthält überflüssige Attribute. • Jede linke Seite einer FA in FC ist einzigartig. Das bedeutet, dass es keine zwei FAen α1 → β1 und α2 → β2 in FC gibt, sodass α1 = α2 . Wann ein Attribut als überflüssig gilt, ist wie folgt definiert. F sei dabei wieder eine Menge von FAen und α → β eine FA in F . • Ein Attribut A ist überflüssig in α, falls A ∈ α und F impliziert (F −{α → β})∪ ({(α − A) → β}). • Ein Attribut A ist überflüssig in β, falls A ∈ β und die FAen (F − {α → β}) ∪ ({(α → (β − A)}) implizieren F . 12 3.1. Grundlegende Definitionen Die Überprüfung, ob ein Attribut überflüssig ist oder nicht, kann effizient mit dem Algorithmus Closure aus [SKS05] in Listing 3.1 geschehen. Dieser nimmt eine Menge F von FAen und eine Menge von Attributen α als Eingabe und berechnet daraus die vollständige Menge von Attributen α+ , die von α gemäß F funktional bestimmt werden. Diese transitive Hülle einer Attributmenge α ist formal definiert als: n o α+ = β : α → β ∈ F + Wie genau überflüssige Attribute mit Hilfe von Closure identifiziert werden, ist dem Algorithmus der kanonischen Überdeckung in Listing 3.2 zu entnehmen. Der Beweis der Korrektheit und eine Laufzeitanalyse von Closure sind in [SKS05] zu finden und werden hier aus Platzgründen nicht geführt. Es sei jedoch festgehalten, dass im schlechtesten Fall eine quadratische Laufzeit in der Größe von F erreicht wird. Closure(F,α) { result := α; while (Änderungen an result) do foreach FA β → γ in F do if β ⊆ result then result := result ∪ γ ; end for end while α+ = result; } Listing 3.1: Closure Die Funktionsweise von Closure ist wie folgt: zunächst wird eine Menge result mit der Menge α initialisiert. Dann wird in der inneren Schleife jede FA daraufhin überprüft, ob ihre linke Seite in result enthalten ist, d. h. ob die linksseiten Attribute der FA bereits impliziert werden. Wenn das der Fall ist, müssen ihre rechtsseitigen Attribute zu result hinzugefügt werden, da sie auch aus result abgeleitet werden können. Dieser Schritt wird solange wiederholt, bis sich keine Änderungen an result mehr ergeben. Nun wird result als transitive Hülle der durch F bestimmten Attribute zurückgegeben. Mit Hilfe des vorgestellten Algorithmus Closure lässt sich nun ein Verfahren zur Herleitung der kanonischen Überdeckung zu einer Menge F von FAen angeben (vgl. [KE06]): 13 3. Funktionale Abhängigkeiten 1. Linksreduktion für jede FA α → β ∈ F : Überprüfe für alle A ∈ α, ob A überflüssig ist, d. h. ob β ⊆ Closure(F, α − A) gilt. Falls dies der Fall ist, ersetze α → β durch (α - A) → β . 2. Rechtsreduktion für jede (verbliebene) FA α → β , d. h. überprüfe für alle B ∈ β , ob B ∈ Closure(F − (α → β) ∪ (α → (β − B)), α) gilt. Falls dies der Fall ist, ist B auf der rechten Seite überflüssig. Dann wird α → β ersetzt durch α → (β − B). 3. Entferne die FAen der Form α → ∅, die im 2. Schritt möglicherweise entstanden sind. 4. Fasse mittels der Vereinigung FAen der Form α → β1 , . . . , α → βn zusammen, sodass α → (β1 ∪ · · · ∪ βn ) verbleibt. Listing 3.2: Kanonische Überdeckung Schlüssel Mit Hilfe der eingeführten Begriffe lässt sich nun auch der in Kapitel 1 bereits erwähnte Begriff des Schlüssels definieren. Schlüssel sind Spezialfälle von FAen und spielen in der Implementierung des Informationssystems und in Datenbanken allgemein eine entscheidende Rolle. Ist eine Menge α ∈ R gegeben, heißt α Superschlüssel, falls gilt: α→R Das bedeutet, dass α alle anderen Attributwerte in R eindeutig bestimmt. Falls R sogar voll funktional abhängig von α ist, d. h., dass es keine überflüssigen Attribute in α gibt, dann heißt α Schlüsselkandidat. Unter den Schlüsselkandidaten wird ein (beliebiger) als Primärschlüssel ausgezeichnet. Attribute, die nicht Bestandteil eines Schlüsselkandidaten sind, nennt man Nichtschlüsselattribute oder auch nicht-prim. Schlüsselattribute sind entsprechend prim. 3.2. Normalisierung Mit Hilfe der funktionalen Abhängigkeiten lassen sich nun einige Datenbankschemaeigenschaften definieren, die im Allgemeinen beim relationalen Datenbankentwurf angestrebt werden. Dazu gehört, dass 14 3.2. Normalisierung • ausschließlich semantisch sinnvolle, konsistente Daten gespeichert werden und • die Daten nach Möglichkeit nicht-redundant gehalten werden. Redundante Datenhaltung ist unerwünscht, da sie schwerwiegende Nachteile mit sich bringt. Zum einen belegen redundant gespeicherte Informationen unnötig viel Speicherplatz, zum anderen führen Änderungsoperationen auf redundanten Daten zu Leistungseinbußen und Konsistenzproblemen. Leistungseinbußen entstehen dadurch, dass die Änderung an mehreren Einträgen vorgenommen werden muss, was gleichzeitig zu einem hohen Aufwand an Konsistenzsicherungsmaßnahmen führt. Schlechte Entwürfe können zu drei Arten von Anomalien führen: Updateanomalien Existieren Informationen mehrfach, kann es bei Änderungen (Updates) dazu kommen, dass einige Einträge übersehen werden. Einfügeanomalien Werden Informationen, die zu verschiedenen Aspekten der realen Welt (sog. Entitytypen) gehören, in einem Relationenschema vermischt, ergeben sich Probleme, sobald Informationen eingefügt werden, die nur zu einem der Entitytypen gehören. Die Attribute, die zu dem anderen Entitytyp gehören, müssen dann ggf. mit NULL-Werten aufgefüllt werden. Löschanomalien Beim Löschen eines von zwei miteinander vermischten Entitytypen können die Informationen über den anderen Entitytyp ebenfalls verloren gehen. Um den genannten Anomalien zu begegnen, müssen Mechanismen eingesetzt werden, die Relationen mit nicht zusammengehörenden Informationen aufspalten und redundante Daten vermeiden helfen. Diese Mechanismen fasst man unter dem Begriff Normalisierung zusammen. Dabei wird ein Relationenschema R in die Relationenschemata R1 , . . . , Rn aufgespalten, sodass Ri ⊆ R für 1 ≤ i ≤ n. Bei einer solchen Zerlegung müssen zwei wesentliche Kriterien eingehalten werden: 1. Verlustlosigkeit: Die in der Instanz R von R enthaltenen Informationen müssen aus den Instanzen R1 , . . . , Rn der neuen Schemata R1 , . . . , Rn rekonstruierbar sein. 2. Abhängigkeitserhaltung: Die für R geltenden FAen müssen auf die Schemata R1 , . . . , Rn übertragbar sein. Unter Einhaltung dieser Kriterien überführen geeignete Normalisierungsverfahren (Synthese- und Zerlegungsalgorithmus) das Relationenschema in eine Normalform (NF abgekürzt). Um einer bestimmten Normalform zu genügen, muss das Relationenschema die Kriterien dieser Normalform erfüllen. Man unterscheidet folgende Normalformen: 15 3. Funktionale Abhängigkeiten • Erste Normalform (1NF): Jedes Attribut der Relation muss einen atomaren Wertebereich besitzen. • Zweite Normalform (2NF): Die Relation ist in 1NF und jedes Nichtschlüsselattribut A ∈ R ist voll funktional abhängig von jedem Schlüsselkandidaten von R. • Dritte Normalform (3NF): Die Relation ist in 2NF und kein Nichtschlüsselattribut ist transitiv abhängig von einem Schlüsselkandidaten. • Boyce-Codd-Normalform (BCNF): Die Relation ist in 3NF und für jede FA α → β gilt: ◦ β ⊆ α, d. h. die FA ist trivial oder ◦ α ist Superschlüssel von R. Es existieren weitere Normalformen, etwa 4NF und 5NF, die in der Praxis jedoch keine große Bedeutung haben und daher in dieser Arbeit nicht weiter behandelt werden. Aus diesem Grund wird auch nicht auf mehrwertige Abhängigkeiten (engl. multivalued dependencies, abgekürzt MVD), die eine Verallgemeinerung funktionaler Abhängigkeiten darstellen, eingegangen. Unter bestimmten Bedingungen ist es sinnvoll, nicht den höchsten möglichen Grad an Normalisierung anzustreben. Insbesondere Performance-Überlegungen können zu einer bewussten Denormalisierung führen. Diese Erwägungen lassen sich jedoch nicht verallgemeinern und sind daher nicht Aufgabe des entwickelten Informationssystems. Vielmehr muss dessen Einsatz eine gezielte und wohlüberlegte Entscheidung des Datenbankdesigners sein. Die Normalisierung, insbesondere die Algorithmen zur Herstellung der Normalformen, ist Gegenstand der mit dieser Arbeit verbundenen Bachelorarbeit [Mei09] und wird dort detailliert erläutert. 16 4 Konzeptioneller Entwurf In diesem Kapitel wird erläutert, welche Ziele beim Entwurf des Informationssystems verfolgt wurden und was der daraus abgeleitete Leistungsumfang beinhaltet. Im Rahmen der Konzeption eines Lösungsansatzes werden außerdem die Designentscheidungen, die der Implementierung zugrunde liegen, dargelegt. 4.1. Ziele Wie bereits einleitend erwähnt, ist es die Aufgabe des entwickelten Informationssystems, funktionale Abhängigkeiten aus PostgreSQL-Datenbanken auszulesen und in einer übersichtlichen Form darzustellen. Neben der Präsentation bereits vorhandener FAen muss auch deren Manipulation möglich sein, d. h. bestehende FAen sollen gelöscht oder neue FAen hinzugefügt werden können. Aus den vorhandenen FAen soll der Grad der Normalisierung berechnet und angezeigt werden. Zudem soll der Benutzer eine Normalform, die sinnvollerweise höher ist, als die momentane, auswählen und automatisch herstellen lassen können. Das Use-Case-Diagramm in Abbildung 4.1 umreißt die beschriebenen Funktionen des Informationssystems. Da es keine Interaktion zwischen mehreren Benutzern gibt, existiert in dem Diagramm nur eine Art von Benutzer. Alle Operationen, die eine Veränderung der Datenbank nach sich ziehen können, also die Normalisierung, als auch die Manipulation der FAen, beinhalten ausdrücklich eine Vorschaufunktion, um die Konsequenzen der gewählten Operation(-en) abschätzen zu können. 4.2. Leistungsumfang Aus Abbildung 4.1 lassen sich die Produktfunktionen des Informationssystems ableiten. Jede Funktion erhält einen eindeutigen Bezeichner der Form /F100/, um sie später referenzieren zu können. Die Nummerierung der Funktionen erfolgt im Allgemeinen in Hunderterschritten. In Fällen, in denen die Funktionen starken Bezug zueinander haben, wird die Nummerierung in Zehnerschritten erhöht und die Hunderterstelle festgehalten. Für jede Funktion werden neben ihren Vor- und Nachbedingungen auch die zu lösenden Probleme spezifiziert. 17 4. Konzeptioneller Entwurf Abbildung 4.1.: Anwendungsfälle des Informationssystems /F100/ Geschäftsprozess: Darstellen eines Datenbankschemas. Ziel: Eine Verbindung zu einer Datenbank wird hergestellt und deren Relationen werden angezeigt. Vorbedingung: Verbindung zur Datenbank konnte hergestellt werden. Nachbedingung Erfolg: Alle Relationen der Datenbank werden dargestellt. Nachbedingung Fehlschlag: Es konnte keine Datenbankverbindung hergestellt werden und eine Fehlermeldung wird ausgegeben. Auslösendes Ereignis: Auswählen des Menüpunktes „Verbindung herstellen“. Probleme Keine. /F110/ Geschäftsprozess: Darstellung von FAen. Ziel: Zu einer ausgewählten Relation werden die FAen bestimmt und angezeigt. 18 4.2. Leistungsumfang Vorbedingung: /F100/ erfolgreich. Nachbedingung Erfolg: Alle FAen der Relation werden angezeigt. Nachbedingung Fehlschlag: FAen konnten nicht bestimmt werden und eine Fehlermeldung wird ausgegeben. Auslösendes Ereignis: Auswahl einer Relation. Probleme 1. Woraus ergeben sich FAen bzw. wie sind diese implementiert? 2. Welche FAen werden angezeigt? Mögliche Alternativen sind alle implementierten FAen, eine kanonische Überdeckung oder die transitive Hülle. 3. Gewährleistung einer übersichtlichen Darstellungsform, auch für eine große Anzahl von FAen. /F120/ Geschäftsprozess: Darstellung des Normalisierungsgrades. Ziel: Zu einer ausgewählten Relation wird ihre Normalform bestimmt und angezeigt. Vorbedingung: /F110/ erfolgreich. Nachbedingung Erfolg: Der Normalisierungsgrad der Relation wird angezeigt. Nachbedingung Fehlschlag: Aufgrund von nicht erfüllten Vorbedingungen kann die Normalform nicht bestimmt werden. Auslösendes Ereignis: Auswahl einer Relation. Probleme Keine. /F200/ Geschäftsprozess: FAen hinzufügen. Ziel: Zu einer ausgewählten Relation wird eine neue FA spezifiziert und implementiert. Vorbedingung: /F100/ ist erfolgreich und die FA ist noch nicht vorhanden bzw. nicht herleitbar und die Instanz der Relation verstößt nicht gegen die FA. Nachbedingung Erfolg: Die spezifizierte FA wird implementiert. 19 4. Konzeptioneller Entwurf Nachbedingung Fehlschlag: Aufgrund von nicht erfüllten Vorbedingungen kann die FA nicht hinzugefügt werden. Auslösendes Ereignis: Spezifikation einer FA durch den Benutzer. Probleme 1. FA darf nicht implementiert werden, wenn die Ausprägung der Relation gegen die FA verstößt. 2. FAen, die von bestehenden FAen impliziert werden, sollen nicht doppelt gespeichert werden. /F210/ Geschäftsprozess: FAen entfernen. Ziel: Von einer ausgewählten Relation wird eine bestehende FA entfernt. Vorbedingung: /F100/ ist erfolgreich und die FA ist nicht durch einen Schlüssel implementiert. Nachbedingung Erfolg: Die spezifizierte FA wird entfernt. Nachbedingung Fehlschlag: Aufgrund von nicht erfüllten Vorbedingungen kann die FA nicht entfernt werden. Auslösendes Ereignis: Auswahl einer bestehenden FA. Probleme 1. Sicherstellung, dass keine FAen gelöscht werden können, die durch einen Schlüsselconstraint implementiert sind. /F300/ Geschäftsprozess: Normalisierung. Ziel: Eine ausgewählte Relation wird normalisiert. Mögliche Alternativen sind 2NF und 3NF. Vorbedingung: /F110/ ist erfolgreich. Nachbedingung Erfolg: Die Relation wird in die gewählte Normalform überführt. Nachbedingung Fehlschlag: Probleme beim Durchführen der Algorithmen oder bei der Änderung des Datenbankschemas führen zu einer Fehlermeldung. 20 4.3. Lösungsansätze Auslösendes Ereignis: Auswahl einer herzustellenden Normalform. Probleme 1. Normalisierung u. U. nicht möglich, wenn Inhalte in der Relation vorhanden sind. 2. Wenn im Zuge der Normalisierung Relationen aufgespalten werden, wie ergeben sich die Namen der neuen Relationen? 3. Behandlung von Fremdschlüsselbeziehungen auf zu normalisierenden Relationen. 4. Behandlung von Check- und Not-Null-Constraints. 5. Behandlung externer Trigger und Views. 4.3. Lösungsansätze In diesem Abschnitt werden Strategien vorgestellt, um die in Abschnitt 4.2 beschriebenen Probleme bzw. Aufgaben zu lösen. Sofern es mehrere sinnvolle Herangehensweisen zur Lösung eines Problems gibt, erfolgt eine begründete Auswahl der verfolgten Strategie. 4.3.1. Darstellung von FAen (/F110/) Bestimmung von FAen Als wesentliches Problem ergibt sich aus /F110/ die Frage, wie sich in einer beliebigen relationalen Datenbank die FAen bestimmen lassen. Dieses Problem ist nicht trivial und stellt einen Schwerpunkt der in dieser Arbeit entwickelten Überlegungen dar. Eine Möglichkeit besteht darin, Data-Mining-Techniken anzuwenden, um aus den in einer Relation enthaltenen Daten FAen abzuleiten. In [MR92a] ist dieses sogenannte dependency inference problem definiert als: Sei eine Relation R gegeben, so finde eine Menge von FAen, die genau die FAen implizieren, die in R gelten. Ein möglicher Algorithmus dafür wäre, durch alle möglichen FAen zu iterieren und jede einzelne daraufhin zu prüfen, ob sie in R gilt oder nicht. Jedoch ist diese Vorgehensweise sehr ineffizient, da der Aufwand der Überprüfung exponentiell mit den Attributen steigt. In [MR92b] wurde gezeigt, dass dieses Problem grundsätzlich nur exponentiell, also nicht effizient, lösbar ist. Zwar existieren einige Verbesserungen für diesen Algorithmus, doch viele davon arbeiten direkt auf den Daten und nutzen nicht die Schnittstellen der DBMS, worunter die Portabilität stark leidet. Weiterhin existieren auch einige SQL-basierte Algorithmen [BB95], die das inference problem plattformunabhängig und relativ einfach lösen, jedoch weisen alle diese Verfahren einige schwerwiegende Nachteile auf. Zum einen ist der Berechnungsaufwand nach wie vor exponentiell, zum anderen lassen sich Data-Mining-Techniken offensichtlich 21 4. Konzeptioneller Entwurf nur einsetzen, wenn (ausreichend) Daten in der Relation gespeichert sind. Im Rahmen dieser Arbeit kann davon jedoch nicht ausgegangen werden, da das entwickelte Informationssystem hauptsächlich in der Lehre eingesetzt werden soll. Dort ist es wahrscheinlicher, dass nur die Relationenschemata angelegt werden und auf das Einfügen zahlreicher Datensätze eher verzichtet wird, da diese für die Veranschaulichung relevanter Konzepte unerheblich sind. Als einen weiteren Kritikpunkt lässt sich aufführen, dass FAen semantische Integritätsbedingungen darstellen und dass die genannten Verfahren dies grundsätzlich unberücksichtigt lassen. Somit erscheinen Data-Mining-Ansätze als für diese Arbeit ungeeignet und werden nicht weiter verfolgt. Es muss stattdessen eine Möglichkeit gefunden werden, FAen ausschließlich aus dem Relationenschema abzuleiten. Dabei sollte auch die Semantik des vom Datenbankdesigners entworfenen Schemas berücksichtigt werden. Die einzigen Informationen, die im Normalfall zu diesem Zweck zur Verfügung stehen, sind der im erweiterten Relationenschema implementierte Primärschlüssel und die Schlüsselkandidaten. Diese stellen als Spezialfälle die einzigen auch im allgemeinen Fall verfügbaren FAen dar. Weitere FAen lassen sich ohne semantisches Wissen nicht ableiten. Wenn weitere FAen existieren, müssen diese daher vom Benutzer des Informationssystems manuell spezifiziert werden. Das Informationssystem übernimmt dann die Implementierung der FAen und deren Speicherung, um diese Informationen auch zu einem späteren Zeitpunkt respektive einem anderen Benutzer zur Verfügung stellen zu können. Wie diese Speicherung umgesetzt wird, ist Gegenstand des nächsten Abschnitts. Der Prozess der Bestimmung der FAen stellt sich somit wie folgt dar: stellt der Benutzer eine erstmalige Verbindung zu einer Datenbank her, existieren neben den Schlüsseln der ausgewählten Relation(-en) keine weiteren Informationen. Daher werden als FAen nur die Schlüssel angezeigt. Nun kann der Benutzer weitere FAen spezifieren, deren Umsetzung und Speicherung dann vom Informationssystem übernommen werden. Bei einer späteren Verbindung zu ebendieser Datenbank stehen dann als FAen sowohl die Schlüssel als auch die manuell spezifizierten Abhängigkeiten zur Verfügung. Realisierung von FAen Es stellt sich nun die Frage, wie allgemeine FAen, d. h. solche, die nicht zwingend einen Schlüssel darstellen, umgesetzt werden. Die Definition der FAen macht deutlich, dass diese als spezielle statische Integritätsbedingungen angesehen werden können, da sie in jedem Zustand der Datenbank gültig sein müssen. Wie in Kapitel 3 beschrieben, bedeutet z. B. die FA A → B nichts anderes als ∀x, y ∈ R : x.A = y.A ⇒ x.B = y.B. Dies lässt sich nach einigen simplen Logiktransformationen elegant als SQL-Assertion ausdrücken: 1 2 3 22 CREATE ASSERTION FD(A,B) CHECK NOT EXISTS ( SELECT * 4.3. Lösungsansätze 4 5 6 7 8 9 10 11 FROM TABLE1 AS X WHERE EXISTS ( SELECT * FROM TABLE1 AS Y WHERE ( X.A = Y.A AND NOT X.B = Y.B ) ) Listing 4.1: SQL-Assertion zur Realisierung einer FA Doch obwohl Assertions bereits im SQL-92-Standard vorhanden sind, werden diese bislang von PostgreSQL (wie auch von anderen, kommerziellen DBMS) nicht unterstützt. Eine umformulierte (und weniger elegante) Konstruktion von CheckConstraints mit eingebetteten Anfragen ist aus demselben Grund nicht möglich, weshalb eine andere Lösung gefunden werden muss. Eine relativ simple Möglichkeit hierfür bieten Trigger. Diese sind Bestandteil des SQL-Standards und werden von PostgreSQL unterstützt, wenn auch nicht völlig standardkonform. Ein Trigger spezifiziert, dass die Datenbank nach bestimmten Operationen eine spezielle Prozedur, die sogenannte Triggerfunktion, aufrufen soll. Trigger können sowohl bevor, als auch nach einer INSERT-, UPDATE- oder DELETE-Operation ausgelöst werden. Die Triggerfunktion kann dann ähnlich wie in Listing 4.1 überprüfen, ob die negierte Bedingung der FA erfüllt ist. Wenn dies der Fall ist, muss die Aktualisierung zurück gesetzt und eine Fehlermeldung ausgegeben werden. Dadurch wird sichergestellt, dass die FA in der Relation immer gültig ist. Wie im Detail aus einer FA ein Trigger und eine zugehörige Triggerfunktion generiert werden, ist Gegenstand von [Mei09]. Speicherung von FAen Es lässt sich somit festhalten, dass es (im Rahmen dieser Arbeit) günstig ist, FAen aus den Schlüsseln einer Relation abzuleiten und zusätzliche FAen als Trigger zu implementieren. Somit stellt sich aber die Frage, wie die zugehörigen Informationen persistent gemacht werden können. Um eine FA α → β zu speichern, werden folgende Daten benötigt: • die Relation, auf die sich die FA bezieht, • die bestimmende Attributmenge α, • die Attributmenge β, • Informationen darüber, ob die FA sich aus einem Schlüssel ergibt oder sie als Trigger implementiert ist, • ggf. der Triggername und die zugehörige Triggerfunktion. 23 4. Konzeptioneller Entwurf Um eine größtmögliche Portabilität des Informationssystems zu erreichen, bietet es sich an, diese Informationen direkt in der betreffenden Datenbank zu hinterlegen. Gegenüber einer Lösung, die diese Informationen etwa auf dem Dateisystem des Arbeitsplatzrechners speichert, können somit zum einen die Funktionen des DBMS, etwa Transaktionen und Sicherungsmechanismen, genutzt werden, und zum anderen können mehrere Benutzer unabhängig voneinander auf den gleichen Datenbestand zugreifen, was für Lehrzwecke vorteilhaft ist. Daher wird eine Menge von Relationen entworfen, die im Folgenden als fd_catalog bezeichnet wird und für die Speicherung aller relevanten Daten für FAen (engl. functional dependencies, kurz fd) zuständig ist. Der zugehörige UML-Entwurf ist in Abbildung 4.2 dargestellt. Die abgebildeten Klassen entsprechen den oben genannten Basisinformationen, wobei LHS und RHS für left-hand side bzw. right-hand side stehen. Abbildung 4.2.: fd_catalog Darstellung Die Fragen nach einer übersichtlichen Darstellungsform der FAen werden im Rahmen der Konzeption einer grafischen Benutzeroberfläche in Abschnitt 6.3 behandelt. Festzuhalten ist jedoch, dass genau die FAen dargestellt werden, die tatsächlich in der Datenbank implementiert sind, da die transitive Hülle zu umfangreich (und daher unübersichtlich) und eine kanonische Überdeckung verwirrend ist, da der Benutzer andere FAen angezeigt bekäme, als die die er hinzugefügt hat. 4.3.2. FA hinzufügen (/F200/) Unerfüllbarkeit von FAen Gemäß /F200/ stellt sich die Frage, was geschieht, wenn versucht wird, eine FA zu implementieren, die von der Instanz R nicht erfüllbar ist, da z. B. Tupel existieren, die gegen die FA verstoßen. Die Antwort darauf lässt sich nicht allgemeingültig angeben, weshalb in diesem Fall eine Rückmeldung an den Benutzer am sinnvollsten ist. Dieser kann sich dann entweder entscheiden, die FA zu verwerfen oder die Relation manuell zu leeren, um so die Realisierung der FA zu ermöglichen. 24 4.3. Lösungsansätze Membership-Problem Ein komplizierteres Problem, das sich aus /F200/ ergibt, ist es, sicherzustellen, dass FAen nicht doppelt gespeichert werden. Wird eine neue FA α → β spezifiziert, so muss zunächst überprüft werden, ob sie sich aus der bereits vorhandenen Menge F ableiten lässt, d. h. ob sie von F impliziert wird. Dies ist das sogenannte Membership-Problem α → β ∈ F +? Eine intuitive Vorgehensweise wäre es, die Hülle F + mit Hilfe der Armstrong-Axiome zu berechnen und dann auf Mengeninklusion zu überprüfen. Da die Größe der Hülle jedoch exponentiell wachsen kann, ist diese Vorgehensweise nicht effizient. Ein schnelleres Verfahren besteht darin, auf die Berechnung von F + zu verzichten und das Membership-Problem durch Berechnung der transitiven Hülle der Attributmenge α bezüglich F zu lösen. Das umformulierte Problem lautet dann β ⊆ α+ und kann effizient gelöst werden. Der simple Algorithmus Member in Listing 4.2 führt unter Verwendung der Funktion Closure(F,α) (Listing 3.1) den Test auf Membership durch und prüft also, ob β ⊆ α+ gilt. Ist dies der Fall, wird α → β durch die FAen in F impliziert und braucht nicht mehr gespeichert zu werden. Member(F,α → β ) return β ∈ Closure(F,α); Listing 4.2: Member 4.3.3. Normalisierung (/F300/) Tupel in der Relation Wird eine Relation normalisiert und dabei in mehrere Relationen zerlegt, stellt sich die Frage, wie mit potentiellen Tupeln in der zu zerlegenden Relation verfahren wird. Die einfachste Realisierung wäre es, eine Normalisierung zu verbieten, wenn tatsächlich Daten in der Relation enthalten sind. Dies wäre für den Benutzer jedoch sehr unkomfortabel und würde den Nutzwert des Informationssystems erheblich mindern. Die hier eingesetzte Lösung besteht daher darin, die Daten der zu zerlegenden Relation entsprechend der Aufteilung ihrer Attribute auf die neu entstehenden Relationen in ebendiese zu kopieren. Dazu wird dynamisch SQL-Code generiert, der pro entstehender Relation einen Query wie in Listing 4.3 erzeugt. Dieser selektiert alle Inhalte aus der alten Relation und projeziert auf die Attribute, die in der neuen Relation ebenfalls vorhanden sind. INSERT INTO NEW_TABLE SELECT ATTRIBUTE1,ATTRIBUTE2 FROM OLD_TABLE; Listing 4.3: SQL-Query zum dynamischen Kopieren 25 4. Konzeptioneller Entwurf Da die Namen der neuen Relationen nicht semantisch sinnvoll durch das Informationssystem bestimmt werden können, ergeben sich diese zunächst aus dem alten Relationennamen mit einem Suffix der Form „_i“, wobei i ∈ ℕ die neuen Relationen aufsteigend durchnummeriert. Durch die Vorschaufunktion bietet sich dem Benutzer dann die Möglichkeit, die Relationen beliebig umzubenennen, bevor die Änderungen an der Datenbank durchgeführt werden. Fremdschlüssel Eine Besonderheit, die bei der Normalisierung beachtet werden muss, sind eventuell bestehende Fremdschlüsselbeziehungen. Diese werden wie im Folgenden beschrieben behandelt: • Existieren Relationen mit Fremdschlüsseln, die die zu normalisierende Relation referenzieren, so müssen diese Fremdschlüssel zum einen gelöscht werden, um das Entfernen der Relation zu ermöglichen. Zum anderen sollten die zusätzlichen Integritätsbedingungen, welche die Fremdschlüssel darstellen, nicht verloren gehen, da es sonst zu Inkonsistenzen kommen kann. In der Konsequenz sollten solche Fremdschlüssel also nicht gelöscht, sondern durch andere ersetzt werden, die nicht mehr die zu normalisierende Relation referenzieren, sondern eine der neu entstehenden. Welche das ist, ergibt sich daraus, in welcher (neuen) Relation die Attribute, die der Fremdschlüssel referenziert, Primärschlüssel sind. • Besitzt die zu normalisierende Relation selbst einen (oder mehrere) Fremdschlüssel, so müssen alle neuen Relationen mit den entsprechenden Attributen ebenfalls diesen Fremdschlüssel implementieren. Check- und Not-Null-Constraints Wenn auf der zu normalisierenden Relation Check- oder Not-Null-Constraints existieren, muss entschieden werden, wie mit diesen zu verfahren ist. Während Not-Null-Constraints ohne weiteres auf jede Relation mit den entsprechenden Attributen übernommen werden können, muss bei CheckConstraints überprüft werden, ob und in welcher der neuen Relationen der Constraint implementierbar ist. Nur wenn weiterhin eine Relation mit allen Attributen des Constraints existiert ist dieser implementierbar und wird auf die entsprechende Relation übertragen. Andernfalls wäre eine aufwändige Generierung tabellenübergreifender Constraints notwendig, worauf im Rahmen dieser Arbeit verzichtet wird. Views Sollten Views existieren, die die zu normalisierende Relation referenzieren, müssen diese im Rahmen der Normalisierung entweder entfernt oder modifiziert werden, sodass die Referenz aufgehoben und die Relation entfernt werden kann. Eine Modifikation der Views ist jedoch nur in sehr einfachen Fällen, etwa wenn diese nicht relationenübergreifend definiert ist, mit vertretbarem Aufwand möglich. Selbst dann können jedoch die Attribute, die von der View referenziert werden, auf mehrere 26 4.3. Lösungsansätze neue Relationen aufgeteilt werden, sodass aufwändig eine neue Definition der View bestimmt werden müsste. Da Views für den Schwerpunkt dieser Arbeit, die Manipulation und Visualisierung funktionaler Abhängigkeiten, nicht wesentlich sind, wird daher auf eine Behandlung verzichtet. Stattdessen wird beim Versuch, eine Relation zu normalisieren, die von einer oder mehrerer Views referenziert wird, eine Fehlermeldung mit dem Hinweis auf die Namen der Views ausgegeben, sodass der Benutzer die Problematik extern behandeln kann. Externe Trigger Externe Trigger, d. h. solche, die nicht durch das Informationssystem erstellt wurden, gehen bei einer Normalisierung der zugehörigen Relation verloren. Dies ist darauf zurückzuführen, dass für eine Anpassung dieser Trigger die aufgerufenen Triggerfunktionen analysiert werden müssten, um zu untersuchen, ob die dort spezifizierten Bedingungen weiterhin erfüllbar sind. Da z. B. die Attribute auf mehrere Relationen aufteilt werden könnten, ist dies im Allgemeinen nicht ohne weiteres entscheidbar, weshalb auf eine Behandlung verzichtet wird. Stattdessen wird dem Benutzer ggf. eine Fehlermeldung mit dem Hinweis auf die Namen der externen Trigger ausgegeben. Information Schema Zu verschiedenen Zwecken, etwa der beschriebenen Behandlung der Fremdschlüsselproblematik, ist es nötig, auf den PostgreSQL-Systemkatalog zuzugreifen, um Metadaten über die gespeicherten Relationen, Attribute, Prozeduren und Constraints abzurufen. Der Systemkatalog ist PostgreSQL-spezifisch und mit Rücksicht auf Implementierungsaspekte angelegt. Er kann sich daher mit jedem Versionssprung der Datenbank grundlegend ändern, was aus Kompatibilitätsgründen für das entwickelte Informationssystem nicht akzeptabel ist. Abhilfe schafft das sogenannte Information Schema, eine Menge von im SQLStandard beschriebenen Views mit allen nötigen Metadaten über die Objekte in der Datenbank. Das Information Schema bleibt stabil und ist portabel, da es keine Informationen über PostgreSQL-spezifische Konstrukte enthält. Detaillierte Informationen über die verschiedenen Views sind [Pos09] zu entnehmen. 27 5 Systementwurf In diesem Kapitel wird die Systemarchitektur festgelegt. Dazu werden die Produktfunktionen in Subsysteme aufgebrochen und das Verhalten des Gesamtsystems auf Interaktionen zwischen diesen Subsystemen abgebildet. 5.1. Projektdetails Das Verhalten des entwickelten Systems wird durch den Statechart in Abbildung 5.1 spezifiziert und im Folgenden detailliert beschrieben. Nachdem das Informationssystem gestartet wurde, ist das Programm bereit und wartet auf Aktionen des Anwenders. Dieser kann nun eine Datenbankverbindung herstellen und das Datenbankschema laden. Ist dies erfolgt, werden alle Relationen der Datenbank angezeigt. Von diesen kann der Benutzer eine auswählen, zu der dann alle zugehörigen FAen sowie die momentane Normalform angezeigt werden. Nun besteht die Möglichkeit, bestehende FAen zu entfernen oder neue hinzuzufügen. In beiden Fällen wird zunächst eine Vorschau auf die Konsequenzen der Manipulation angezeigt. Insbesondere wird nach jeder Änderungsoperation der neue Normalisierungsgrad bestimmt. Dieser Zustand wird so lange beibehalten, bis sich der Benutzer dazu entscheidet, die Änderungen festzuschreiben oder zu verwerfen (Transitionen Commit bzw. Rollback in Abbildung 5.1). Außerdem hat der Benutzer die Möglichkeit, eine Normalform auszuwählen, die hergestellt werden soll. In diesem Fall wird ebenfalls eine Vorschau generiert, die entweder festgeschrieben oder verworfen werden kann. Das Programm lässt sich jederzeit beenden, sofern keine Vorschau angezeigt wird. 5.2. Analyse der Produktfunktionen Die in Abschnitt 4.2 beschriebenen Produktfunktionen werden für dieses Projekt wie folgt analysiert: 1. Die Verteilung in der Architektur und somit die resultierende Komponentenstruktur wird mittels eines Aktivitätsdiagrammes dargestellt. Die in den Sequenzdiagrammen aufgeführte Komponente Control wird hierbei unberücksichtigt bleiben, da sie keine Aktivität im eigentlichen Sinne darstellt, sondern als Vermittler zwischen den anderen Akteuren auftritt. 28 5.3. Analyse von Funktionalität /F100/ Abbildung 5.1.: Statechart Projektdetails 2. Die Realisierung der Funktion als Interaktion von Objekten des zu entwickelnden Systems wird durch ein Sequenzdiagramm dargestellt. Hierbei wird lediglich ein Weg durch den Entscheidungsbaum der Funktion dargestellt und auf die in den Aktivitätsdiagrammen beschriebenen Fallunterscheidungen verzichtet. Andernfalls würde die Anzahl der gegenseitigen Aufrufe überproportional ansteigen und der zur Verfügung stehenden Platz würde nicht für eine übersichtliche Darstellung ausreichen. Während die Funktion /F100/ exemplarisch im nächsten Abschnitt untersucht wird, ist die Analyse aller weiteren Funktionen aus Platzgründen in Anhang A zu finden. 5.3. Analyse von Funktionalität /F100/ 5.3.1. Grobanalyse Der Benutzer kann ein bereits gespeichertes Datenbankprofil auswählen oder ein neues anlegen, zu dem dann versucht wird, eine Verbindung herzustellen. Im Erfolgsfall liest die Komponente FD das Datenbankschema aus, woraufhin die Komponente 29 5. Systementwurf DBTools überprüft, ob der fd_catalog bereits existiert. Ist dies nicht der Fall, so werden die zugehörigen Relationen angelegt und die Schlüssel aller Relationen des Schemas als FAen in den fd_catalog aufgenommen. Schließlich werden aus diesem die FAen pro Relation ausgelesen und im von FD erstellten Schema-Objekt gespeichert. Nachdem die Verbindung zur Datenbank geschlossen wurde, wird das Schema von der Komponente GUI angezeigt. Abbildung 5.2.: Verteilung von /F100/ 30 5.3. Analyse von Funktionalität /F100/ 5.3.2. Feinanalyse Geht über die View der Aufruf zum Laden eines Datenbankschemas ein, stellt der ConnectionManager eine Verbindung zur Datenbank her. Der SchemaManager erstellt nun ein DbSchema-Objekt, in dem alle Relationen mit ihren Attributen gespeichert sind. Dieses Objekt übergibt er an den CatalogManager, der prüft, ob der fd_catalog existiert. Da der entsprechende Funktionsaufruf false liefert, wird der fd_catalog angelegt und die Schlüssel der Relationen werden als FAen dort eingetragen. Die gespeicherten FAen werden als Menge von FDep-Objekten im übergebenen DbSchema hinterlegt, woraufhin dieses entlang der Aufrufkette zurückgegeben und angezeigt wird. Abbildung 5.3.: Sequenzdiagramm für /F100/ 31 5. Systementwurf 5.4. Komponentenspezifikation Aus der Analyse der Produktfunktionen in Abschnitt 5.3 und Anhang A ergibt sich die Komponentenstruktur in Abbildung 5.4, die die grobe Kommunikation zwischen den einzelnen Komponenten des Produkts beschreibt. Abbildung 5.4.: Komponentenentwurf 5.5. Technische Produktumgebung Dieser Abschnitt beschreibt die technischen Anforderungen für die Umgebung, in der das entwickelte Informationssystem zum Einsatz kommt, bzw. für die es entwickelt wurde und zu der es daher kompatibel ist. Software Das Informationssystem ist vorrangig Linux-kompatibel (z. B. Ubuntu, Debian), arbeitet jedoch plattformübergreifend auch unter Windows-Betriebssystemen der Fa. Microsoft. Mindestvoraussetzung für den Einsatz ist eine Java Runtime Environment (JRE) Version 6. Entwickelt wird das System für PostgreSQL-Datenbanken ab Version 8.3.7-1. Hardware Das Produkt sollte auf einem Rechner ausgeführt werden, der den Mindestanforderungen der JRE entspricht. Laut Herstellerangabe gemäß [Sun09] liegen diese für 32 5.5. Technische Produktumgebung einen Desktop-Rechner mit Linux-Betriebssystem (beispielsweise Red Hat Linux 9.0) und 32-Bit-Architektur bei: • Arbeitsspeicher: min. 64 MB (unter Windows Vista 128 MB) • Festplattenspeicher: min. 58 MB (unter Windows Vista 98 MB) 33 6 Implementierung Gegenstand dieses Kapitels ist die Implementierung des vorgestellten Systementwurfs, sowie eine Beschreibung der grafischen Benutzeroberfläche und der eingesetzten Bibliotheken. 6.1. Model-View-Controller Die Implementierung des Informationssystems ist nach dem Model-View-ControllerArchitekturmuster (MVC, „Modell/Präsentation/Steuerung“) strukturiert. Ziel des Musters ist es, durch größtmögliche Entkopplung von GUI und Geschäftslogik einen flexiblen Programmentwurf zu realisieren, sodass eine spätere Änderung oder Erweiterung erleichtert und eine Wiederverwendbarkeit der einzelnen Komponenten ermöglicht wird. Insbesondere ein Austausch des GUI beispielsweise gegen eine JSPbasierte Web-Oberfläche ist so ohne eine aufwändige Umgestaltung der internen Programmstruktur möglich. MVC unterscheidet drei Komponenten: • Model trägt die Verantwortung für die Verwaltung der Daten. Im Informationssystem übernimmt das Paket FD, genauer gesagt, die darin enthaltenen Klassen, diese Rolle. • View übernimmt die sichtbare Darstellung. Die Views befinden sich im Paket GUI. • Controller sorgt für die interne Regelung aller ablaufenden Prozesse und ist somit die zentrale Schnittstelle zwischen Model und View. Der Controller (im gleichnamigen Paket) übersetzt die Benutzerinteraktionen mit der View in Operationen, die das Model ausführt. Die Aktualisierung der Darstellung bzw. der Daten erfolgt gemäß MVC, indem sich eine oder mehrere Views beim Model als Beobachter registrieren. Sobald Änderungen an den Daten vorliegen, benachrichtigt das Model alle registrierten Beobachter, woraufhin die Views ihre Darstellung aktualisieren. 6.2. Klassendiagramme Dieser Abschnitt beschreibt die Implementierung der in Kapitel 5 entworfenen Komponenten. Jede Komponente entspricht einem Java-Package, dessen Klassen hier in 34 6.2. Klassendiagramme vereinfachter Form vorgestellt werden. Dabei wird aus Gründen der Übersichtlichkeit auf Methoden und Attribute, die für die Belange des Informationssystems irrelevant sind, verzichtet. Dazu gehören insbesondere Getter- und Setter-Methoden, sowie Methoden, die Java-Klassen von Object oder anderen allgemeinen Oberklassen erben. 6.2.1. Implementierung von Komponente Control Control (s. Abbildung 6.1) besteht im Wesentlichen aus der abstrakten Oberklasse AbstractController und deren Implementierung DefaultController. Diese reali- sieren die Controller-Komponente des MVC-Architekturmusters, wie in Abschnitt 6.1 beschrieben. AbstractController beinhaltet zwei ArrayList-Objekte, um die registrierten Models und Views zu verwalten. Wenn ein Model registriert wird, registriert sich der Controller selbst ebenfalls als ein PropertyChangeListener auf dem Model. Somit ruft jede Zustandsänderung des Models die propertyChange()-Methode auf, woraufhin der Controller dieses Event an die entsprechenden Views weiterleitet. Die setModelProperty()-Methode bedient sich der Java Reflection API, um eine vollständige Entkopplung der Models vom Controller zu realisieren. Dazu wird, wenn eine Zustandsänderung des Models erfolgen soll, jede registrierte Model-Klasse daraufhin untersucht, ob sie die zu ändernde Eigenschaft besitzt und ggf. wird der entsprechende Setter dynamisch aufgerufen. Die DefaultController-Klasse besteht aus Property-Konstanten und Methoden, die dazu dienen, Aufrufe der GUI an die Models weiterzuleiten. FDISApp enthält die main()-Methode und ist für den Programmstart verantwortlich. 6.2.2. Implementierung von Komponente DBTools DBTools (s. Abbildung 6.2) ist verantwortlich für alle Aufgaben, die in einem en- geren Zusammenhang mit der Verwaltung der Datenbank stehen. Dies umfasst zum einen den Verbindungsaufbau und die Verwaltung der Zugangsdaten. Letztere werden in Credentials-Objekten erfasst und serialisiert. Der ConnectionManager ist dafür verantwortlich, die gewählten Credentials zu laden, zu bearbeiten oder auf die Festplatte zu schreiben. Er stellt anderen Klassen die Methode getConnection() zur Verfügung, die ein Connection-Objekt zurückliefert, auf dem Datenbankoperationen ausgeführt werden können. Da für das Informationssystem kein Mehrbenutzerbetrieb vorgesehen ist, ist getConnection() nach dem Singleton-Muster implementiert, sodass immer höchstens eine offene Verbindung existiert. Mittels closeConnection() wird die momentane Verbindung wieder geschlossen. Zum anderen übernimmt der CatalogManager alle Lese- und Schreiboperationen auf dem fd_catalog, d. h. dass er beim Laden eines Schemas für die Erstellung bzw. das Auslesen des fd_catalog, sowie für das Hinzufügen und Entfernen von FAen verantwortlich ist. Bei der Erstellung der Trigger greift er auf den StatementGenerator 35 6. Implementierung Abbildung 6.1.: Klassendiagramm Control 36 6.2. Klassendiagramme zurück, der das Interface IStatement implementiert. IStatement definiert Methoden zur SQL-Codegenerierung für Trigger, Triggerfunktionen sowie zum Erstellen von Relationen und Fremdschlüsselbeziehungen. Abbildung 6.2.: Klassendiagramm DBTools 6.2.3. Implementierung von Komponente FD FD (s. Abbildung 6.3) realisiert die Model-Komponente des MVC-Architekturmusters, sowie die das Model bearbeitenden Klassen. Alle Model-Klassen erben von der abstrakten Oberklasse AbstractModel, welche die Klasse PropertyChangeSupport aus dem Paket javax.beans benutzt, um an Zustandsänderungen des Models interessierte Beobachter zu registrieren, deregistrieren und zu benachrichtigen. Die DbSchema-Klasse repräsentiert ein Datenbankschema und enthält eine Menge von Relation-Objekten, die die Relationen des Schemas darstellen. Die RelationKlasse besitzt neben einem Namen und einer Menge von Attributen (columns) auch eine Menge von in ihr gültigen FAen (fdeps). Ferner implementiert Relation das Normalizable-Interface, welches die notwendigen Methoden zur Bestimmung der Normalform einer Relation und zur Durchführung einer Normalisierung nach 2NF 37 6. Implementierung oder 3NF (normalize(form)) definiert. Während der Normalisierung entsteht eine Menge von SynRelation-Objekten, die von Relation abgeleitet sind und diese um weitere Attribute, etwa eine Ursprungsrelation, erweitern. FAen sind ihrerseits durch die Klasse FDep vertreten, die jeweils eine Menge von links- und von rechtsseitigen Attributen aufweist. Die SchemaManager-Klasse übernimmt die Verwaltung des Models, d. h. sie ist für das Auslesen des Datenbankschemas und das Laden von Relationen verantwortlich. Sie bietet Funktionalität für das Hinzufügen und Entfernen von FAen und delegiert Normalisierungsoperationen an die entsprechenden Relationen. Ferner übernimmt sie den Ableich von Relation- bzw. FDep-Objekten mit deren tatsächlichen Entitäten in der Datenbank. 6.2.4. Implementierung von Komponente GUI GUI (s. Abbildung 6.4) übernimmt die grafische Darstellung des Models und bie- tet dem Benutzer Operationen auf dem Model an. An zentraler Stelle steht dabei FDISView, die die Haupt-Oberfläche (s. Abschnitt 6.3) realisiert, indem sie einen JFrame implementiert. Um sich als View beim Controller registrieren zu können, implementiert FDISView das Interface IView, welches die modelPropertyChange()Methode zur Verfügung stellt. Diese wird automatisch vom Controller aufgerufen, sobald das Model eine Zustandsänderung meldet. Für die Darstellung, insbesondere der FAen, bedient sich FDISView einer Reihe von manuell erweiterten Swing-Komponenten, der FDTable, dem FDTableModel und dem SchemaTreeCellRenderer. Diese sind reine Hilfsklassen und werden daher nicht näher erläutert. Eine weitere Klasse, die IView implementiert, ist NfDialog, welche eine Vorschaufunktion auf eine Normalisierung umsetzt. NfDialogConsole erweitert diese Vorschau um eine Textkonsole, die auf Wunsch die Zwischenschritte bei der Normalisierung anzeigt. ServerDialog realisiert eine Darstellung der gespeicherten Datenbankprofile und bietet Eingabefelder für die Erstellung neuer Profile. AboutBox dient der Darstellung von Hintergrundinformationen über das Programm und die Entwickler. Alle vier genannten Klassen sind von JDialog abgeleitet und werden bei Bedarf von FDISView erzeugt und dargestellt. 6.3. User Interface Design Abbildung 6.5 zeigt den Entwurf der grafischen Benutzeroberfläche. Die Oberfläche ist nach den Windows User Experience Interaction Guidelines gestaltet, so dass sie auf die meisten Benutzer vertraut wirken und den Umgang mit der Software erleichtern sollte. 38 6.3. User Interface Design Abbildung 6.3.: Klassendiagramm FD 39 6. Implementierung Abbildung 6.4.: Klassendiagramm GUI 40 6.3. User Interface Design l 8 l 4l 5l 3 l l 1 2 l 6 l 7 Abbildung 6.5.: GUI l Auf der linken Seite befindet sich der Relationenbaum 1 , der dazu dient, alle in der geladenen Datenbank vorhandenen Relationen übersichtlich darzustellen. Eine Relation kann aufgeklappt werden, so dass ihre Attribute sichtbar sind. Wird eine Relation selektiert, dann werden im Zentrum der Oberfläche die zugehörigen funktionalen Abhängigkeiten in Tabellenform 2 angezeigt. Die Spalte LHS (left-hand side) steht dabei für die linke und die Spalte RHS (right-hand side) für die rechte Seite einer FA. Im oberen rechten Teil der Anwendung befindet sich ein Panel mit Informationen zum Normalisierungsgrad der ausgewählten Relation, sowohl im aktuellen Zustand 3 (wie er in der Datenbank vorhanden ist), als auch nach einem potentiellen Commit 4 , welcher temporäre Änderungen an den FAen festschreiben würde. Direkt darunter bieten zwei Listen 5 die Möglichkeit, aus den Attributen der Relation neue FAen zu spezifizieren und temporär hinzuzufügen. Dies wird durch die grüne Hintergrundfarbe der neuen FA im mittleren Teil der Oberfläche hervorgehoben. Wird dort eine Zeile selektiert, kann durch den Button Remove 6 die entsprechende FA (zunächst wieder temporär) entfernt werden, welches durch eine rote Hintergrundfarbe signalisiert wird. Im unteren rechten Teil der Oberfläche wird diese Farbcodierung durch l l l l l 41 6. Implementierung l l eine Legende 7 erläutert. Wenn Änderungen an den FAen durchgeführt wurden, werden in der Toolbar 8 die Buttons Commit und Rollback aktiv, die die Änderungen entweder in die Datenbank festschreiben oder aber verwerfen. Alle Operationen können auch über den Menüpunkt Normalization im Hauptmenü aufgerufen werden. Für fortgeschrittene Benutzer wurde jeder Operation zudem ein Tastaturkürzel zugewiesen, um die Arbeit zu beschleunigen. 6.4. Externe Bibliotheken Im Rahmen der Entwicklung des Informationssystems wurden einige externe Bibliotheken eingesetzt, die in diesem Abschnitt kurz vorgestellt werden. 6.4.1. Swing Application Framework Name appframework-1.0.3.jar Version 1.0.3 URL http://appframework.dev.java.net Das im JSR-296 (Java Specification Request)1 spezifizierte Swing Application Framework definiert eine gemeinsame, wiederverwendbare Infrastruktur für Swing-Anwendungen, um den Entwicklungsprozess von Desktop-Anwendungen zu vereinheitlichen und zu vereinfachen. Das Framework stellt dabei insbesondere folgende Funktionalität zur Verfügung: • Lebenszyklusmanagement, besonders für den Start und das Beenden von GUIs. • Ressourcenmanagement, z. B. für Icons, Farben, Fonts und formatierte Nachrichten. • Vereinfachung des Event-Handlings durch Definition von Actions und Tasks, die auch asynchron zum Hauptthread laufen können. • Anwendungszustand sichern: Positionen von Fenstern, Tabellenspaltenbreiten, usw. von einer Programmsitzung in die nächste übernehmen. Das JSR-296 wird von Sun Microsystems, Oracle und Google unterstützt und soll voraussichtlich in Java Version 7 integriert werden. In dieser Arbeit wurde intensiver Gebrauch von dem Framework gemacht, was eine deutliche Verringerung der Komplexität, besonders im Bereich des Event-Handling, 1 Eine Anforderung einer neuen Java-Spezifikation im Rahmen des Java Community Process. 42 6.4. Externe Bibliotheken zur Folge hatte. So konnten zahlreiche Klassen, die sonst für GUI-Listener nötig gewesen wären, eingespart werden, wodurch die Pflege und Weiterentwicklung des Programmcodes vereinfacht wird. 6.4.2. SwingLabs SwingX Name swingx-1.0.jar Version 1.0 URL http://swingx.dev.java.net SwingX ist ein SwingLabs2 -Projekt, das diverse Erweiterungen des Standard-GUIToolkits beinhaltet. Dazu gehören u. a.: • Sortier-, Filter- und Highlighting-Funktionen für Tables, Trees und Listen • Auto-Vervollständigung • Finden/Suchen • Neue Komponenten, wie TreeTable Erfolgreiche Projektkomponenten, z. B. Desktop, SystemTray und GroupLayout, werden schließlich in die Swing-Bibliothek übernommen. Eine zentrale Komponente der entwickelten Oberfläche, nämlich die Tabelle zur Darstellung der FAen, ist eine SwingX-Klasse. Sie bietet gegenüber der herkömmlichen JTable-Klasse komfortablere Highlighting-Mechanismen und erweiterte Funktionen, wie das Ausblenden von Spalten. 2 Ein Open-Source-Projekt von Sun Microsystems, das sich mit der Verbesserung von Swing beschäftigt. 43 7 Zusammenfassung und Ausblick In diesem Kapitel erfolgt zunächst eine kritische Beurteilung des entwickelten Informationssystems, insbesondere unter Performance-Gesichtspunkten. Schließlich erfolgt ein zusammenfassendes Fazit und ein Ausblick. 7.1. Bewertung Für den praktischen Einsatz des Informationssystems ist die Performance der Software auch unter Last interessant. Daher wurden die Laufzeiten von zwei Szenarien, unter denen große zu verarbeitende Datenmengen auftreten können, genauer untersucht. Die für eine hohe Last entscheidenden Kriterien sind: 1. Anzahl der Relationen in der Datenbank 2. Anzahl der Attribute in einer Relation Die folgenden Evaluationen fanden auf einem Desktop-PC mit folgenden Merkmalen statt: • CPU: Intel Core2Duo E6400 @ 2.13 GHz • RAM: 3,0 GB 7.1.1. Szenario 1: Anzahl von Relationen Die Anzahl der Relationen einer Datenbank wirkt sich auf die Laufzeit des Ladeprozesses aus, da für jede vorhandene Relation entsprechende Einträge im fd_catalog angelegt werden müssen. Somit tritt diese Ladeverzögerung nur beim erstmaligen Verbinden zu einer Datenbank auf. Bei jeder weiteren Verbindung ist der fd_catalog bereits angelegt und muss nur noch ausgelesen werden. Für das untersuchte Maximum von 1000 Relationen liegt die Zeit für das Auslesen des fd_catalog noch unter 8 Sekunden. Tabelle 7.1 stellt die Laufzeit des (erstmaligen) Ladeprozesses in Abhängigkeit der in der Datenbank vorhandenen Relationen dar. Als Ergebnis lässt sich festhalten, dass auch bei sehr großen Datenbanken (die für das Einsatzgebiet des Informationssystems eher untypisch sind) der Ladevorgang in akzeptabler Zeit abgeschlossen wird. 44 7.1. Bewertung Anz. Relationen Laufzeit 3 15 ≤ 2s ≤ 2s 50 ≤ 2s 200 500 ≤ 5s ≤ 8s 1000 ≤ 15s Tabelle 7.1.: Laufzeit in Abhängigkeit der Relationen 7.1.2. Szenario 2: Anzahl von Attributen Die Anzahl der Attribute einer Relation hat Auswirkungen auf die Laufzeit der Bestimmung der Normalform, als auch der Normalisierungsalgorithmen. Um die Normalform zu bestimmen, muss u. a. überprüft werden, ob Attribute von einem Schlüsselkandidaten partiell abhängig sind. Die Berechnung der Schlüsselkandidaten ist NP-vollständig (s. [RG03], vgl. [Mei09]). Sie erfordert die Bestimmung der Potenzmenge der n Attribute, sodass hier also der Speicherverbrauch bereits in O(2n ) liegt. Tabelle 7.2 stellt die Laufzeit der Bestimmung der Normalform in Abhängigkeit der vorhandenen Attribute dar. Anz. Attribute Laufzeit 3 6 11 13 ≤ 1s ≤ 1s ≤ 2s ≤ 4s 14 ≤ 5s 15 16 17 ≤ 11s ≤ 25s kritisch Tabelle 7.2.: Laufzeit in Abhängigkeit der Attribute Bei einer Kardinalität der Attributmenge von n ≥ 17 wird folgende Exception geworfen: 1 2 3 4 5 6 7 8 9 Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space at java.util.LinkedHashMap.createEntry(LinkedHashMap.java:424) at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:406) at java.util.HashMap.put(HashMap.java:385) at java.util.HashSet.add(HashSet.java:200) at fd.Relation.powerset(Relation.java:927) at fd.Relation.determineKeys(Relation.java:283) at fd.Relation.is2NF(Relation.java:683) at fd.Relation.getNormalform(Relation.java:207) Listing 7.1: Heap Space Exception Dieser Fehler ist offensichtlich auf eine Erschöpfung des Heap-Speichers der JVM durch die Größe der Potenzmenge, die in der Methode powerset der Klasse Relation berechnet wird, zurückzuführen. Die Standard-Heap-Größe auf 32-Bit-Systemen ist 64 MB, die durch die Berechnung der Potenzmenge für n = 17 offenbar schnell erreicht werden, wie der Speicherauszug in Abbildung 7.1 zeigt. Eine Analyse der allozierten Objekte (vgl. Abbildung 7.2) ergibt zudem, dass das Problem durch zuviele LinkedHashMap-Einträge entsteht, die wiederum die interne Implementierung der in powerset() verwendeten LinkedHashSet-Objekte sind. 45 7. Zusammenfassung und Ausblick Abhilfe schafft hier die Anpassung des für die JVM verfügbaren Heaps mit den Optionen -Xms256m -Xmx256m. Jedoch sollte durch die hauptsächliche Anwendung in der Lehre die erwartete durchschnittliche Attributmenge einzelner Relationen deutlich unter der problematischen Größe liegen. Abbildung 7.1.: Heap Dump Abbildung 7.2.: Objekt-Allokation 46 7.2. Fazit 7.2. Fazit Im Rahmen dieser Arbeit wurde ein Informationssystem für funktionale Abhängigkeiten in PostgreSQL-Datenbanken entwickelt, welches die Visualisierung und Manipulation funktionaler Abhängigkeiten, sowie eine automatische Normalisierung relationaler Datenbankschemata ermöglicht. Dazu wurde zunächst eine Einführung in die relationale Entwurfstheorie gegeben (s. Kapitel 3) und Lösungsstrategien für die wesentlichen Problemstellungen entwickelt (s. Kapitel 4.3). Daraufhin erfolgte eine Beschreibung der resultierenden Softwarearchitektur (Kapitel 5) und der Entwurf einer grafischen Benutzeroberfläche. Eines der Hauptanliegen während der Entwicklung war es, die GUI möglichst benutzerfreundlich und robust zu gestalten, um das Informationssystem auch in der Praxis einsetzbar zu machen. Dabei eignet es sich insbesondere für den Einsatz in der Lehre, um Studierenden die wesentlichen Konzepte der Datenbanktheorie interaktiv und anschaulich zu vermitteln. Außerdem wird Lehrenden ein Werkzeug zur Verfügung gestellt, mit dem auf einfache Weise Übungsaufgaben gestellt und überprüft werden können. Ein weiteres Anwendungsgebiet findet sich in der gezielten Bewertung und Verbesserung von Datenbankschemata. Denn anders als ähnliche Systeme, die zu reinen Lehrzwecken entwickelt wurden und daher nur theoretisch bzw. modellbasierte Zerlegungen durchführen können (vgl. [Sel05]), ist das in dieser Arbeit entwickelte System in der Lage, beliebige bestehende Relationenschemata zu normalisieren und die Änderungen, d. h. die Zerlegung, in der Datenbank zu implementieren. Wie mittels Performanceanalyse in Kapitel 7.1 gezeigt werden konnte, läuft das System auch bei relativ großen Datenbanken bzw. Relationen schnell und stabil. Dass bei Relationen mit Attributmengen einer bestimmten Größe der Speicherverbrauch stark ansteigt, ist aufgrund der NP-Vollständigkeit der Bestimmung der Schlüsselkandidaten nicht zu vermeiden (vgl. [RG03]). Dies sollte bei den typischen Anwendungsszenarien jedoch zu vernachlässigen sein. Ein konsequenter nächster Schritt in der Weiterentwicklung wäre es, ein Normalisierungsverfahren für BCNF zu implementieren. Durch den modularen Aufbau des Systems und die klare Aufgabentrennung sollte dies keine Probleme bereiten, da nur ein weiterer Zerlegungsalgorithmus zu realisieren wäre. Die Erweiterung auf 4NF hingegen würde die Implementierung neuer Datenstrukturen und Algorithmen, die auch MVDs unterstützen, erfordern. Für den Einsatz in der Lehre sind die höheren Normalformen jedoch uninteressant und auch ihre praktische Bedeutung ist relativ gering, sodass hier wenig Weiterentwicklungsbedarf besteht. Eine weitere mögliche Richtung für zukünftige Entwicklungen ist die Unterstützung von graphischen Verfahren zur Darstellung von FAen. In dieser Arbeit wurde bewusst darauf verzichtet, neben der tabellarischen weitere Darstellungsformen zu unterstützen, da bei einer großen Zahl von FAen graphische Darstellungen schnell unübersichtlich werden, wohingegen die tabellarische für beliebige Größen gut skaliert. 47 Literatur [Arm74] Armstrong, William W.: Dependency Structures of Data Base Relationships. In: IFIP Congress, 1974, S. 580–583 [BB95] Bell, Siegfried ; Brockhausen, Peter: Discovery of Constraints and Data Dependencies in Databases (Extended Abstract). In: ECML ’95: Proceedings of the 8th European Conference on Machine Learning. London, UK : Springer-Verlag, 1995. – ISBN 3–540–59286–5, S. 267–270 [Cod70] Codd, E. F.: A relational model of data for large shared data banks. In: Commun. ACM 13 (1970), Nr. 6, S. 377–387. – ISSN 0001–0782 [KE06] Kemper, Alfons ; Eickler, André: Datenbanksysteme - Eine Einführung, 6. Auflage. Oldenbourg, 2006. – ISBN 3–486–57690–9 [Mei09] Meine, Matthias: Funktionale Abhängigkeiten in PostgreSQL-Datenbanken - Normalisierungsverfahren. Bachelorarbeit, Institut für Informationssysteme, Technische Universität Braunschweig. Deutschland, September 2009 [MR92a] Mannila, Heikki ; Räihä, Kari-Jouko: The design of relational databases. Boston, MA, USA : Addison-Wesley Longman Publishing Co., Inc., 1992. – ISBN 0–201–56523–4 [MR92b] Mannila, Heikki ; Räihä, Kari-Jouko: On the complexity of inferring functional dependencies. In: Discrete Appl. Math. 40 (1992), Nr. 2, S. 237– 243. – ISSN 0166–218X [Pos09] PostgreSQL Global Development Group: The Information Schema. Website, 2009. – Verfügbar online unter: http://www.postgresql.org/ docs/current/interactive/information-schema.html; abgerufen am 14. Juni 2009. [RG03] Ramakrishnan, Raghu ; Gehrke, Johannes: Database Management Systems. New York, NY, USA : McGraw-Hill, Inc., 2003. – ISBN 0072465638, 9780072465631 [Sel05] Selikoff, Scott: Database Normalization Tool. Website, 2005. – Cornell University. Dept. of Computer Science. http://dbtools.cs.cornell.edu; abgerufen am 01. Mai 2009. 48 Literatur [SK08] Stark, Thomas ; Krüger, Guido: Handbuch der Java-Programmierung. Standard Edition Version 6. Inkl. DVD (Programmer’s Choice): Standard Edition Version 6. 5. Auflage. Addison-Wesley, Muenchen, 2008. – ISBN 9783827328151 [SKS05] Silberschatz, Abraham ; Korth, Henry F. ; Sudarshan, S.: Database System Concepts. 5. McGraw-Hill Science/Engineering/Math, 2005. – ISBN 0072958863 [SSH08] Saake, Gunter ; Sattler, Kai-Uwe ; Heuer, Andreas: Datenbanken: Konzepte und Sprachen, 3. Auflage. mitp-Verlag, Redline GmbH, 2008. – ISBN 978–3–8266–1664–8 [Sun09] Sun Microsystems: What are the system requirements for Java 6? Website, 2009. – Verfügbar online unter: http://www.java.com/en/download/ help/6000011000.xml; abgerufen am 18. April 2009. [Ull09] Ullenboom, Christian: Java ist auch eine Insel. Programmieren mit der Java Standard Edition Version 6 (Galileo Computing). 8. Auflage. Galileo Press, 2009. – ISBN 9783836213714 49 Sachwortregister – Symbole – ≡ ................................ R ................................ → . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11, 1NF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2NF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3NF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . –F– 12 11 23 16 16 16 –A– Anomalie . . . . . . . . . . . . . . . . . . . . . . . . . Armstrong-Axiome . . . . . . . . . . . 11, Assertion . . . . . . . . . . . . . . . . . . . . . . . . . Attributhülle . . . . . . . . . . . . . . . . . 13, –G– GUI . . . . . . . . . . . . . . . . . . . . . . 24, 34, 38 –H– 15 25 22 25 –B– BCNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Bytecode . . . . . . . . . . . . . . . . . . . . . . . . . . 5 –C– Codd . . . . . . . . . . . . . . . . . . . . . . . . . . . 1, 3 Constraints . . . . . . . . . . . . . . . . . . . 21, 26 –D– Data Mining . . . . . . . . . . . . . . . . . . . . . 21 Datenbankkatalog . . . . . . . . . . . . . 9, 27 Denormalisierung . . . . . . . . . . . . . . . . . 16 Domäne . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 –E– Entwurfstheorie . . . . . . . . . . . . . . . . . . . 1 50 FA . . . . . . . . . . . . . . . . . . . . 11, 17, 21, 25 Fremdschlüssel . . . . . . . . . . . . . . . . 21, 26 funktionale Abhängigkeit . . . siehe FA hierarchisches Modell . . . . . . . . . . . . . . 1 Hülle . . . . . . . . . . . . . . . . . . . . . . . . . 11, 25 –I– Impedance Mismatch . . . . . . . . . . . . . . 6 Inferenzregeln . . . . . . . . . . . . . . . . . . . . 11 Information Schema . . . . . . . . . . . . . . 27 Instanz . . . . . . . . . . . . . . . . . . . . . . . 11, 24 Integritätsbedingung . . . . . . . . . . . 4, 22 –J– Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Java Database Connectivity . . . . siehe JDBC Java Runtime Environment . . . . siehe JRE Java Virtual Machine . . . . siehe JVM JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 JRE . . . . . . . . . . . . . . . . . . . . . . . . . . . 5, 32 JVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 –K– kanonische Überdeckung . . . . . . . . . . 12 Sachwortregister Relationenmodell . . . . . . . . . . . . . . . . . . 3 –M– mehrwertige Abhängigkeit . . . . . . . . Membership-Problem . . . . . . . . . . . . . Metadaten . . . . . . . . . . . . . . . . . . . . . 9, MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 25 27 34 –N– Netzwerkmodell . . . . . . . . . . . . . . . . . . . 1 Normalform . . . . . . . . . . . . . . . . . . . . . . 15 Boyce-Codd- . . . . . . . . . . . . . . . . . 16 Dritte . . . . . . . . . . . . . . . . . . . . . . . . 16 Erste . . . . . . . . . . . . . . . . . . . . . . . . . 16 Zweite . . . . . . . . . . . . . . . . . . . . . . . . 16 Normalisierung . . . . . . . . 15, 17, 20, 25 –P– PostgreSQL . . . . . . . . . . . . . . . . . 2, 7, 32 Produktfunktionen . . . . . . . . . . . . . . . 17 –R– Redundanz . . . . . . . . . . . . . . . . . . . . . . . 15 –S– Schlüsselkandidat . . . . . . . . . . . . . . . . 14 Schlüssel . . . . . . . . . . . . . . . . . . . . . . . . . 14 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . 2, 4, 7 Synthesealgorithmus . . . . . . . . . . . . . . 15 –T– Transaktion . . . . . . . . . . . . . . . . . . . . . . . 9 Trigger . . . . . . . . . . . . . . . . . . . . 21, 23, 27 -funktion . . . . . . . . . . . . . . . . . . . . . 23 Tupel . . . . . . . . . . . . . . . . . . 3, 11, 24, 25 –V– View . . . . . . . . . . . . . . . . . . . . . . . . . 21, 26 –Z– Zerlegungsalgorithmus . . . . . . . . . . . . 15 51 A Detaillierter UML-Entwurf A.1. Analyse von /F110/ und /F120/ Grobanalyse: Abbildung A.1 Von den angezeigten Relationen kann der Benutzer eine selektieren, die dann von FD geladen wird. Dabei wird die Normalform der Relation bestimmt und zusammen mit den FAen von der GUI angezeigt. Abbildung A.1.: Verteilung von /F110/ und /F120/ Feinanalyse: Abbildung A.2 Wenn über die View der Befehl zum Laden einer Relation ausgelöst wird, leitet der Controller diesen Aufruf an den SchemaManager weiter. Dieser lädt die übergebene Relation, woraufhin ein PropertyChangeEvent ausgelöst wird, der über den Controller zurück an die View gesendet wird und eine Aktualisierung der Anzeige veranlasst. 52 A.2. Analyse von /F200/ Abbildung A.2.: Sequenzdiagramm für /F110/ und /F120/ A.2. Analyse von /F200/ Grobanalyse: Abbildung A.3 Entscheidet sich der Benutzer dazu, eine neue FA zu spezifizieren, zeigt die GUI die in der Relation vorhandenen Attribute an. Aus diesen stellt der Benutzer die gewünschte FA zusammen. Die Komponente FD prüft darauf hin, ob die FA gültig ist, d. h. noch nicht impliziert wird und nicht gegen die Ausprägung der Relation verstößt. Sind beide Bedingungen erfüllt, werden die Änderungen an der Normalform berechnet und von der GUI in einer Vorschau angezeigt; andernfalls erfolgt eine Fehlermeldung. Wenn der Benutzer die Vorschau prüft, kann er sich entscheiden, weitere FAen zu spezifizieren, die Änderungen zurückzusetzen oder aber einen Commit durchzuführen. Bei letzterem stellt DBTools zunächst eine Datenbankverbindung her und implementiert die FA durch einen Trigger. Der fd_catalog wird ebenfalls aktualisiert und die Verbindung wieder geschlossen. Die GUI übernimmt dann die durchgeführten Änderungen. Feinanalyse: Abbildung A.4 Spezifiziert der Benutzer eine neue FA, wird ein FDep-Objekt angelegt, das von der View über den Controller an den SchemaManager übergeben wird. Die Funktion isInClosure(FDep) prüft zunächst, ob die FA bereits von den vorhandenen FAen impliziert wird. Ist dies nicht der Fall, wird über den ConnectionManager eine Ver- 53 A. Detaillierter UML-Entwurf Abbildung A.3.: Verteilung von /F200/ 54 A.3. Analyse von /F210/ bindung hergestellt. Mit deren Hilfe überprüft checkInstance(FDep), ob die FA gegen die momentane Ausprägung der Relation verstößt. Wenn dies nicht der Fall ist, fügt addAdditionalFdep(FDep) die FA zur Menge der temporären FAen hinzu. Ein ausgelöster PropertyChangeEvent veranlasst dann die View dazu, die Anzeige zu aktualisieren und eine Vorschau auf die Auswirkungen der neuen FA zu erstellen. Entscheidet sich der Benutzer dann für einen Commit, wird in einer Schleife für jede temporär hinzugefügte FA der CatalogManager aufgerufen, der diese jeweils durch einen Trigger implementiert und den fd_catalog aktualisiert. Nach dem Schließen der Verbindung wird die Anzeige auf den aktuellen Zustand gebracht. A.3. Analyse von /F210/ Grobanalyse: Abbildung A.5 Wenn eine selektierte FA entfernt werden soll, prüft FD zunächst, ob diese durch einen Schlüsselconstraint implementiert ist. Ist dies nicht der Fall, wird die Normalform der Relation nach Entfernen der FA berechnet und von der GUI angezeigt. Der Benutzer kann dann weitere FAen entfernen, die Änderungen zurücksetzen oder aber einen Commit durchführen. Für den Commit wird von DBTools eine Datenbankverbindung hergestellt und die entsprechenden FAen und die sie implementierenden Trigger werden entfernt. Nach dem Schließen der Verbindung wird die Anzeige mit der veränderten Relation aktualisiert. Feinanalyse: Abbildung A.6 Spezifiziert der Benutzer eine zu löschende FA, wird deren ID von der View über den Controller an den SchemaManager übergeben. Dieser lässt über den CatalogManager eine Verbindung herstellen und alle Informationen der zu dieser ID gehörenden FA aus dem fd_catalog auslesen, die dann in einem FDep-Objekt gespeichert werden. Das resultierende Objekt wird dann mittels addRemovedFdep(FDep) zur Menge der temporär gelöschten FAen hinzugefügt. Ein ausgelöster PropertyChangeEvent veranlasst dann die View dazu, die Anzeige zu aktualisieren und eine Vorschau auf die Auswirkungen der neuen FA zu erstellen. Entscheidet sich der Benutzer dann für einen Commit, wird in einer Schleife für jede temporär entfernte FA der CatalogManager aufgerufen, der diese und den jeweils implementierenden Trigger löscht und den fd_catalog aktualisiert. Nach dem Schließen der Verbindung wird die Anzeige auf den aktuellen Zustand gebracht. 55 A. Detaillierter UML-Entwurf Abbildung A.4.: Sequenzdiagramm für /F200/ 56 A.3. Analyse von /F210/ Abbildung A.5.: Verteilung von /F210/ 57 A. Detaillierter UML-Entwurf Abbildung A.6.: Sequenzdiagramm für /F210/ 58 A.4. Analyse von /F300/ A.4. Analyse von /F300/ Grobanalyse: Abbildung A.7 Wenn der Benutzer eine gewünschte Normalform auswählt, berechnet die Komponente FD die daraus resultierende Zerlegung der Relation. Eine Vorschau darauf wird von der GUI angezeigt. Ein Rollback verwirft die Zerlegung, während ein Commit dazu führt, dass FD die Änderungen am Datenbankschema vornimmt und DBTools daraufhin den fd_catalog aktualisiert. Nach dem Schließen der benutzen Verbindung wird die Anzeige aktualisiert. Abbildung A.7.: Verteilung von /F300/ Feinanalyse: Abbildung A.8 Eine Normalisierung wird durch den Benutzer ausgelöst, woraufhin die gewünschte Normalform von der View über den Controller an den SchemaManager übergeben 59 A. Detaillierter UML-Entwurf wird. Dieser führt (im Falle von 3NF) auf der geladenen Relation den Synthesealgorithmus aus, der eine Menge von neuen Relationen zurückliefert. Durch einen PropertyChangeEvent wird die View veranlasst, eine Vorschau dieser Zerlegung anzuzeigen. Wird dann vom Benutzer ein Commit ausgeführt, erstellt der SchemaManager mittels createTables() die neuen Relationen in der Datenbank, kopiert die Inhalte der Originalrelation in ebendiese (copyContents()) und behandelt eventuell vorhandene Fremdschlüsselbeziehungen und Check-Constraints durch den Aufruf von addConstraints(). In einer Schleife werden dann alle FAen der Originalrelation mit removeFdep(id,table) gemäß /F210/ entfernt und schließlich die Relation selbst aus der Datenbank gelöscht. In einer weiteren Schleife fügt addFdep(FDep) die auf die neuen Relationen aufgeteilten FAen wieder hinzu. Schließlich wird die Verbindung geschlossen und ein PropertyChangeEvent ausgelöst, der die View aktualisiert. 60 A.4. Analyse von /F300/ Abbildung A.8.: Sequenzdiagramm für /F300/ 61 B Bedienungsanleitung Bei der Entwicklung des Informationssystems wurde darauf geachtet, die Bedienung möglichst einfach und effizient zu gestalten. Zur Unterstützung des Benutzers wird in diesem Anhang zusätzlich ein kurzer Abriß über die Funktionalität geboten, um so die Einarbeitungszeit zu minimieren. Programmstart Direkt nach Programmstart ist der größte Teil der Oberfläche deaktiviert (s. Abbildung B.1), da keine Informationen zur Anzeige vorhanden sind. Über das Serversymbol in der Toolbar oder das Hauptmenü File | Connect lässt sich nun eine Verbindung erstellen oder auswählen. Abbildung B.1.: Systemstart 62 l Verbindungsauswahl Der Verbindungsmanagerdialog (s. 1 in Abbildung B.2) bietet die Möglichkeit, Profile für Datenbankverbindungen zu definieren, die auf der Festplatte abgespeichert werden. So müssen die zugehörigen Verbindungsdaten nur einmal eingegeben werden und das Wechseln zwischen verschiedenen Servern/Datenbanken ist problemlos möglich. Bestehende Profile können geändert oder entfernt werden. Wird ein Profil gewählt, kann mittels Connect eine Verbindung hergestellt werden. l 1 Abbildung B.2.: Verbindungsaufbau l FA-Manipulation Wurde eine Verbindung hergestellt, wird das Datenbankschema ausgelesen und im Relationenbaum (s. 1 in Abbildung B.3) auf der linken Seite dargestellt. Die Relationen können aufgeklappt werden, woraufhin ihre Attribute sichtbar werden. Wird eine Relation selektiert, werden im mittleren Bereich 2 die zugehörigen FAen in Tabellenform angezeigt. Im rechten Bereich der Oberfläche lassen sich durch Selektion von Listeneinträgen 3 weitere FAen spezifizieren, die dann ebenfalls in der zentralen Tabelle erscheinen, jedoch grün hinterlegt sind, um deutlich zu machen, dass sie sich insofern von den anderen FAen unterscheiden, als dass sie noch nicht in die Datenbank festgeschrieben wurden. Das Festschreiben kann über l l 63 B. Bedienungsanleitung l das Diskettensymbol in der Toolbar 4 erfolgen, wohingegen das Warnsymbol dazu führt, dass die Änderungen zurückgesetzt werden. Wird eine Zeile in der Tabelle und somit die entsprechende FA ausgewählt, kann diese über den Button Remove 5 entfernt werden. Die FA verschwindet darauf hin nicht aus der Tabelle, sondern wird rot hinterlegt. Natürlich können sowohl mehrere neue FAen spezifiziert werden, als auch mehrere bestehende FAen entfernt werden, bevor die Änderungen in die Datenbank festgeschrieben werden. Die Auswirkungen der Änderungen auf den Normalisierungsgrad werden im oberen rechten Bereich dargestellt, wo sowohl die momentane Normalform 6 , als auch jene nach einem Commit 7 angezeigt wird. l l l l 4 l 7l 6 l 1 l l 3 2 l 5 Abbildung B.3.: Hinzufügen/Entfernen von FAen l Normalisierung Soll gezielt eine bestimmte Normalform, d. h. 2NF oder 3NF, hergestellt werden, kann diese über die Radio-Buttons (s. 1 in Abbildung B.4) ausgewählt und mittels Preview die entsprechende Vorschau angezeigt werden. Die Checkbox Steps ermöglicht es, optional die Berechnungsschritte, die zum Ergebnis der Normalisierung führen, mit anzuzeigen. Die Vorschau, wie in Abbildung B.4 2 zu sehen, beinhaltet die neu entstehenden Relationen und ihre Attribute, sowie Infor- l 64 mationen darüber, wie die FAen der zu normalisierenden Relation aufgeteilt werden. Der Vorschaudialog ist dabei an die Oberfläche des Hauptprogramms angelehnt und erleichtert somit die Bedienung. Neu zu erstellende Relationen können umbenannt werden, etwa um sprechende Namen zu vergeben. Commit schreibt die Änderungen in die Datenbank fest, Cancel führt keine Änderungen durch, sondern verwirft die Änderungen. l 1 l 2 Abbildung B.4.: Normalisierung 65 C Schnittstellenspezifikation Die in den Tabellen C.1 und C.2 beschriebenen Interfaces definieren die Schnittstellen dieser Arbeit zu [Mei09]. Detailliertere Informationen sind dem Javadoc dieser Arbeit zu entnehmen. Operation getNormalform(boolean preview) : String normalize(NF form) : SortedSet<SynRelation> isKey(Set<String> candidate) : boolean xPlus(boolean preview, Set<String> attributes) : Set<String> getColumns() : SortedSet<String> setColumns(SortedSet<String> columns) : void getRelationName() : String getAdditionalFdeps() : Set<FDep> setAdditionalFdeps(Set<FDep> additionalFdeps) : void addAdditionalFdep(FDep fd) : void getRemovedFdeps() : Set<FDep> setRemovedFdeps(Set<FDep> removedFdeps) : void addRemovedFdep(FDep fd) : void getFdeps() : SortedSet<FDep> addFdep(FDep fd) : void Beschreibung Bestimmt die Normalform der Relation. preview gibt an, ob temporäre Änderungen an den FAen berücksichtigt werden sollen. Zerlegt die Relation in eine Menge von Relationen, die in der durch form bestimmten Normalform sind. Liefert true, wenn candidate ein Schlüsselkandidat der Relation ist. Berechnet die transitive Attributhülle. preview gibt an, ob auch temporäre FAen berücksichtigt werden. Gibt die Attribute der Relation zurück. Setzt die Attribute der Relation auf columns. Gibt den Relationennamen zurück. Gibt alle temporär hinzugefügten FAen zurück. Setzt die Menge der temporär hinzugefügten FAen auf additionalFdeps. Fügt eine FA fd temporär hinzu. Gibt alle als gelöscht markierten FAen zurück. Setzt die Menge der als gelöscht markierten FAen auf removedFdeps. Markiert fd temporär als gelöscht. Gibt alle dauerhaften FAen zurück. Fügt eine FA fd dauerhaft hinzu. Tabelle C.1.: Schnittstellenspezifikation Normalizable 66 Operation generateAddConstraint( String referencedTable, String referencingTable, Map<String, String> refMap, String onUpdate, String onDelete) : String generateCreateTable( Map<String, String> typeMap, SynRelation srel) : String generateTFunction(FDep fd, String rname, String fname) : String generateTFunctionName(int id, String rname) : String generateTrigger(int id, String rname, String fname) : String generateTriggerName(int id, String rname) : String generateIntraFKeys( Set<SynRelation> newRelations, Connection con) : Set<String> Beschreibung Generiert SQL-Code zum Hinzufügen von Fremdschlüsseln zu einer Relation. Generiert SQL-Code zum Erstellen einer Relation. Generiert SQL-Code für eine Triggerfunktion. Generiert einen eindeutigen Namen für eine Triggerfunktion. Generiert SQL-Code für einen Trigger. Generiert einen eindeutigen Namen für einen Trigger. Generiert SQL-Code für eine Menge von internen Fremdschlüsselbeziehungen. Tabelle C.2.: Schnittstellenspezifikation IStatement 67