# MsAccess immer beim zweiten Update java.sql.SQLException



## Kaffeebohne (24. Apr 2006)

Hallo,

ich versuche Daten in eine MSAccess mdb zu schreiben.

Zuerst hole ich alle Daten, dann verarbeite ich diese in einer Schleife in der ich das Ergebnis wieder reinschreibe.


Jedesmal beim 2. Update bekomme ich eine Exception

```
java.sql.SQLException: General error
        at sun.jdbc.odbc.JdbcOdbc.throwGenericSQLException(JdbcOdbc.java:7087)
        at sun.jdbc.odbc.JdbcOdbc.SQLAllocStmt(JdbcOdbc.java:174)
        at sun.jdbc.odbc.JdbcOdbcConnection.createStatement(JdbcOdbcConnection.java:413)
        at sun.jdbc.odbc.JdbcOdbcConnection.createStatement(JdbcOdbcConnection.java:396)
        at database.MSAccess.getStatement(MSAccess.java:73)
```

Ich weiss nicht warum, weil ich auch darauf achte nur eine Instantz zu öffen -> Singleton


```
public class MSAccess {
    
    protected static DatabaseDSN dsn = null;
    protected static Connection con = null;
    protected static MSAccess instance = null;


    
    /** Creates a new instance of MSAccess */
    protected MSAccess() {
    
    }

    public static void setDSN(DatabaseDSN d) {
        dsn = d;
    }
    
    protected void connect() throws SQLException {
        Messenger.send("Connecting to database: " + dsn.url);
        try{
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        } catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        con = DriverManager.getConnection("jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=" + dsn.url, dsn.user, dsn.password);
    }
    
    public Connection getConnection() throws SQLException {
        checkConnnection();
        return con;
    }
    
    public void close() throws SQLException {
        Messenger.send("Trying to close the connection with database: " + dsn.url);
        if(con != null) con.close();
    }
    
    protected void checkConnnection() throws SQLException {
         if(con == null) {
            System.out.println("MSAccess connection was null");
            connect();
        } else {
            
            System.out.println("MSAccess connection IS OKAY");
        }
    }
    
    
    public static MSAccess getInstance() {
        if(instance == null) instance = new MSAccess();
        return instance;
    }
    
    

    
    public Statement getStatement() throws SQLException {
        checkConnnection();
        return con.createStatement();
    }
    

    public Statement getStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        checkConnnection();
        return con.createStatement(resultSetType, resultSetConcurrency);
    }
}
```


----------



## SamHotte (24. Apr 2006)

Der 'general error' wird bei mir meistens dann von Access geschmissen, wenn es den Primärschlüssel schon gibt. Fügst du vielleicht zweimal dasselbe ein?


----------



## Kaffeebohne (24. Apr 2006)

Leider nein - es knallt schon vorher. 

In dem Moment wo ich (msAccessObj ist ein Objekt von MSAccess): 
msAccesObj.getStatement 

aufrufe wirft er die Exception.

PS. Es ist ein update das ich ausführe, aber wie gesagt es kommt gar nicht so weit


----------



## SamHotte (24. Apr 2006)

Hmm, ohne die Code-Zeile 73 (in der der Error geschmissen wird) ist es schwierig - poste bitte mal den Code, wo du die DB initialisierst.


----------



## Kaffeebohne (24. Apr 2006)

Die Zeile 73 ist in der Klasse MSAccess ist hier im Code die Zeile 60.


Der Teil des Codes der das aufruft ist:


```
class ShipmentDAO {
MSAccess con = null;
    public ShipmentDAO() {
        con = MSAccess.getInstance();
    }
.....
   public void update(Shipment shipment) throws SQLException {
        String sql = 
                "UPDATE data.... sql Zeugs;
        System.out.println(sql);
       
        Statement state = con.getStatement();
        state.executeUpdate(sql);
        state.close();
    }

}
```

Die Exception wird hier in Zeile 13 verursacht.


----------



## SamHotte (25. Apr 2006)

Irgendwie kommst du da mit den Typen durcheinander, glaube ich. In deinem letzten Codefragment (class ShipmentDAO) müsste con vom Typ MSAccess sein, wenn du es über '.getInstance()' instantiierst - für das Statement brauchst du aber ein Connection-Objekt. Probier doch mal, ob es so geht:

```
MSAccess db = MSAccess.getInstance();
Connection con = db.getConnection();
Statement st = con.getStatement();
...
```


----------



## Kaffeebohne (25. Apr 2006)

Hmm,

ich glaube ich habe den Fehler entdeckt:

Er ist in der ShipmentDAO - da habe ich noch eine Methode:


```
protected void finalize() throws Throwable {
        con.close();
    }
```

Seit ich die Rausgenommen habe gehts.

Nur ich habe einen anderen komischen Seiteneffekt.

Es fehlt jetzt immer der letzte SQL-Befehl, obwohl er gesendet wird.

Als Test habe ich eine Tabelle erstellt mit ID PrimaryKey Autoincrement und eine Spalte foo Text


```
String url = "c:\test.mdb";
        DatabaseDSN dsn = new DatabaseDSN();
        dsn.url = url;

       

        MSAccess ms = MSAccess.getInstance();
        ms.setDSN(dsn);
        try {
            Statement s = ms.getStatement();
            for(int i = 0; i < 10; ++i)
                s.execute("INSERT INTO testtab (foo) VALUES ('TEST')"); 
        }catch (SQLException se) {
            se.printStackTrace();
        }
```

Eingetragen werden aber nur neun. Auch bei Updates ist das so. 

Irgendjemand eine Idee?[/code]


----------



## Kaffeebohne (25. Apr 2006)

Morgen Sam,

ich habe gerade den Post geschrieben als du geantwortet hast *fg*


Hmm, als con ist doch bei mir vom Typ MSAccess - ist in der Klasse ShipmentDAO eine Membervariable. Werde deinen Vorschlag trotzdem gleich mal ausprobieren.


----------



## SamHotte (25. Apr 2006)

Moin 

Stimmt, hatte ich übersehen - war verwirrt, weil ich immer meine Connections mit 'con' bezeichne ...

Gib mal Bescheid, ob es funzt.


----------



## Kaffeebohne (25. Apr 2006)

Hmm, 

also ich hab gerade nochmal überlegt. Was anderes mache ich in meiner MSAccess Klasse auch nicht nur das ich "Connection.con.createStatement" mache und du "Connection.con.getStatement", geht aber nicht  da die Methode nicht vorhanden ist - denke aber du meinst createStatement


----------



## SamHotte (25. Apr 2006)

Jo, meinte ich. Du hattest doch so eine 'getStatement()'-Methode selbst in deinem Post von 9:50 Uhr  :bahnhof:


----------



## Kaffeebohne (25. Apr 2006)

Stimmt ich hole mir über ein MSAccess Objekt über die Methode getStatement ein Statement Objekt, dass aber in der Klasse MSAccess unter zuhilfennahme des Connection Objekts mit der Methode createStatement erzeugt wird.

Hoffe das ist jetzt nicht zu unverständlich   


Sam, könntest du eventuell eine Testdatenbank testtab in Access erzeugen mit ID, Primary Key, Autowert und einer Spalte foo, Text und testen ob bei dir auch der letzte SQL-Befehl nicht ausgeführt wird?

Ich kapier das Momentan echt nicht warum das nicht funktioniert. Es wird auch keine Exception geworfen.    :?


----------



## Kaffeebohne (25. Apr 2006)

Also ich glaube es gibt so eine Art Puffer, weil wenn ich nochmal ein unsinniges SQL-Statement reinhaue was nicht erfüllt werden kann gehts.

Demnach müsste es sowas wie ein Flush geben mit dem man den Puffer leermachen kann. ???


----------



## SamHotte (25. Apr 2006)

welche Klassen brauche ich - die MSAccess von oben stimmt noch?
_edit_auf jeden Fall brauche ich deine 'DatabaseDSN'


----------



## Kaffeebohne (25. Apr 2006)

```
package database;

public class DatabaseDSN {
    
    public String url      = "";
    public String database = "";
    public String user     = "";
    public String password = "";
    
    
    /** Creates a new instance of DatabaseDSN */
    public DatabaseDSN() {
    }
    
}
```

Ist eingentlich nur so eine Art Struct (C++), aber ich fasse immer Daten zusammen die meiner Meinung nach zusammengehören, da HashMaps oder Strings mir bei solchen sachen zu "unsicher" sind.


----------



## SamHotte (25. Apr 2006)

Prinzipiell ok, natürlich kann man jetzt über public Felder meckern ;-)


----------



## Kaffeebohne (25. Apr 2006)

Ich weiss, aber es geht mir nur darum das wenn ich z.B. eine HashMap, Array oder sowas einer Methode übergeben würde nicht sichergestellt ist, dass auch alle Felder vorhanden sind. So kann ich auch Defaultwerte setzen. Sicherlich könnte man noch über setter eine Validation einerführen, aber das ist bei reinen "Datentypen" imho übertrieben.


----------



## SamHotte (25. Apr 2006)

Mit kleinen Änderungen funzt es bei mir (aber nur, wenn die DB in Access nicht geöffnet ist):

```
import java.sql.SQLException;
import java.sql.Statement;

public class TestMsAccess
{

  public static void main (String[] args)
  {
    String url = "c:\\test.mdb";
    DatabaseDSN dsn = new DatabaseDSN();
    dsn.url = url;

    MSAccess ms = MSAccess.getInstance();
    Statement s = null;
    ms.setDSN(dsn);
    try {
      s = ms.getStatement();
      for (int i = 0; i < 10; ++i)
        s.execute("INSERT INTO testtab (foo) VALUES ('TEST')");
    }
    catch (SQLException se) {
      se.printStackTrace();
    }
    finally {
      try {
        s.close();
      }
      catch (SQLException exception) {
        exception.printStackTrace();
      }
    }

  }

}
```

_edit_ hmm... ist das Absicht, dass er 9 Datensätze einbaut?


----------



## Kaffeebohne (25. Apr 2006)

Also ich habe jetzt das Ding auch mal probiert bei mir werden aber nur 9 Datensätze geschrieben. Sind es bei dir wirklich  10?


----------



## SamHotte (25. Apr 2006)

Ne, 9, stimmt aber, wenn du in der for-Schleife '++i' machst und nicht 'i++' ...


----------



## Kaffeebohne (25. Apr 2006)

Precrement zählt einmal weniger, hmm -



```
System.out.println("Precrement");
        for(int i = 0; i < 10; ++i)
            System.out.println(i);
        System.out.println("Postcrement");
        for(int i2 = 0; i2 < 10; i2++)
            System.out.println(i2);
```

Also beides mal kommt bei mir 9 raus plus die 0 gibt 10    (9+0=10 hihi - so isses net gemeint)


----------



## SamHotte (25. Apr 2006)

Dann hab ich nen Denkfehler - und die Datenbank verschluckt einen Datensatz. Vielleicht muss man die Connection auch noch schließen ... hmm, nicht gut.


----------



## SamHotte (25. Apr 2006)

Jepp. Baue die finally-Klammer so:

```
finally {
      try {
        s.close();
        ms.close();
      }
      catch (SQLException exception) {
        exception.printStackTrace();
      }
    }
```


----------



## Kaffeebohne (25. Apr 2006)

Danke für den Tip. :toll:   

Was mich aber wundert: Was ähnliches hab ich mir ja auch gedacht, bei der ShipmentDAO finalize Methode. Also die Verbindung wieder zu schliessen, wenn das Objekt zerstöhrt wird. Nur da gabs ja den Fehler mit dem der Thread angefangen hat.

Finalize ist doch der "Destructor" oder?


----------



## SamHotte (25. Apr 2006)

Jein - finalize wird zwar vom GC aufgerufen, aber du kannst nicht steuern, wann - daher besser selbst machen.


----------



## Kaffeebohne (25. Apr 2006)

Kann es sein, dass der GC nicht aufgerufen wird, wenn das Programm beendet wird?


----------



## SamHotte (25. Apr 2006)

Kann auch passieren; verlassen kann man sich jedenfalls nicht auf ihn, daher würde ich gerade sowas wie Datenbankverbindungen selbst managen. Wird eigentlich auch in allen Beispielen so gemacht.


----------



## Kaffeebohne (25. Apr 2006)

Okay, dann mal ein dickes Dankeschön an dich für die tolle Hilfe.  :applaus: 

Somit wäre das Problem gelößt.


----------



## SamHotte (25. Apr 2006)

Gerne


----------

