# Richtige Ergebnisprüfung?



## mad-din (1. Jun 2007)

Hi Leute!

Wie funktioniert eine richtige Ergebnisprüfung? Ich hab folgendes Problem, ich prüfe, ob bereits ein Eintrag in der DB ist. Wenn nicht, soll ein neuer gemacht werden, wenn ja, soll überprüft werden, welche Einträge in der DB sind und wenn der neue noch nicht drin ist, dann soll er rein, ansonsten soll nichts gemacht werden. Dafür hab ich ein PreparedStatement, das so aussieht:


```
PreparedStatement stmtCheck = con.prepareStatement("SELECT Nr, PDF_Name FROM temp_pdf WHERE Nr=?");
```

Das ResultSet sieht dann so aus: 


```
stmtCheck.setInt(1, nr);
ResultSet resCheck = stmtCheck.executeQuery();
```

So, zu Überprüfung benutze ich folgendes:


```
if (!resCheck.next()) {
    // kein Eintrag vorhanden, neuen speichern
} else {

    // ein oder mehrere einträge vorhanden, prüfe, ob aktueller dabei ist
    while (resCheck.next()) {
        // hier ist die eigentliche prüfung
    }

}
```

Bei welchem Datensatz befindet sich jetzt Java, wenn es in die while-Schleife springt? Angenommen es sind 5 Datensätze vorhanden, es ist es dann beim zweiten oder beim ersten? So wie es mir erscheint springt Java bereits bei der If-Prüfung weiter zum nächsten Datensatz, was natürlich fatal wäre, wenn das Ergebnis nur einen Datensatz enthält. Weil dann liefert resCheck.next() beim ersten Durchlauf der while-Schleife schon false und das neue Element wird eventuell gespeichert, obwohl es eigentlich schon drin ist.

Wie prüft man jetzt richtig, ob das Ergebnis etwas zurückgeliefert hat oder ob es einfach null zurückgegeben hat? Kann man vielleicht auch vor der while-Schleife den internen Zeiger wieder zurücksetzen?

Danke schonmal & viele Grüße,
Martin


----------



## Guest (1. Jun 2007)

Mache eine do-while Schleife daraus und dein Problem ist weg.
	
	
	
	





```
if (!resCheck.next())
{ 
    // kein Eintrag vorhanden, neuen speichern 
}
else
{ 

    // ein oder mehrere einträge vorhanden, prüfe, ob aktueller dabei ist 
    do
    { 
        // hier ist die eigentliche prüfung 
    } 
    while (resCheck.next());

}
```


----------



## Tobias (1. Jun 2007)

Ich benutze das hier, um zu prüfen, ob überhaupt Zeilen im Set sind:

http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html#first()

mpG
Tobias


----------



## mad-din (2. Jun 2007)

Hm,


das scheinen mir aber alles keine sauberen Lösungen zu sein. Gibts denn wirklich keine Funktion, die einfach 'nen boolschen Wert zurückliefert, ob das Ergebnis Zeilen enthält oder nicht? Gibt ja auch SELECT-Abfragen, die null zurückliefern. Die beste Methode scheint mir momentan zu sein, einen Wrapper zu schreiben, der das Ergebnis in eine Liste (ArrayList, Vector, was auch immer) schreibt und diesen zurückgibt. Je nachdem, ob die Liste jetzt null ist oder nicht, hat das Ergebnis etwas zurückgeliefert oder eben auch nicht.
Aber das wäre in meinem Fall mit Kanonen auf Spatzen geschosen, da mein Java Programm nur eine einfache Konsolen-Anwendung ist, die Daten vom Filesystem einliest und in die DB speichern muss.

Danke trotzdem!
Viele Grüße,
Martin

P.S.: Ich hab jetzt einfach die if-Bedingung weggelassen, dadurch ging es auch. Ich hab jetzt nur keine Ausgabe mehr, ob es ein neuer Eintrag ist oder ein weiterer, was aber nicht so tragisch ist.


----------



## semi (2. Jun 2007)

mad-din hat gesagt.:
			
		

> ...das scheinen mir aber alles keine sauberen Lösungen zu sein.


Trenne einfach die drei Operationen Create, Read und Update und versuche nicht diese auf einen Schlag, in einer Methode, zu erledigen.


----------



## mad-din (3. Jun 2007)

semi hat gesagt.:
			
		

> mad-din hat gesagt.:
> 
> 
> 
> ...



Und das soll eine saubere Lösung einer Ergebnisprüfung sein? Das ist vielleicht in Hinblick auf Kapselung sauber und normalerweise mach ich das auch. Aber in diesem Fall hab ich nur eine Methode, die überprüft, ob schon ein Eintrag vorhanden ist. Und wenn ich diese Überprüfung in eine weitere Methode auslagere, bleibt immernoch die Frage, wie überprüfe ich ein ResultSet-Objekts eines SELECTs sauber und richtig, ob das Ergebnis einfach nur "null" zurückgeliefert hat oder nicht (sofern die SQL-Abfrage nicht stimmt oder etwas anderes wird ja eine SQLException geworfen, die abgefangen wird, was aber nicht der Fall ist wenn ein "SELECT Spalte1 FROM tabelle WHERE spalte2=Wert" einfach NULL zurückliefert, weil es keinen Datensatz gibt, der diese Bedingung erfüllt).
Desweiteren hab ich überhaupt keine CREATE in meiner Anwendung.

Viele Grüße,
Martin

P.S.: Du kannst mir aber gerne genauer erklären, was du meinst, vielleicht erkenne ich den Sinn nur im Moment nicht. Aufs wesentliche will ich aber nur sauber prüfen, ob mein ResultSet null zurückliefert oder nicht.


----------



## semi (3. Jun 2007)

mad-din hat gesagt.:
			
		

> ...sofern die SQL-Abfrage nicht stimmt oder etwas anderes wird ja eine SQLException geworfen, die abgefangen wird, was aber nicht der Fall ist wenn ein "SELECT Spalte1 FROM tabelle WHERE spalte2=Wert" *einfach NULL zurückliefert, weil es keinen Datensatz gibt, der diese Bedingung erfüllt*).


So einen Fall wird es nie geben bzw. wenn ein Datensatz die Abfragebedingungen nicht erfüllt, taucht es 
gar nicht erst in der Ergebnismenge auf.


			
				mad-din hat gesagt.:
			
		

> Desweiteren hab ich überhaupt keine CREATE in meiner Anwendung.


Du schreibst doch selbst, dass wenn ein Datensatz nicht existiert, soll es angelegt werden.

Per Default ist ein ResultSet vom Typ ResultSet.TYPE_FORWARD_ONLY, d.h. du kannst nur von Anfang bis Ende 
iterieren. Die Methoden first(), last(), relative(int) etc. stehen dabei nicht zur Verfügung. Möchtest du eine andere 
Variante (TYPE_SCROLL_INSENSITIVE oder TYPE_SCROLL_SENSITIVE), musst du dies beim Erstellen des Statements 
angeben (siehe hierzu die anderen prepareStatement-Methoden in Connection).

Was die Prüfung angeht, ob eine Abfrage etwas liefert, da hast du nicht viele Möglichkeiten bei TYPE_FORWARD_ONLY.


```
ResultSet resultSet = statement.executeQuery();
if(resultSet.next())
{
   // Datensatz gefunden
}
else
{
   // Datensatz nicht gefunden
}
```


----------



## mad-din (3. Jun 2007)

semi hat gesagt.:
			
		

> So einen Fall wird es nie geben bzw. wenn ein Datensatz die Abfragebedingungen nicht erfüllt, taucht es
> gar nicht erst in der Ergebnismenge auf.



Genau, aber das Objekt ResultSet ist nicht null, was es nach meinem Verständnis eigentlich sein sollte. Denn wenn kein Ergebnis vorhanden ist, das auf den SQL-Query zutrifft (es trat ja kein Fehler auf, es wurde nur einfach nichts gefunden), dann sollte es auch kein ResultSet-Objekt geben und wenn doch, dann sollte man über dieses Objekt zumindest herausfinden können, ob das SQL-Statement X Zeilen zurückliefert oder eben 0 Zeilen. 



			
				semi hat gesagt.:
			
		

> Du schreibst doch selbst, dass wenn ein Datensatz nicht existiert, soll es angelegt werden.



Ja, ein Datensatz und keine Tabelle, es kommt ein INSERT zustande, kein CREATE, aber das ist nicht mein Problem. 

Mein Problem ist, dass es IMHO keine saubere Lösung gibt mit JDBC zu prüfen, ob ein simpler SELECT "NULL" (oder 0 Zeilen) zurückliefert oder nicht. Wie prüft man zum Beispeil, wenn man ein Select auf eine leere Tabelle macht? Meiner Meinung nach, sollte in diesem Fall das ResultSet Objekt - wie oben bereits erwähnt - null sein, da ja NICHTS gefunden werden kann und falls doch, muss es doch eine saubere Lösung geben, abzufragen, ob 0 Zeilen zurückgeliefert wurden.

Viele Grüße,
Martin


----------



## semi (3. Jun 2007)

Ein ResultSet ist ein Cursor. Das Abfrageergebnis ist ein offener Cursor. ResultSet ist nicht eine Collection mit 
den gelesenen Zeilen, sondern ein Wrapper für den Zugriff auf die Ergebnismenge. 
Nach der Ausführung eines Statements steht noch gar nicht fest, ob das Ergebnis eine leere Menge ist oder 
nicht (zumindest nicht Javaseitig).

Das hier
	
	
	
	





```
ResultSet resultSet = statement.executeQuery();
```
liefert nur eine Art Iterator für die Daten, aber noch keine Daten selbst.

Eine leere Menge ist auch ein Ergebnis.

Und mit CREATE meinte ich INSERT. CRUD (Create, Read, Update und Delete). Die vier grundlegenden 
Operationen beim Zugriff auf Daten.


----------



## mad-din (3. Jun 2007)

Ok, 

das bestätigt meine Meinung, dass es in Java keine vernünftige und saubere Lösung gibt, um zu überprüfen, ob ein SELECT eine leere Ergebnissmenge zurückgeliefert hat oder nicht. Oder wie prüfst du z.b. einen Select auf eine leere Tabelle?

Viele Grüße,
Martin


----------



## semi (3. Jun 2007)

Ich kann dein Problem nicht richtig nachvollziehen. Das ist, wie du ja selbst weiss, mit paar Zeilen erledigt.
	
	
	
	





```
ResultSet resultSet = statement.executeQuery();
if(resultSet.next())
{
    // Es ist mindestens eine Zeile drin
   do
   {
      // irgendwas mit der aktuellen Zeile anstellen
   }
   while(resultSet.next());
}
else
{
   // nix drin
}
```
Wenn dir JDBC zu bunt ist, dann verwende einen OR-Mapper wie Hibernate, Toplink bzw. JPA allgemein.


----------



## bronks (4. Jun 2007)

mad-din hat gesagt.:
			
		

> ... das bestätigt meine Meinung, dass es in Java keine vernünftige und saubere Lösung gibt, um zu überprüfen, ob ein SELECT eine leere Ergebnissmenge zurückgeliefert hat oder nicht. Oder wie prüfst du z.b. einen Select auf eine leere Tabelle?


Java ist nicht einfach nur eine andere Sprache, in der man seine Gedanken so umsetzt, wie man es aus PHP oder VB  gewöhnt ist. Bei korrekter Anwendung von  OO und entsprechenden Patterns würdest Du Deine Frage nicht stellen müssen.

Warum prüfst Du überhaupt ob ein Satz bereits vorhanden ist und machst erst danach das Insert?


----------



## mad-din (4. Jun 2007)

bronks hat gesagt.:
			
		

> Java ist nicht einfach nur eine andere Sprache, in der man seine Gedanken so umsetzt, wie man es aus PHP oder VB  gewöhnt ist. Bei korrekter Anwendung von  OO und entsprechenden Patterns würdest Du Deine Frage nicht stellen müssen.



Gut, wie lauten diese Patterns? Ich bin mir durchaus bewusst über OO und entsprechende Patterns, aber es ist hier folgendes: ich hab eine simple Klasse, die einfach nur ein Verzeichnis rekursiv durchsuchen soll und die darin enthaltenen PDFs in eine Datenbank speichern soll (die Dateinamen, nicht die PDFs selber). Das ist keine große Anwendung und man muss sich nicht immer an alle Konzepte halten, weil sonst hätte ich für diese Klasse 3 Tage gebraucht. Wie gesagt, ich bin mir im Klaren darüber und bei einer größeren Anwendung würde ich mir einen DB-Wrapper schreiben, der z.B. eine HashTable o.Ä. als Ergebnis zurückliefert. Da kann ich dann zumindest anhand der Größe überprüfen, ob es Ergebnisse gibt oder nicht.

Ich glaube aber, dass ich hier falsch verstanden werde. Wie gesagt, mit JDBC bin ich noch nicht per Du. Ich finde es einfach komisch, dass in den Treibern bzw. in JDBC keinerlei Methoden oder Variablen hinterlegt sind, die mir sagen, wieviel Zeilen ein Query gebracht hat. Das ist doch IMHO einer der grundlegenden Sachen, die eine Datenbankschnittstelle mitbringen sollte (zumindest ist es in C#, Perl und C++ so). Vielleicht suche ich auch ander falschen Stelle und solche Sachen stehen nicht in ResultSet, sondern vielleicht in ResultSetMetaData, wo ich die betreffenden Sachen aber auch nicht gefunden habe. 



			
				bronks hat gesagt.:
			
		

> Warum prüfst Du überhaupt ob ein Satz bereits vorhanden ist und machst erst danach das Insert?



Das hast du wohl falsch verstanden, ich prüfe, ob genau dieser Datensatz schon vorhanden ist. Wenn nicht, wird er gespeichert, wenn schon vorhanden, wir er nicht gespeichert. Ich will ja nicht, dass bei jedem Lauf sämtliche PDFs nochmal drin stehen. Sonst würde die Tabelle täglich um 12.000 Einträge steigen und das wäre zuviel Redundanz, die in diesem Fall nicht benötigt wird. (Das Problem ist, es gibt eine Kennummer, zu dieser Kennnummer kann es x PDFs geben, jetzt muss ich natürlich prüfen, ob für diese Kennummer dieses PDF schon vorhanden ist).


----------



## bronks (4. Jun 2007)

mad-din hat gesagt.:
			
		

> ... Das hast du wohl falsch verstanden, ich prüfe, ob genau dieser Datensatz schon vorhanden ist. Wenn nicht, wird er gespeichert, wenn schon vorhanden, wir er nicht gespeichert. Ich will ja nicht, dass bei jedem Lauf sämtliche PDFs nochmal drin stehen. Sonst würde die Tabelle täglich um 12.000 Einträge steigen und das wäre zuviel Redundanz, die in diesem Fall nicht benötigt wird. (Das Problem ist, es gibt eine Kennummer, zu dieser Kennnummer kann es x PDFs geben, jetzt muss ich natürlich prüfen, ob für diese Kennummer dieses PDF schon vorhanden ist).


Ich weiß nicht, ob da noch ein paar andere Hindernisse im Weg stehen, aber so wie ich das lese, würde ich den PK der Tabelle anpassen und den Execute in die Fehlerbehandlung reinlaufen lassen, wenn das PDF bereits vorhanden ist.

Sonst wäre ich für meine Zwecke immer bereit entsprechende DAOs auszuprogrammieren und dann die zurückgegebenen Collections auf vorhandenen Inhalt zu prüfen.


----------



## mad-din (4. Jun 2007)

bronks hat gesagt.:
			
		

> Ich weiß nicht, ob da noch ein paar andere Hindernisse im Weg stehen, aber so wie ich das lese, würde ich den PK der Tabelle anpassen und den Execute in die Fehlerbehandlung reinlaufen lassen, wenn das PDF bereits vorhanden ist.



Doch, stehen, das ja mein Problem. Ein PDF kann auch mehreren Kennummern zugeordnet werden. D.h. weder die Spalte mit der Kennnummer kann der PK sein (da es mehrere PDFs für eine Kennummer gibt) noch die Spalte PDF kann es sein, da ja jedes PDF auch zwei oder mehrmals vorkommt.



			
				bronks hat gesagt.:
			
		

> Sonst wäre ich für meine Zwecke immer bereit entsprechende DAOs auszuprogrammieren und dann die zurückgegebenen Collections auf vorhandenen Inhalt zu prüfen.



Ja, aber das wäre mir in meinem Fall schlicht und einfach zuviel Aufwand für mein Problem gewesen. 

Viele Grüße,
Martin


----------



## bronks (4. Jun 2007)

mad-din hat gesagt.:
			
		

> ... Doch, stehen, das ja mein Problem. Ein PDF kann auch mehreren Kennummern zugeordnet werden. D.h. weder die Spalte mit der Kennnummer kann der PK sein (da es mehrere PDFs für eine Kennummer gibt) noch die Spalte PDF kann es sein, da ja jedes PDF auch zwei oder mehrmals vorkommt.


Wäre es möglich den PK über beide Felder zu legen? Damit müsste doch das gröbste Problem beseitigt sein?


----------



## mad-din (6. Jun 2007)

Hi!

Ja so ähnlich wurde das Problem auch gelöst. Das ganze war ja nur eine quasi Quick-and-Dirty Anwendung, die mal eben schnell laufen sollte. Da aber im Zuge des aktuellen Projektes diese Prozedur immer wieder kehrte - was eigentlich nicht geplant war, aber sich als sehr nützlich erwies -  wurde es jetzt neu überdacht und neu geschrieben.

Aber um auf meine eigentliche Frage zurückzukommen: eine saubere Lösung auf leere Ergebnisse gibt es nicht, die muss ich selbst implementieren, richtig? Wäre es dann sinnvoller einen allgemeinen DB-Wrapper zu schreiben oder für jede Anwendung einen eigenen? Wo würden da die Vor- und Nachteile eurer Meinung nach liegen?

Viele Grüße,
Martin


----------

