Storing Objects and Classes
Page 2 of 5

Storing Objects in the Database

Cloudscape allows you to store Java objects in tables. To be storable, an object must be serializable. Serialization is a Java mechanism for reading and writing objects to a stream; an object is serializable if its class--or any of its superclasses or the interfaces it implements--implements java.io.Serializable or java.io.Externalizable. When an object is serializable, Java knows how to write the value of each of its fields to an output stream and how to read the value of each of its fields from an input stream.

To define a table to use a Java data type, you use the keyword SERIALIZE along with the name of the serializable class or interface to specify the data type:

columnName SERIALIZE(className or aliasName)

NOTE: Serialization is a relatively slow process. For that reason, it is more efficient to store built-in data types than objects. Where possible, you may want to "translate" an object into built-in types for storage. The toursDB database contains many objects to teach you how to work with objects in SQL if you choose to. In reality, the database should probably contain more built-in data types.

Create a Class for HelloWorldDB

In this task, you will create a new class and store instances of it in the HelloWorldDB database.

  1. Open the file Square.java.

    Square is currently an empty class. You will define one field for the class (width), two constructors, and one method (calculateArea).

  2. Create one field for the width of the square:

    int width;

  3. Make two constructors: an empty one that constructs a square of the default size of 10x10, and one that takes one parameter (width).

    public Square() {

       width = 10;

    }

    public Square(int w) {

       width = w;

    }

  4. Give the class a calculateArea method (a class without any methods is like a day without sunshine):

    public int calculateArea() {

       return width*width;

    }

The file should look like Figure 10-1.

Figure 10-1 Square.java

  1. Save the file and compile the class:

    javac Square.java

  2. Start ij following the instructions in Start ij.
  3. Connect to HelloWorldDB:

    Connect 'HelloWorldDB';

  4. Try to a create a table called Shapes with a single column, called Rectangle. The data type for the column should be:

    SERIALIZE (Square)

    So the full table definition should be:

    CREATE TABLE Shapes (Rectangle SERIALIZE(Square));

    You should get the following error:

    ERROR 42X27: The class 'Square' for column 'RECTANGLE' does not implement java.io.Serializable. User-defined types must be serializable.

    Cloudscape does not let you use the Square data type in the table definition because Square does not implement java.io.Serializable.

  5. Exit ij, open Square.java, and edit its definition to implement Serializable:

    public class Square implements Serializable {

The file should now look like Figure 10-2.

Figure 10-2 Square.java, stage two

  1. Compile the class.

    javac Square.java

  2. Start ij and connect to HelloWorldDB.
  3. Try again:

    CREATE TABLE Shapes (Rectangle SERIALIZE(Square));

    This time, you should see success:

    0 rows inserted/updated/deleted

  4. Insert a few Squares just to make sure:

    INSERT INTO Shapes VALUES (new Square()),
    (new Square(5)), (new Square(10));

  5. Now try out method invocation:

    SELECT MAX(Rectangle.calculateArea()) FROM Shapes;

    If all goes well, you should see 100 as the result:

    SQLCol1
    ---------------
    100

Try Altering the Class and Storing New Instances

  1. Exit ij.
  2. Open Square.java and add a new method:

    public int calculatePerimeter() {

        return 4*width;

    }

The file should now look like Figure 10-3.

Figure 10-3 Square.java, stage three

  1. Save the file and compile it.
  2. Start ij and reconnect to HelloWorldDB.
  3. Try selecting from the table:

    SELECT * FROM Shapes;

    You should get an error that looks something like the following:

    ERROR XSDA8: Exception during restore of a serializable object
    ERROR XJ001: Java exception: 'Square; Local class not compatible: stream classdesc serialVersionUID =
    -3864197885517086522 local class serialVersionUID = 1230816243124053902: java.io.InvalidClassException'.

    You get this error because Java requires that serialized classes be compatible. Changes in a class automatically violate this compatibility, unless you explicitly tell the JVM that the changes in class are compatible changes. Compatible changes are changes such as new methods. Incompatible changes are changes such as field deletion. Java automatically assumes that any change is an incompatible change unless a class's serialVersionUID is unchanged. Java includes this automatically generated static final long field in all Serializable classes, regenerating a new value with each permutation of a class. If you explicitly include the value of the field in your class, instead of letting Java do it for you, you have more control over when classes are deemed incompatible, because then you--not Java--decide when the value should change.

    In our current situation, the SQLException message has given us a clue: the serialVersionUID of the stored instances of the class is
    -3864197885517086522. This number may be different in your environment, since it is automatically generated.

Add the Explicit serialVersionUID to Square

  1. Exit ij.
  2. Edit Square.java, adding the following field:

    /* paste the actual value from your own error message, and add a capital L at the end of the value to avoid an arithmetic overflow */
    static final long serialVersionUID = -3864197885517086522L;

The file should now look like Figure 10-4.

Figure 10-4 Square.java, stage four

  1. Compile the class.
  2. Start ij and reconnect to HelloWorldDB.
  3. Try selecting from the table:

    SELECT Rectangle.calculatePerimeter() FROM Shapes;

  4. Do not exit ij.