Freitag, 9. März 2012

Problem beim Auslesen eines Blobs aus SQLite: invalid stream header

org.xerial.sqlite-jdbc 3.7.2
mysql-connector-java 5.1.18

Ursprünglich habe ich das Speichern und Auslesen eines Objekts für eine MySql Datenbank geschrieben, aber um das ganze ohne laufenden MySql Server testbar zu machen wollte ich einfach auch eine sqLite Datenbank als Alternative zur Verfügung haben. Ich habe nicht schlecht gestaunt, als derselbe Code, der für MySql einwandfrei funktionierte, bei SQLite beim Auslesen des Blobs eine Fehlermeldung warf:

java.io.StreamCorruptedException: invalid stream header: 61742E75 at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:797) at java.io.ObjectInputStream.(ObjectInputStream.java:294)

Nachlesen in Foren ergab, dass ObjectInputStream nicht funktioniert wenn die Daten nicht zuvor mit ObjectOutputStream der Datenbank übergeben wurden.

Hier mein ursprünglicher Code zum Einspielen des Objects:

public static void insertIntoTable(BigInteger id, SessionData sd, byte[] rtsd, IvissWorker ivissWorker) {
    PreparedStatement pstmt = null;
    Connection con = null;
    try {
        con = getConnection();
        pstmt = con
                .prepareStatement("REPLACE INTO iviss_session_table (id, ivissblob, rtblob, lastaccess) VALUES(?,?,?,?)");
        pstmt.setLong(1, Long.parseLong(id.toString()));
        pstmt.setObject(2, sd);
        pstmt.setObject(3, rtsd);
        pstmt.setDate(4, new Date(System.currentTimeMillis()));
        pstmt.executeUpdate();
    } catch (SQLException e) {
        ivissWorker.getIvissWorkerOutputHandler().addError(Constants.ERROR_205, "", DbConfiguration.getDbUri());
    } finally {
        try {
            if (pstmt != null) {
                pstmt.close();
            }
            if (con != null) {
                con.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Beim Auslesen aus einer SQLite Datenbank schlug dann das Erzeugen des ObjectInputstream fehl:

public static SessionData getIvissSession(BigInteger id) throws IvissDatabaseException {
    SessionData sd = null;
    PreparedStatement pstmt = null;
    Connection con = null;
    try {
        con = getConnection();
        pstmt = con.prepareStatement("SELECT ivissblob FROM iviss_session_table WHERE id =?");
        pstmt.setLong(1, Long.parseLong(id.toString()));
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            byte[] ivissblob = rs.getBytes("ivissblob");
            ObjectInputStream objectIn = new ObjectInputStream(new ByteArrayInputStream(ivissblob));
            sd = (SessionData) objectIn.readObject();
            objectIn.close();
        }

    } catch (SQLException e) {
        throw new IvissDatabaseException(Constants.ERROR_202);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {

        try {
            if (pstmt != null) {
                pstmt.close();
            }
            if (con != null) {
                con.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    return sd;
}

Gelöst wurde das Problem, indem ich der Datenbank das Object nicht mit .setObject übergeben habe, sondern als byte Array, das byte Array wird wiederrum mit ObjectOutputStream erzeugt, damit kam ObjectInputStream dann zurecht. Warum der MySql Treiber hier weniger Probleme macht und etwas aus dem ResultSet übergibt, mit dem der ObjectInputStream gut zurechtkommt bleibt mir ein Rätsel.

Hier die geänderte Zeile bei der Übergabe:

     pstmt.setBytes(2, IvissUtil.getBytes(sd));

Und hier die Methode die mir das byte Array erzeugt:

public static byte[] getBytes(Object obj) throws java.io.IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    oos.flush();
    oos.close();
    bos.close();
    byte[] data = bos.toByteArray();
    return data;
}

Links:
Frage diesbezüglich in stackoverflow

Keine Kommentare:

Kommentar veröffentlichen