Informatik Zusammenfassung JII.2 #1 Entity-Relationship-Modell erstellen Aufgabenstellung Eine Firma möchte ihre Abteilungen über eine Datenbank verwalten. Hierbei soll jeder Abteilung ein Name und ein Zimmer zugeteilt werden. Weiterhin hat jede Abteilung einen Abteilungsleiter, der mit Name und Anschrift erfasst werden soll. Zudem soll jeder Abteilung Material mit den Attributen Bezeichnung, Vorrat und Kosten zugeteilt werden. Erstelle ein Entity-Relationship-Modell. Beispiel: Die Personalabteilung hat den Abteilungsleiter Peter Mayer und hat die Materialien Computer, Kugelschreiber und Papier. Lösung 1. Schritt: Entitätstypen und Beziehungen (+ Kardinalitäten) identifizieren Entitätstypen sind in Datenbanken das was man früher unter einer Klasse verstand. So war der Kunde früher ein Entitätstyp. Das was wir früher als Objekt einer Klasse bezeichnet haben heißt bei Datenbanken Entität eines Entitätstyps. Kardinalitäten bestimmen die Mengenverbindungen zwischen den einzelnen Tabellen (Bsp.: Ein Ort kann mehrere Einwohner haben, aber ein Einwohner lässt sich eindeutig zu einem Ort zuordnen – Also ist die Kardinalität 1:n). Es kann allerdings keine n:m Verbindung geben! Entitäten: Abteilung Abteilungsleiter Material Karsdinalitäten: Abteilung 1:1 Abteilung 1:n Abteilungsleiter Material 2. Schritt: Identitätsschlüssel (Primärschlüssel) und Fremdschlüssel für Entitäten bestimmen Ein Primärschlüssel identifiziert ein Objekt eindeutig (z.B. gibt es nur eine PLZ für jeden Ort; es gibt keine zwei Orte mit gleicher PLZ – ein Name hingegen ist nicht geeignet als Primärschlüssel zu fungieren, da es mehrere verschiedene Personen mit dem gleichen Namen geben kann). Falls sich kein Primärschlüssel aus der Aufgabenstellung identifizieren lässt, so erstellt man sich einen eigenen: Üblich ist eine laufende Nummer. Über den Primärschlüssel findet man das jeweilige Objekt in der Datenbank Um Verbindungen zwischen den einzelnen Tabellen in der Datenbank herzustellen werden Fremdschlüssel vergeben. Diese Fremdschlüssel sind die Primärschlüssel der jeweils verbunden Entitätstypen. Ist einer Abteilung bspw. die LeiterNr „1“ zugeteilt, so ist der Leiter mit der LeiterNr „1“ in der Tabelle Abteilungsleiter die Führungskraft in der Abteilung. Seite 1 Primärschlüssel: Abteilung: AbteilungsNr Abteilungsleiter: LeiterNr Material: MaterialNr Fremdschlüssel: Abteilung: LeiterNr Abteilung: MaterialNr 3. Schritt: Attribute und ihre Wertebereiche der Entitätstypen bestimmen Man muss wie beim normalen Programmieren den Attributen eine „Form“ vorgeben. Diese Formen sind die Datentypen. Bei Datenbanken sind die folgenden Formen wichtig: CHAR(), VARCHAR(%n) BOOLEAN, DATE, DATETIME, DOUBLE, FLOAT, INT. In den Klammern hinter CHAR lässt sich eine maximale Länge der Zeichenkette eingeben, VARCHAR dagegen erweitert sich selbst falls die Länge zu kurz ist. Abteilung: - Name: VARCHAR() -Zimmer: VARCHAR() -LeiterNr: CHAR(10) -MaterialNr: CHAR(10) Abteilungsleiter: -LeiterNr: CHAR(10) -Name: VARCHAR() -Anschrift: VARCHAR() Material: -MaterialNr: CHAR(10) -Bezeichnung: VARCHAR() -Vorrat: INT -Kosten: DOUBLE 4. Schritt: Schema erstellen All diese Informationen stellt man abschließend in einem Schema da, in dem die Entitätstypen durch Pfeile verbunden werden die bspw. durch Pfeile (bei uns durch Rauten). Die Kardinalitäten werden jeweils an die Pfeilenden geschrieben. Datenbank in Eclipse eingeben Um ein solches Modell in Eclipse zu übertragen muss man zunächst eine neue *.clay-Datei erstellen. Dies erreicht man mit rechtsklick auf das Projekt. Dort findet sich unter „New“ ganz unten der Punkt „Other“. Nach klick hierauf öffnet sich ein Seite 2 weiteres Fenster. In diesem findet sich im Ordner „Database Modelling“ der Eintrag „Clay Database Design Diagram“. Dieser wird ausgewählt! Anschließend kommt ein weiteres Fenster in dem nur noch der Dateiname eingegeben werden muss und dass dann auch mit „Fertig stellen“ geschlossen werden kann. Nun sollte sich ein blaugraues Fenster geöffnet haben mit einer kleinen Symbolleiste an der linken Seite, die etwa wie diese hier aussehen sollte. Zunächst benutzen wir das unterste Tool – hiermit können wir die Entitätstypen erstellen. Ist das Tool ausgewählt wird einmal in die Arbeitsfläche geklickt und eine leere Tabelle erscheint. Auf diese wird doppelt geklickt, sodass sich ein Fenster öffnet. Dieses Fenster hat die Registerkarten „Table“ und die „Columns“. In der Registerkarte „Table“ wird zunächst unter „Name:“ der Name der Entität eingetragen (bspw.: Abteilung) – die anderen Felder kann man sich schenken. Nun zur Registerkarte „Columns“. Hier fügen wir zunächst mittels „Add new“ ein neues Attribut hinzu. Bei „Name:“ wird der jeweilige Name eingegeben, bei „Data Type“ der entsprechende Datentyp ausgewählt. Interessant sind noch die Checkboxen unten im Fenster. Beim Primärschlüssel muss die Checkbox „Primary Key“ gewählt sein! „Mandatory“ steht für Pflichtfelder die nicht leer sein dürfen (wird üblicherweise auch gewählt, ist aber nicht so wichtig in unserem Fall) und „Auto Increment“ steht eigentlich für laufende Nummern, funktioniert allerdings bei uns nicht! Die fertige Tabelle für den Abteilungsleiter sollte etwa so aussehen: Mit Klick auf „OK“ werden die Daten in die Tabelle übernommen, sie ist fertig. Genauso verfahren wir nun noch mit Material und Abteilungsleiter, sodass wir drei Tabellen im Arbeitsfenster haben. Nun bedienen wir uns des zweituntersten Symbols der Symbolleiste. Mit diesem Symbol ( ) können wir die einzelnen Entitätstypen miteinander in Beziehungen setzen. Dazu klickt man zunächst auf die übergeordnete Tabelle (also bspw.: Abteilung) und anschließend auf die untergeordnete Tabelle (bspw.: Material). Die beiden Tabellen sind dann durch eine Linie verbunden und in der oberen Tabelle taucht nun auch der Fremdschlüssel der unteren Tabelle auf. Will man den Verbindungen noch Kardinalitäten zuweisen, so muss doppelt auf die Verbindung geklickt werden und im sich öffnenden Fenster kann in der Registerkarte „Decorations“ der jeweilige Typ ausgewählt werden. An einem Ende steht jeweils wie viele Entitäten es von diesem Entitätstyp geben kann. An der Abteilung wird also jeweils eine „1“ stehen, da es nur eine Abteilung pro Leiter und Material gibt, ebenso am Abteilungsleiter. Am Material steht „0…*“ da es pro Abteilung kein bis unendlich viel Material geben kann. Fertig sieht das Diagramm nun so aus wie hier rechts. Seite 3 bIst das Diagramm fertig, so muss man es in einen SQL-Code übersetzen (SQL ist die Programmiersprache für Datenbanken). Dazu klickt man irgendwo in den Arbeitsbereich mit der rechten Maustaste und wählt „Generate SQL (CREATE TABLE) Script…“ (s. Bild). Im sich öffnenden Fenster gibt man dem Script noch einen Namen und bestätigt mit „Fertig stellen“. Anschließend öffnet sich das fertige Script. Daneben öffnet sich zusätzlich noch die „Connection“-Registerkarte. In dieser müssen wir eine neue Verbindung zur Datenbank herstellen (diese Verbindung benutzt dann später die Klasse DBZugriff). Dazu wählen wir den ersten Punkt der Symbolleiste aus welcher hier im Bild auch angewählt ist. Dann öffnet sich wiederum ein Fenster in welchem man dann den Namen der Verbindung eingeben muss, bei Driver den „MySQL Driver“ wählen muss und bei User den Namen „root“ eintragen muss. Weiterhin müssen noch die Hacken vor „Auto Logon“ und „Auto Commit“ gesetzt werden. Nach Bestätigen mit „OK“ ist die Verbindung hergestellt – sie erscheint auch nun im „Connections“-Fenster. Klickt man doppelt auf sie öffnet sich ein weiteres Fenster in dem kurz geladen wird, wir aktivieren dadurch nämlich die Verbindung. Anschließend steht hinter dem Verbindungsname in Klammern eine Anzahl an laufenden Verbindungen (mindestens 1). Das ist das Zeichen das alles funktioniert hat. Des Weiteren hat sich ein leeres Textfenster geöffnet. In diesem Fenster wird nun zunächst eine neue Datenbank erstellt. Dies geschieht mittels Eingabe des Befehls „CREATE DATABASE …“. Statt … wird der jeweilige Name eingegeben (in unserem Fall Firma). Anschließend wird der ganze Code aus der vorhin erstellten SQL-Datei dahinter kopiert. Falls irgendwo das Wort „DEFAULT_SCHEMA“ auftaucht muss es übrigens ebenfalls durch den Namen der Datenbank ersetzt werden (passiert evtl. bei euch zuhause beim üben – in der Schule wohl eher nicht). Diesen Ganzen Code kann man nun mit Klick auf dieses Symbol ausführen lassen. Wenn keine Fehlermeldungen kommen hat es geklappt. Überprüfen kann man das noch mit dem Befehl „SELECT * FROM …“, wobei man … durch den Tabellennamen ersetzt. Bekommt man keinen Fehler hat es wiederum funktioniert. Arbeiten mit Datenbanken (speichern, lesen, ändern, löschen) 4-Schichten-Architektur Das GUI (Graphical-User-Interface) stellt die Verbindungsschicht zwischen dem Benutzer und dem Programm dar. In diesem kann der Nutzer seine Befehle ausführen und Daten eingeben. Wünscht der Nutzer ein Befehl in der Datenbank auszuführen, dann dürchläuft dieser Befehl alle vier Schichten: Zunächst wird in der GUI-Schicht ein Objekt der Fachklasse erstellt, welches wiederrum eine Methode X aus der Fachklasse ausführt. Diese Methode X ist in der Fachklasse Programmiert und erstellt wiederrum ein Objekt der Verbindungsschicht, welches wiederrum eine Methode Y aus der Verbindungsschicht ausführen kann. Diese Methode Y ändert, löscht oder fügt Daten zu der Datenbank hinzu. GUI Fachklasse Verbindungsschicht Seite 4 Datenbank Generelles Vorgehen Zunächst einmal muss auf jeden Fall eine oder mehrere Fachklasse (get-/set-Methoden, Attribute) sowie die Visual-Class programmiert werden. In dem Hauptfenster muss mittels der folgenden Zeile noch ein Objekt der Fachklasse angelegt werden: private Abteilungsleiter leiter1 = null; Weiterhin brauchen wir eine Methode, die die Daten von den Textfeldern in das Objekt überträgt. Dazu erstellen wir den Button „btAnlegen“ der ein Action-Performed-Event erhält welches wiederum die Methode anlegen ausführt die wie folgt programmiert wird (bis hier noch Stoff der letzten Klausuren!) private void anlegen() { leiter1 = new Abteilungsleiter(tfNummer.getText()); leiter1.setName(tfName.getText()); leiter1.setAnschrift(tfAnschrift.getText()); } Speichern, ändern, löschen Diese drei Vorgehensweisen sind alle nahezu ähnlich, von daher lassen sie sich relativ gut zusammen erläutern. In jedem der drei Fälle brauchen wir einen Button mit Action-Performed-Event der dann die jeweilige Methode in der Fachklasse ausführt die wir programmieren müssen. Wir müssen daher auf das vorher erstellte Objekt zugreifen. Bei ändern beispielsweise braucht die Methode noch einen Übergabewert, damit die Methoe weiß, welcher Abteilungsleiter geändert werden soll. Dazu bietet sich der „InputDialog“ beim JOptionPane an! //Abfragen der Kundennummer nach der gesucht werden soll. Speichern der Nummer im String s String s = JOptionPane.showInputDialog(null, "Bei welcher Nummer soll geändert werden?"); leiter1.aendern(s); //ActionListener der die Methode aendern() des Abteilungsleiterobjektes ausführt Jede der Methoden läuft gleich ab: Datenbank öffnen, SQL-Befehl schreiben, SQL-Befehl an die Verbindungsschicht übergeben und Datenbank schließen. Die SQL-Befehle sind jeweils anders aber die könnt ihr aus Frau Dercks Formelsammlung holen. public void erfasse() { //Objekt der Verbindungsschicht erstellen DBZugriff aktuellerZugriff = new DBZugriff(); //Datenbank öffnen aktuellerZugriff.oeffneDB(); //SQL-Befehl erstellen und im String stmt speichern String stmt = "INSERT INTO abteilungsleiter (LeiterNr ,Name ,Anschrift)" + "VALUES ('" + leiterNr + "', '" + name + "', '" + anschrift + ');"; //SQL-Befehl ausführen aktuellerZugriff.aendern(stmt); //Datenbank schließen aktuellerZugriff.schliesseDB(); } public void aendern(String primKey) { //String primKey ist die LeiterNr des Kunden der geändert werden soll! Seite 5 DBZugriff aktuellerZugriff = new DBZugriff(); aktuellerZugriff.oeffneDB(); String stmt = "UPDATE abteilungsleiter SET LeiterNr = '"+leiterNr+"', Name = '"+name+"'," + "Anschrift = '"+anschrift+"' WHERE LeiterNr = '"+primKey+"';"; aktuellerZugriff.aendern(stmt); aktuellerZugriff.schliesseDB(); } public void delete() { DBZugriff aktuellerZugriff = new DBZugriff(); aktuellerZugriff.oeffneDB(); String stmt = "DELETE FROM abteilungsleiter WHERE LeiterNr = '"+leiterNr+"';"; aktuellerZugriff.aendern(stmt); aktuellerZugriff.schliesseDB(); } Suchen Vom Grundaufbau ist auch die Suchen-Methode ähnlich den drei anderen. Allerdings erhalten wir hier einen Rückgabewert der auch ausgewertet werden muss. Zunächst aber fragen wir (wie beim ändern) ab nach welcher LeiterNr gesucht werden soll (InputDialog!) und leiten die weiter an die Methode suchen() in der Fachklasse die dann programmiert wird. In der Methode suchen() wird die Datenbank auch zunächst geöffnet und der SQL-Befehl wird übergeben und ausgeführt. Dieses Mal verwenden wir allerdings statt aktuellerZugriff.aendern nun aktuellerZugriff.lesen und wir müssen den Rückgabewert dieser Methode in einem Resultset abspeichern der dann mittels return zurückgegeben wird. public ResultSet suchen(String primKey) //String primKey ist die LeiterNr des Kundne der geändert werden soll! { DBZugriff aktuellerZugriff = new DBZugriff(); aktuellerZugriff.oeffneDB(); String stmt = "SELECT * FROM abteilungsleiter WHERE LeiterNr LIKE '"+primKey+"'"; //Methode lesen statt aendern verwenden und den Rückgabewert im Resultset speichern ResultSet rs = aktuellerZugriff.lesen(stmt); aktuellerZugriff.schliesseDB(); //das Resultset an das Hauptfenster zurückgeben - dort wird es ausgewertet return rs; } Das Resultset wird dann von der ursprünglichen Methode ausgewertet. Zunächst wird die next()Methode aufgerufen die das nächste Objekt des ResultSets (also das was wir gesucht haben) zurückgibt. Anschließend erhält man über getString(„Attributsname“) den Wert des jeweiligen Attributs, der dann ins Textfeld übertragen wird. Der try-catch-Block ist hier übrigens Pflicht! //Abfragen der Kundennummer nach der gesucht werden soll. Speichern der Nummer im String s String s = JOptionPane.showInputDialog(null, "Nach welcher Nummer soll gesucht werden?"); ResultSet rs = leiter1.suchen(s); //ActionListener der die Methode suchen() des Abteilungsleiterobjektes ausführt und speichern im ResultSet Seite 6 try { rs.next(); //Standartzeile - einfach auswendig lernen //Übertragen der jeweiligen Daten aus dem Resultset tfNummer.setText(rs.getString("LeiterNr")); tfName.setText(rs.getString("Name")); tfAnschrift.setText(rs.getString("Anschrift")); //Fehlerbehandlung } catch (SQLException ex) { ex.printStackTrace(); } Fehlerbehandlung Beim Arbeiten mit Datenbanken können an verschiedenen Stellen Fehler auftreten, die man abfangen sollte. Dabei arbeitet man mit mehreren try-catch Blöcken. Weiterhin muss man beachten, dass eine Datenbank bei der das öffnen nicht geklappt hat nicht bearbeitet werden kann. public void aendern(String primKey) { DBZugriff aktuellerZugriff = new DBZugriff(); int mOK=0; if(aktuellerZugriff.oeffneDB()==true) { mOK=1; String stmt = "UPDATE abteilungsleiter SET LeiterNr = '"+leiterNr+"', Name = '"+name+"'," + "Anschrift = '"+anschrift+"' WHERE LeiterNr = '"+primKey+"';"; if(aktuellerZugriff.aendern(stmt)==true) { mOK=2; if(aktuellerZugriff.schliesseDB()==true) { mOK=3; } } } switch(mOK) { case 0: JOptionPane.showMessageDialog(null, "Das Öffnen der Datenbank ist fehlgeschlagen!"); break; case 1: JOptionPane.showMessageDialog(null, "Das Ändern der Datenbank ist fehlgeschlagen!"); break; case 2: JOptionPane.showMessageDialog(null, "Das Schließen der Datenbank ist fehlgeschlagen!"); break; case 3: JOptionPane.showMessageDialog(null, "Es hatt alles funktioniert!"); break; } } Seite 7 Zunächst einmal muss man einen Integer einfügen (In diesem Fall mOK), der je nach Fehlerquelle später einen anderen Wert einnehmen wird. Zunächst wird er als Null definiert. Darüber hinaus muss man wissen, dass die Methoden aus der Verbindungsschicht jeweils einen Boolean als Rückgabewert besitzen, der angibt, ob die Methode funktioniert hat (true wenn sie funktioniert hat und false wenn nicht). Daher setzt man die Methode in einen if-Block der den Rückgabewert überprüft. Falls das Öffnen nicht geklappt hat, bleibt mOK null und die Datenbank wird nicht weiter bearbeitet (verzweigte if Bedingungen beachten!). Falls das Öffnen geklappt hat, dann wird mOK 1 und es wird zunächst versucht die Datenbank zu ändern und danach zu schließen. Je nachdem wird mOK dann zwei, wenn die Datenbank erfolgreich geändert wurde und 3 wenn sie auch erfolgreich geschlossen werden konnte. Nun muss der Wert von mOK ausgewertet werden. Dies wird am einfachsten mit einem Switch-caseBlock gelöst. In der Switch-Anweisung steht mOk in der Klammer und es wird je nach Wert der entsprechende Case-Block ausgeführt. Falls mOK = 0 ist schon das Öffnen gescheitert, bei mOK=1 ist das Ändern fehlgeschlagen und bei mOK=2 führte das Schlie0en zu einem Fehler. Besitzt mOK den Wert 3 dann hat alles perfekt funktioniert. Im Jeweiligen Case-Block wird die passende Meldung ausgegeben. Jeder Case-Block muss mit einem break-Befehl enden, damit der Compiler nicht fälschlicherweise auch die anderen Case-Blöcke ausführt. Suchen: try { rs.next(); //Standartzeile - einfach auswendig lernen //Übertragen der jeweiligen Daten aus dem Resultset tfNummer.setText(rs.getString("LeiterNr")); tfName.setText(rs.getString("Name")); tfAnschrift.setText(rs.getString("Anschrift")); //Fehlerbehandlung } catch (SQLException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(null, "Es wurde nichts gefunden!"); } Die suchen-Methode kann auch noch abstürzen, wenn kein Eintrag in der Datenbank gefunden wurde. Deshalb muss man in den schon bestehenden Catch-Block noch die Fehlermeldung programmieren. Fertig ist die Info-Zusammenfassung für die Klausur am nächsten Montag. Sie hat ja halbwegs deutlich gesagt wie die Aufgaben aussehen sollten und in etwa die Teile ist auch die Zusammenfassung jetzt auch aufgeteilt. Wie immer bei Fehlern/Fragen/eigenen Zusammenfassungen/… könnt ihr mir eine Mail schreiben ([email protected]). Ansonsten viel Erfolg am Montag. Ein Dankeschön geht noch an Kai Salzmann für die Hilfe bei der Zusammenfassung. Gruß, Florian Seite 8