# Probleme mit java.sql.SQLException: ResultSet closed



## Burned (18. Apr 2016)

Ich versuche aus einer SQLite Tabelle eine zufällige Spalte auszulesen. Dann will ich testen, ob diese Spalte leer ist. Falls das der Fall ist, will ich in Sie einen Wert eintragen. Allerdings wirft mir eclipse die ganze Zeit die java.sql.SQLException. Weiß jemand von euch, woran es liegen könnte?
 Hier der Code:

```
try {
            Statement stmt = DBController.connection.createStatement();
            String updateTableSQL = "UPDATE individuen set Geschlecht  = ? where Geschlecht IS NULL";
            PreparedStatement ps = DBController.connection.prepareStatement(updateTableSQL);
            while (copyMaen != 0) {
                ResultSet rs = stmt.executeQuery("SELECT Geschlecht FROM individuen ORDER BY RANDOM() LIMIT 1;");
                if (rs.getInt("Geschlecht") == 0) {
                    ps.setInt(1, 1);
//                    ps.executeUpdate();
                    copyMaen--;
                    System.out.println(copyMaen);
                }
                ps.executeUpdate();
                rs.close();
            }
```


----------



## VfL_Freak (19. Apr 2016)

Moin,

also das SELCT-Statement sieht für mich auf den ersten Blick ok aus - wobei die spannende Frage bleibt, welches DBS Du verwendest (http://www.petefreitag.com/item/466.cfm) ...
Wo genau fliegt denn die Exception?
Evtl. schon beim Update ?
Welchen Typ hat 'Geschlecht' ?

Gruß Klaus


----------



## Thallius (19. Apr 2016)

Schaumal hier:

http://stackoverflow.com/questions/935511/how-can-i-avoid-resultset-is-closed-exception-in-java

Vor allem die zweite Antwort ist interessant.

Gruß

Claus


----------



## Burned (19. Apr 2016)

Hallo Klaus & Claus,

ich glaube ich war gestern Nacht dann doch zu frustriert und hab den Beitrag zu früh online gestellt. Ich benutze SQLite mit einem JDBC-Treiber nach diesem Muster hier:
http://javabeginners.de/Datenbanken/SQLite-Datenbank.php

Die Exception wurde mit in der if-Abfrage geworfen. Die Exception habe ich raus bekommen, indem ich im ResultSet alle Spalten abfrage anstatt nur "Geschlecht". Bin mir aber nicht sicher, ob das die richtige Lösung ist.
Jetzt habe ich auch noch weitere Probleme mit meinem PreparedStatement. Ich weiß nicht genau, wie ich die UPDATE-anfrage in SQL hier richtig stelle und mir wird da eine Exception geworfen. Aktueller Code:


```
try {
            Statement stmt = DBController.connection.createStatement();
//            String updateTableSQL = "UPDATE * set Geschlecht  = ? where Geschlecht IS NULL";
            PreparedStatement ps = DBController.connection.prepareStatement("UPDATE individuen SET Geschlecht;");
            while (copyMaen != 0) {
                ResultSet rs = stmt.executeQuery("SELECT * FROM individuen ORDER BY RANDOM() LIMIT 1;");
                if (rs.getInt("Geschlecht") == 1) {
                    ps.setInt(1, 0);
//                    ps.executeUpdate();
                    copyMaen--;
                    System.out.println(copyMaen);
                }
                ps.executeUpdate();
                rs.close();
            }        
        } catch (SQLException e) {
            e.printStackTrace();
        }
```

Weiß jemand von euch weiter?
David


----------



## Joose (19. Apr 2016)

Dein PreparedStatement ist falsch. Du sagst zwar es soll die Tabelle "individuen" geändert werden und dort ein neues Geschlecht gesetzt werden, aber es fehlt eine Angabe welcher Wert gesetzt werden soll (Parameter) und welche Zeilen davon betroffen sein sollen (WHERE Bedingung).

Dazu ein paar Grundlagen: http://www.w3schools.com/sql/sql_update.asp


----------



## Burned (19. Apr 2016)

Ah, jetzt hab ichs. Vielen Dank für die Hilfe! So funktionierts:


```
try {
            Statement stmt = DBController.connection.createStatement();
//            String updateTableSQL = "UPDATE * set Geschlecht  = ? where Geschlecht IS NULL";
            PreparedStatement ps = DBController.connection.prepareStatement("UPDATE individuen SET Geschlecht = ? WHERE Geschlecht IS NULL;");
            while (copyMaen != 0) {
                ResultSet rs = stmt.executeQuery("SELECT * FROM individuen ORDER BY RANDOM() LIMIT 1;");
                if (rs.getInt("Geschlecht") == 0) {
                    ps.setInt(1, 1);
//                    ps.executeUpdate();
                    copyMaen--;
                    System.out.println(copyMaen);
                }
                ps.executeBatch();
                rs.close();
            }    
        } catch (SQLException e) {
            e.printStackTrace();
        }
```

Aber die Lösung ist recht langsam. Es müssen 50.000 Einträge gemacht werden. Kennt ihr vielleicht eine effizientere Lösung und könnt mich in die richtige Richtung schicken?


----------



## Joose (19. Apr 2016)

Was willst du denn genau erreichen? Jeden Datensatz in "individuen" ein Geschlecht zuweisen wenn keines vorhanden ist?

So wie dein Code aktuell ist macht dein PreparedStatement nicht viel Sinn, vor allem nicht das du es X mal ausführst, obwohl nach der 1.Ausführung nichts mehr passiert.
Laut Prepared Statement soll das Geschlecht gesetzt werden und zwar bei allen Datensätzen wo es bisher NULL ist. Nachdem du das PreparedStatement 1x ausgeführt hast gibt es keine Datensätze mehr wo Geschlecht nicht gesetzt ist.
Außerdem ist dein SELECT auch nicht das gelbe vom Ei, du wählst einfach alle Spalten von allen Datensätzen aus, beliebig sortiert und willst dann nur die 1.Zeile.

Wie schon geschrieben ergibt der Code so nicht wirklich viel Sinn und du solltest dir genau überlegen was du erreichen willst!


----------



## Burned (19. Apr 2016)

Hallo Joose,

ich will erreichen, dass in einer festgesetzten Anzahl von Spalten in denen Geschlecht bis jetzt NULL istcht auf 1 gesetzt wird. Diese Spalten sollen jedoch zufällig ausgewählt werden, da bei dem aktuellen Stand der DB alle Spalten in Geschlecht NULL sind.


----------



## Joose (19. Apr 2016)

Kann es sein das du Spalte mit Zeile/Datensatz verwechselst? Eine Tabelle hat für mich X Spalten und ich kann immer neue Datensätze/Datenzeilen eintragen.
(In deinem Fall wäre Geschlecht eine Spalte der Tabelle individuen)

1.) Du solltest dein PreparedStatement noch anpassen: Und zwar solltest du dort noch eine WHERE Bedingung hinzufügen welche dir nur deine zufällig gewählte Zeile ändert.
Damit wird einfach der Fehler behoben das mit der 1.Durchführung alle Datensätze geändert werden

2.) Du selektierst immer eine zufällige Zeile: Hier könntest du ebenfalls eine WHERE Bedingung hinzufügen welche dir direkt alle rausfiltert wo Geschlecht schon gesetzt ist. Außerdem könntest du für diese SELECT ebenfalls ein PreparedStatement verwenden und LIMIT per Parameter auf "copyMaen" festlegen (da ich annehme dass das die festgesetzte Anzahl ist)
Damit verbesserst du deine Performance, da die DB weniger Datensätze selektiert und sortiert. Des weiteren ersparst du dir die Ausführung von einigen SELECTs, und in Java musst du nicht kontrollieren ob Geschlecht schon gesetzt ist oder nicht.


----------



## Thallius (19. Apr 2016)

Du must dann in der Tabelle eine unique id haben anhand der du eine Zeile einwandfrei erkannt werden kann. In dem Update sagst du dann WHERE id = ...

Gruß

Claus


----------



## VfL_Freak (19. Apr 2016)

Burned hat gesagt.:


> Hallo Klaus & Claus,


ich hoffe, wir beiden müssen jetzt nicht auch noch singen ...


----------



## Burned (19. Apr 2016)

Ja, tut mir leid, ich meinte natürlich Zeile!
Deine Antwort verstehe ich nicht so ganz Joose.. Ich habe versucht das ganze umzusetzen, hänge aber immer noch an an dem Problem, dass das ganze zu langsam ist.


```
try{
        while(copyMaen !=0){
            int rng =(int)(Math.random()*ReadCSV.sumBev);
            String updateTableSQL ="UPDATE individuen set Geschlecht  = ? where Geschlecht IS NULL AND ID = "+Integer.toString(rng);
            PreparedStatement preparedStatement =DBController.connection.prepareStatement(updateTableSQL);
            preparedStatement.setString(1,"0");
            //execute update SQL statement
            preparedStatement.executeUpdate();
            copyMaen--;
            System.out.println(copyMaen);
        }
    }catch(SQLException e){
        e.printStackTrace();}
```


----------



## Thallius (19. Apr 2016)

Zeig uns mal wie deine Tabellenstruktur aussieht


----------



## Joose (19. Apr 2016)

Burned hat gesagt.:


> Deine Antwort verstehe ich nicht so ganz Joose..


Was genau ist unverständlich? Dann versuche ich es vielleicht anders zu erklären.



Burned hat gesagt.:


> Ich habe versucht das ganze umzusetzen, hänge aber immer noch an an dem Problem, dass das ganze zu langsam ist.


Was heißt bei dir zu langsam? Du hast was von 50000 Zeilen geredet, aber keine Zeit genannt.



Burned hat gesagt.:


> ```
> String updateTableSQL ="UPDATE individuen set Geschlecht  = ? where Geschlecht IS NULL AND ID = "+Integer.toString(rng);
> ```


Du führst hier `copyMaen` UPDATE Statements aus.
Theoretisch könntest du alles in einem machen: Dazu müsstest du die WHERE Bedingung anpassen: WHERE ID in (...)
http://www.tutorialspoint.com/sqlite/sqlite_where_clause.htm

Warum ist der SELECT plötzlich weg? Die ID wird jetzt einfach per Zufall berechnet. Wenn du Pech hast werden zufällig 10 IDs generiert deren Geschlecht schon gesetzt ist, dann ändert sich gar nichts.


----------



## Burned (19. Apr 2016)

An der Stelle hier wollte ich euch schon mal für eure ganze Hilfe Danken! Auch wenn ich noch keine perfekte Lösung habe, habe ich zumindest das Gefühl das Problem besser zu verstehen.  



Thallius hat gesagt.:


> Zeig uns mal wie deine Tabellenstruktur aussieht


Ein Bild der Tabelle ist im Anhang. So sieht sie aus, wenn ich die geschlechter eintragen will.



Joose hat gesagt.:


> Was heißt bei dir zu langsam? Du hast was von 50000 Zeilen geredet, aber keine Zeit genannt.


Ich habe das Programm nicht ganz durchlaufen lassen, aber meine Schätzung sind 25-30 Minuten.



Joose hat gesagt.:


> Theoretisch könntest du alles in einem machen: Dazu müsstest du die WHERE Bedingung anpassen: WHERE ID in (...)


Muss ich dabei dann nicht genau die Zeilen kennen, bei denen ich Geschlecht ändern will? So wie ich den Befehl verstanden habe, könnte ich schreiben: 

```
"UPDATE individuen set Geschlecht  = ? WHERE Geschlecht IS NULL AND ID IN (200, 300)"
```
Aber dann würde ich ja keine Zeilen zufällig auswählen und auch nur in exakt diese Zeilen schreiben. Oder habe ich WEHRE IN () falsch verstanden?



Joose hat gesagt.:


> Die ID wird jetzt einfach per Zufall berechnet. Wenn du Pech hast werden zufällig 10 IDs generiert deren Geschlecht schon gesetzt ist, dann ändert sich gar nichts.


Das sollte doch durch den "WHERE Geschlecht IS NULL" Teil aus dem PreparedStatement verhindert werden. Oder vertue ich mich da auch?


----------



## Joose (19. Apr 2016)

Burned hat gesagt.:


> Ich habe das Programm nicht ganz durchlaufen lassen, aber meine Schätzung sind 25-30 Minuten.


Für 50.000 Zeilen ist das sehr sehr lange.



Burned hat gesagt.:


> Muss ich dabei dann nicht genau die Zeilen kennen, bei denen ich Geschlecht ändern will? So wie ich den Befehl verstanden habe, könnte ich schreiben:
> 
> ```
> "UPDATE individuen set Geschlecht  = ? WHERE Geschlecht IS NULL AND ID IN (200, 300)"
> ...


Genau man muss diese schon wissen. Natürlich kannst du sie dann zufällig wählen, so zufällig wie du es jetzt schon machst.
Du berechnest ja bei jedem Schleifendurchlauf eine Zufallszahl mit `int rng =(int)(Math.random()*ReadCSV.sumBev);`, wenn du das einfach 10x machst, hast du ja deine 10 zufälligen IDs für die Bedingung.



Burned hat gesagt.:


> Das sollte doch durch den "WHERE Geschlecht IS NULL" Teil aus dem PreparedStatement verhindert werden. Oder vertue ich mich da auch?


Das Statement sagt aus das der Wert von Geschlecht gesetzt werden soll bei den Datensätzen wo Geschlecht NULL ist und die ID = die von dir angegebene. Wenn natürlich kein Datensatz gefunden wird (weil ID nicht vorhanden oder Geschlecht schon gesetzt ist) dann passiert eben nichts.
Wie oben schon gesagt: Die IDs lässt du dir wirklich nur per Zufall geben, aber du kannst natürlich auch eine erwischen wo Geschlecht schon gesetzt ist.

Dein Ansatz aus dem 1.Post war schon richtig mit dem zusätzlichen SELECT.

```
SELECT ID FROM individuen WHERE Geschlecht IS NULL ORDER BY RANDOM() LIMIT ?;
```
Mit diesem SELECT kannst du dir X (random) Datensätze rausholen bei denen Geschlecht nicht gesetzt ist.

Danach musst du nur noch für jede der selektierten IDs dein UPDATE Statement ausführen.


----------



## Burned (19. Apr 2016)

Joose hat gesagt.:


> Dein Ansatz aus dem 1.Post war schon richtig mit dem zusätzlichen SELECT.
> Mit diesem SELECT kannst du dir X (random) Datensätze rausholen bei denen Geschlecht nicht gesetzt ist.
> 
> Danach musst du nur noch für jede der selektierten IDs dein UPDATE Statement ausführen.



Hum, könnte ich mir dann nicht einfach mit dem SELECT direkt meine Anzahl an benötigten Zeilen rausholen und in all diesen Zeilen meinen Wert eintragen? Dann bräuchte ich keine Schleife und hätte auch kein Problem mit doppeltem beschreiben.


----------



## Thallius (19. Apr 2016)

Du brauchst auch so keine Schleife wenn du einfach

UPDATE individual SET Geschlecht = 'ka' WHERE ID IN ()

benutzt


----------



## Joose (19. Apr 2016)

Die 1 Statement Lösung von Thallius ist natürlich optimal 
Wenn ichs richig verstanden haben wäre das fehlende Stichwort dazu noch "subselect" im IN.



Burned hat gesagt.:


> Hum, könnte ich mir dann nicht einfach mit dem SELECT direkt meine Anzahl an benötigten Zeilen rausholen und in all diesen Zeilen meinen Wert eintragen? Dann bräuchte ich keine Schleife und hätte auch kein Problem mit doppeltem beschreiben.


Ja du kannst dir deine benötigte Anzahl an Zeilen holen, aber wie das Keyword schon sagt es handelt sich um ein SELECT. Sprich es werden nur Daten selektiert und du kannst sie dann nicht einfach in Java ändern, sondern musst eben ein entsprechende UPDATE Statement ausführen.


----------



## Burned (20. Apr 2016)

Hallo, ich versuche weiter mein Glück 



Joose hat gesagt.:


> Du solltest dein PreparedStatement noch anpassen: Und zwar solltest du dort noch eine WHERE Bedingung hinzufügen welche dir nur deine zufällig gewählte Zeile ändert.


Ich glaube das ist der Teil, an dem ich scheiter. Ich schaffe es einfach nicht so ein prepared Statement zu schreiben. Ich schaffe es einfach nicht mein Prepared Statement mit meinem ResultSet, also dem Select zu verbinden. Stattdessen werden mir alle Zeilen geändert.


```
try {
             Statement stmt = DBController.connection.createStatement();
             String select = "SELECT Geschlecht FROM individuen WHERE Geschlecht is NULL ORDER BY RANDOM()"
                     + " LIMIT " + Integer.toString(copyMaen);
             ResultSet rs =stmt.executeQuery(select);
             PreparedStatement ps = DBController.connection.prepareStatement("UPDATE individuen set Geschlecht  = ?;");
//             rs.beforeFirst();
             while (rs.next()) {
                 ps.setInt(1, 0);
//                 rs.updateRow();
             }
             ps.executeUpdate();
    } catch (SQLException e) {
       e.printStackTrace();
     }
```


----------



## Joose (20. Apr 2016)

Burned hat gesagt.:


> Ich schaffe es einfach nicht mein Prepared Statement mit meinem ResultSet, also dem Select zu verbinden. Stattdessen werden mir alle Zeilen geändert.



Du musst doch dem UPDATE Statement nur eine WHERE Bedingung hinzufügen, damit nur des Geschlecht einer bestimmten ID (der 2.Parameter dann im Statement) geändert wird.


```
while (rs.next()) {
                 ps.setInt(1, 0);
...
             }
             ps.executeUpdate();
```
Hier hast du einen Denkfehler! Du führst das UPDATE Statement 1x nach der Schleife aus. Es sollte aber in der Schleife ausgeführt werden, da du für jeden selektierten Datensatz den Parameter für die WHERE Bedingung setzen musst um dann jeden der selektierten Datensätze zu ändern.
Natürlich musst du in der Schleife noch die ID von der aktuellen Zeile im ResultSet nehmen und als Parameter deines UPDATE Statements setzen.


----------



## Burned (20. Apr 2016)

Ich glaube jetzt hab ichs. Sogar so, dass es schnell durchläuft (~ 1 Sekunde). Oder sieht noch jemand Probleme an meiner Lösung?

```
try {
            Statement stmt = DBController.connection.createStatement();
            String select = "SELECT ID FROM individuen WHERE Geschlecht is NULL ORDER BY RANDOM()" +
            " LIMIT " + Integer.toString(copyMaen);
            ResultSet rs = stmt.executeQuery(select);
            PreparedStatement ps = DBController.connection.prepareStatement("UPDATE individuen set Geschlecht  = ? WHERE ID = ?;");
            // rs.beforeFirst();
            int count = 0;
            while (rs.next()) {
                ps.setInt(1, 0);
                ps.setInt(2, rs.getInt(1));
                ps.addBatch();
                if (count%100==0) {
                    System.out.println(count);
                }
                count++;
            }
            DBController.connection.setAutoCommit(false);
            ps.executeBatch();
            DBController.connection.setAutoCommit(true);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
```


----------

