Bug #35228 getObject() doesn't return correct type all the time
Submitted: 11 Mar 2008 19:42 Modified: 12 Mar 2008 1:26
Reporter: S Dan Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: Data Types Severity:S1 (Critical)
Version:5.0.45 OS:Linux
Assigned to: CPU Architecture:Any

[11 Mar 2008 19:42] S Dan
Description:
getObject() method doesn't return the correct type all the time.
If I insert a double to the blob type column, getObject retrieves
it as String. However if I store it as an ArrayList, getObject retrieves
it as ArrayList which is correct.

I'm using 5.0.45 and connector version is 5.1.6
Please try the following code after replacing the appropriate fields
and see what happens.

The oitput will be
readJavaObject: done de-serializing: java.lang.String

where is should be
readJavaObject: done de-serializing: java.lang.Double
/*
 * mysql> CREATE TABLE java_objects ( 
 * id INT AUTO_INCREMENT, 
 * name varchar(128), 
 * object_value BLOB, 
 * primary key (id));
 **/

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class SerializeJavaObjects_MySQL {
  static final String WRITE_OBJECT_SQL = "INSERT INTO java_objects(name, object_value) VALUES (?, ?)";

  static final String READ_OBJECT_SQL = "SELECT object_value FROM java_objects WHERE id = ?";

  public static Connection getConnection() throws Exception {
    String driver = "org.gjt.mm.mysql.Driver";
    String url = "jdbc:mysql://localhost/testdb?autoDeserialize=true";
    String username = "user";
    String password = "pass";
    Class.forName(driver);
    Connection conn = DriverManager.getConnection(url, username, password);
    return conn;
  }

  public static long writeJavaObject(Connection conn, Object object) throws Exception {
    String className = object.getClass().getName();
    PreparedStatement pstmt = conn.prepareStatement(WRITE_OBJECT_SQL);

    // set input parameters
    pstmt.setString(1, className);
    pstmt.setObject(2, object);
    pstmt.executeUpdate();

    // get the generated key for the id
    ResultSet rs = pstmt.getGeneratedKeys();
    int id = -1;
    if (rs.next()) {
      id = rs.getInt(1);
    }

    rs.close();
    pstmt.close();
    System.out.println("writeJavaObject: done serializing: " + className);
    return id;
  }

  public static Object readJavaObject(Connection conn, long id) throws Exception {
    PreparedStatement pstmt = conn.prepareStatement(READ_OBJECT_SQL);
    pstmt.setLong(1, id);
    ResultSet rs = pstmt.executeQuery();
    rs.next();
    Object object = rs.getObject(1);
    String className = object.getClass().getName();

    rs.close();
    pstmt.close();
    System.out.println("readJavaObject: done de-serializing: " + className);
    return object;
  }
  public static void main(String args[])throws Exception {
    Connection conn = null;
    try {
      conn = getConnection();
      System.out.println("conn=" + conn);
      conn.setAutoCommit(false);
      /*
      List<Object> list = new ArrayList<Object>();
      list.add("This is a short string.");
      list.add(new Integer(1234));
      list.add(new Date());
      long objectID = writeJavaObject(conn, list);
      */

      long objectID = writeJavaObject(conn, 2.0);
      conn.commit();
      System.out.println("Serialized objectID => " + objectID);
      Object value = readJavaObject(conn, objectID);
                                                                                                                                                                         System.out.println("[After De-Serialization] value=" + value);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      conn.close();
    }
  }
}

How to repeat:
It can be repeated all the time.
[11 Mar 2008 19:50] Mark Matthews
JDBC specifies type mappings for java.lang.Object, that don't include java.lang.Double. If you look at the type mappings in the JDBC specification, there the mapping from Double to Blob isn't allowed. We're a little loose there, but because we don't know metadata about the column you're inserting into, we can't determine ahead of time that we need to serialize to a java object. Until such time as MySQL itself returns that metadata, this functionality can't work as you expect.

I'd suggest that a workaround would be always using an ArrayList, even of element count 1 for those single values, if you really need to serialize/de-serialize Java Objects from MySQL BLOBs. 

  -Mark
[12 Mar 2008 1:26] S Dan
Mark, Thank you for your prompt response. So let me make sure if I understood this right. If I'd like to serialize/deserialize an arbitrary java object, the only work around is to use an ArrayList of length 1? Or is there a better way around?
Thanks.
-Dan