Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


18.6.2

Abfrage von Werten



In der Datenbank sind die Daten zwar nur in einem Datentyp gespeichert, in JDBC kann man jedoch bei der Abfrage von Werten automatisch Typ-Konvertierungen durchführen, sofern der gewünschte Typ mit dem Datentyp in der Datenbank kompatibel ist. Welche Konvertierungen genau zulässig sind, zeigt Abbildung 18.13. Dort ist angegeben, welche Datentypen aus der Datenbank mit welchen getXXX()-Methoden abgerufen werden können.

Abbildung 18.13: Abfragen von Daten über ein ResultSet
Abbildung 18.13

Die Mehrheit der aufgeführten Methoden führt eine Konvertierung in primitive Java-Datentypen durch. Über die Methode getObject() kann man sich jeden Wert in der Ergebnismenge als Java-Objekt zurückgeben lassen. Die Verwendung von getObject() ist obligatorisch für den Zugriff auf Objekt-Typen.

Beim Aufruf von getBoolean() ist zu beachten, dass der Treiber zunächst versucht, den abgefragten Wert in eine Zahl zu konvertieren (falls es sich um einen alphanumerischen Typ handelt) und anschließend einen numerischen Vergleich durchführt. Ist der Wert gleich 0, wird false, ansonsten true zurückgeliefert.

Versucht man einen Wert über eine Methode abzufragen, die keine Konvertierung in einen adäquaten Typ durchführen kann, wird eine SQLException ausgelöst.

NULL-Werte

Ruft man die Daten über eine getXXX()-Methode ab, die das Ergebnis in einem primitiven Rückgabewert zurückliefert, wird vom Treiber ein Default-Wert zurückgegeben, falls in der Datenbank ein NULL-Wert enthalten ist. Bei der Abfrage von Zahlenwerten ist dieser Default-Wert 0, bei booleschen Typen false. Das heißt, beim Aufruf dieser Methoden kann man nicht unterscheiden, ob der Wert wirklich in der Datenbank gespeichert ist, oder ob es sich um einen vom Treiber generierten Default-Wert handelt, der als Ersatz für einen NULL-Wert zurückgegeben wird. Man kann jedoch auf folgende Arten herausfinden, ob in der Datenbank tatsächlich ein NULL-Wert gespeichert ist: Folgendes Beispiel zeigt, wie bei einer Abfrage geprüft wird, ob ein gültiger Preis verfügbar ist:
  float preis;
  while(rs.next()) {
    System.out.println(rs.getString("titel"));
    preis = rs.getFloat("preis");
    // Ist der Preis in der Datenbank NULL?
    if(rs.wasNull()) 
      System.out.println("Noch kein Preis verfügbar !");
    else 
      System.out.println("Preis: "+preis);
  }

Positionierung

Während in JDBC 1.0 nur das sequenzielle Abfragen der Ergebniszeilen möglich ist, kann man sich ab JDBC 2.0 in einer Ergebnismenge auch wieder zurückbewegen bzw. direkt an eine bestimmte Zeile in der Ergebnismenge springen. Im Folgenden werden die im Interface ResultSet definierten Konstanten aufgelistet, mit denen die Positionierbarkeit eingestellt werden kann: Positionierbare Ergebnismengen werden verwendet, indem man beim Erzeugen des Statement die entsprechende Konstante übergibt (z. B. createStatement()). Ein Beispiel dazu ist im folgenden Abschnitt enthalten, da die Positionierbarkeit immer zusammen mit der Veränderbarkeit angegeben wird.

Wenn man sich für die Verwendung einer positionierbaren Ergebnismenge entschieden hat, stellt das Interface ResultSet folgende Methoden zur Positionierung bereit: Alle diese Methoden haben den Ergebnistyp boolean. Sie liefern true zurück, wenn die neue Positionierung geglückt ist. Andernfalls wird false als Ergebnis zurückgegeben. Das ist z. B. dann der Fall, wenn last() aufgerufen wird, das ResultSet-Exemplar jedoch keine Datensätze enthält oder wenn der an absolute() übergebene Wert größer ist als die Anzahl der Datensätze in der Ergebnismenge.

Verändern von Datensätzen

Seit JDBC 2.0 gibt es die Möglichkeit, über ein ResultSet den aktuellen Datensatz zu verändern. Hierzu definiert ResultSet updateXXX()-Funktionen, wobei XXX durch einen konkreten Datentyp ersetzt werden muss. Ob die Daten einer Ergebnismenge aktualisiert werden können wird beim Erzeugen des zugrundeliegenden Statement-Exemplars durch folgende Konstanten definiert:

Bei der Durchführung von Änderungen muss man folgendermaßen vorgehen: Der Ablauf bei der Veränderung von Daten über Ergebnismengen ist für INSERT, UPDATE und DELETE in Abbildung 18.14 dargestellt.

Abbildung 18.14: Durchführung von Änderungen über die Ergebnismenge
Abbildung 18.14

Folgendes Beispiel zeigt an einem Quellcode-Ausschnitt, wie über die Methoden der Klasse ResultSet in der Datenbank eine UPDATE-Operation durchgeführt wird:

  Connection con;
  ...
  Statement stmt;
  stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
                             ResultSet.CONCUR_UPDATABLE);
  String sql = "SELECT tit_id, titel, preis FROM titel WHERE tit_id = 2";
  ResultSet rs = stmt.executeQuery(sql);
  // Positionierung auf den ersten Datensatz
  rs.next();
  // Durchführung der Änderung im internen Puffer
  rs.updateFloat(2, 68);
  // Übertragen des geä;nderten Datensatzes an das Datenbanksystem
  rs.updateRow();
  ...
Nach der Positionierung auf den zu ändernden Datensatz wird durch Aufruf von updateFloat() der Wert zunächst in einem internen Puffer aktualisiert. Das Interface ResultSet definiert verschiedene updateXXX()-Methoden für die Aktualisierung von Daten verschiedener Typen (genau wie bei den getXXX()-Methoden zur Abfrage von Daten). Durch Aufruf von updateRow() wird der intern zwischengespeicherte Datensatz in die Datenbank geschrieben. Durch Aufruf von cancelRowUpdates() können die Änderungen im internen Puffer wieder rückgängig gemacht werden.

Beim Einfügen von neuen Datensätzen muss man den Satzzeiger zunächst auf den Einfüge-Datensatz positionieren. Dies geschieht durch Aufruf von moveToInsertRow(). Danach werden die Daten mit den updateXXX()-Methoden gesetzt, die auch bei der Durchführung der UPDATE-Operation benutzt werden. Hierbei müssen alle Spalten mit Werten belegt werden, die in der Datenbank nicht NULL sein dürfen. Danach wird durch Ausführung von insertRow() die INSERT-Anweisung an das Datenbanksystem geschickt. Durch Aufruf von moveToCurrentRow() wird der Satzzeiger anschließend wieder auf den Datensatz positioniert, auf dem er sich vor dem Aufruf von moveToInsertRow() bereits befand.

Die Ausführung einer DELETE-Anweisung ist die Einfachste von allen drei DML-Operationen. Der Satzzeiger muss hierzu lediglich auf den zu löschenden Datensatz positioniert und anschließend die Methode deleteRow() aufgerufen werden:
  Connection con;
  ...
  Statement stmt;
  stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
                             ResultSet.CONCUR_UPDATABLE);
  String sql =
     "SELECT tit_id, titel, preis FROM titel WHERE tit_id = 2";
  ResultSet rs = stmt.executeQuery(sql);
  // Positionierung auf den ersten Datensatz
  rs.next();
  //Löschen der Zeile
  rs.deleteRow();
  ...
Nach dem Aufruf von deleteRow() wird bei Scroll-sensitiven Ergebnismengen derjenige Datensatz aktuell, der sich direkt vor dem gelöschten Datensatz befand.

Alle Änderungen werden letztendlich persistent in der Datenbank festgeschrieben, wenn die aktuelle Transaktion abgeschlossen wird (siehe Abschnitt 18.11).

Einschränkungen

Aktualisierbare und Scroll-sensitive Ergebnismengen können nicht mit allen SQL-Anweisungen erzeugt werden. Die Einschränkungen für die beiden Arten von Ergebnismengen sind in Tabelle 18.6 aufgeführt.

Tabelle 18.6: Einschränkungen beim Erzeugen von Scroll-sensitiven und positionierbaren Ergebnismengen.
EinschränkungaktualisierbarS.-sensitiv
JoinsXX
SELECT *XX
ORDER BYXX
GROUP BYXX
Selektion von NOT NULL Spaltennur bei INSERT 
abgeleitete SpaltenX 

Die Einschränkungen zur Aktualisierung entsprechen den Einschränkungen, die man auch bei aktualisierbaren Views vorfindet.

Wenn ein Statement-Objekt eine Ergebnismenge eines bestimmten Typs erzeugt und die übergebene SQL-Anweisung ein Konstrukt enthält, das dem gewünschten Typ widerspricht, generiert der Treiber eine SQL-Warnung und gibt ein ResultSet-Exemplar zurück, das dem angeforderten Typ möglichst gut entspricht. Von welchem Typ die zurückgelieferte Ergebnismenge tatsächlich ist, kann man über die Methoden getType() bzw. getConcurrency() ermitteln. Die Methoden liefern die entsprechenden Konstanten zurück, die auch beim Erzeugen benutzt werden.

Sichtbarkeit von Änderungen

Während Daten in der Ergebnismenge abgerufen werden, können Veränderungen an den zugrunde liegenden Daten durchgeführt werden. Man unterscheidet hierbei zwei Arten von Änderungen: Abhängig vom Typ der Ergebnismenge sind die Veränderungen automatisch sichtbar oder auch nicht. Sichtbar bedeutet in diesem Zusammenhang, dass veränderte Daten während der Verarbeitung der Ergebnismenge über die getXXX()-Methoden abgerufen werden können.

Prinzipiell gilt, dass für die Sichtbarkeit externer Änderungen die Ergebnismenge scrollsensitiv und für die Sichtbarkeit interner Änderungen aktualisierbar sein muss, da sonst überhaupt keine Aktualisierung der Ergebnismenge durchgeführt werden kann. In Abbildung 18.15 ist die Sichtbarkeit von DML-Operationen in den verschiedenen ResultSet-Varianten dargestellt.

Abbildung 18.15: Sichtbarkeit von Änderungen
Abbildung 18.15

Daran kann man sehen:

Wenn ein JDBC-Treiber intern Zeilen zwischenpuffert, werden externe Änderungen in der Regel erst dann sichtbar, wenn die Zeilen neu vom Server angefordert werden. Daher wirken sich externe Änderungen unter Umständen nicht sofort auf die Ergebnismenge aus. Durch Änderung der Abfragegröße kann man die Anzahl der zwischengepufferten Zeilen beeinflussen (siehe Abschnitt 18.10.1).

Alternativ kann man eine Aktualisierung der Zeilen explizit durch Aufruf der Methode refreshRow() durchführen.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.