Core Java

Java User Defined Types (UDT) in Java DB

Java DB is a relational database management system that is based on the Java programming language and SQL. This is the Oracle release of the Apache Software Foundation’s open source Derby project. Java DB is included in the Java SE 7 SDK.

A user defined type (UDT) is a Java class whose instances (objects) are stored in database table columns. UDTs are defined as column data type and UDT instances are stored as column values. UDTs can be created and used in a Java DB database.

The following are the contents of this post:
 
 

  1. User Defined Type (UDT)
  2. Create and use UDT
  3. Designing UDT – considering the effects of updating UDT on the existing data
  4. Example Code
  5. Notes &References

1. User Defined Type (UDT)

A UDT defines a data type in the database. A UDT is a Java class with public access modifier. This class implements the java.io.Serializable interface. The class must be visible on the classpath of the database application (or a tool) referring the class.

The UDT class is used to define a table or view column’s data type – a user defined data type. The UDT data is an instance (a Java object) of the UDT class; and is stored as column data. The UDT can also be referred in stored procedures and functions (in Java DB, these are Java based) as data types.

1.1.UDTs Attributes (Characteristics)

  • A UDT can have subtypes; and the subtype data can be populated to the main type i.e., a UDT class’s sub class instance can be populated as a UDT value. For example: (a) A Java class Type1 and a UDT defined in database as dbtype1 with it, (b) a Java class Subtype1 is a subclass of Type1, and (c) a dbtype1 table column can also be populated with an instance of Subtype1 (in addition to instances of Type1).
  • A UDT cannot be indexed, ordered, or compared; and may not be used with operators, grouped or aggregated in SQL expressions (for example, =, LIKE, DISTINCT, GROUP …).

2. Create and Use a UDT

  • 2.1. Create a UDT
  • 2.2.Use the UDT

2.1.Create a UDT

Create a Java class and define the UDT in the database.Create a Java class, for example TestType1.java(see code at: 4.1 TestType1.java), to be used as a UDT in the database. Compile the source code.

The SQL CREATE TYPE statement creates a UDT in the database. The syntax is:

CREATE TYPE udtTypeName
EXTERNAL NAME javaClassName
LANGUAGE JAVA

The command creates a UDT in the default or a specified schema, where:

  • udtTypeName is the name identifier for the UDT in the database.
  • javaClassName is the fully qualified name of the Java class.

For example, create the UDT in Java DB database using the ij tool (ij is a command line tool included with Java DB. ij is a JDBC tool used to run interactive queries on a Java DB database.):

ij> CONNECT 'jdbc:derby:testDB';
ij> CREATE TYPE type1 EXTERNAL NAME 'TestType1' LANGUAGE JAVA;

In the above example, testDB is an existing database. The UDT with the name type1 is created in the testDB database.

NOTE

The Java class file must be in the classpath to be referred from the ij tool.

2.1.1.Verify, Delete and Update a UDT

The created UDT can be verified using the following SQL command:

ij> SELECT alias, javaclassname FROM SYS.SYSALIASES WHERE aliastype='A';

To remove a UDT from the database use the DROP TYPE SQL command. The following is an example:

ij> DROP TYPE udtTypeName RESTRICT;

In the above example, the udtTypeName is the UDT name as defined in the database.

A UDT cannot be dropped if a database object is using (or referring) the UDT. For example, (a) if a table column is of type UDT, that UDT cannot be dropped, unless until the corresponding table column is dropped, or (b) if a database function is referring a UDT’s class (instance) the UDT cannot be dropped, unless until the function is modified not to refer that UDT class.

To update a UDT with the updated Java code, (re)compile the UDT class. This affects the objects of the UDT type. This may also affect the data stored in UDT objects, depending on the way a UDT is defined and used in an application. See the topic: 3. Designing UDT – considering the effects of updating UDT on the existing data.

2.2.Use the UDT

Create database objects with the UDT and manipulate the UDT data (insert, update, delete and query). The UDT data can be used in the database either with SQL interactively or with JDBC API in a Java program.

2.2.1. Interactive SQL

The following describes creating a database table column of UDT type, inserting data and querying the inserted data.

  • (i) Create a database table with UDT as a column type.

    For example:

    CREATE TABLE test_table1 (
    	id INT,
    	type1col type1, // column with UDT
    )
  • (ii) Insert data into the table.

    A custom built database function is used to insert data into the table column defined with UDT.For details about creating a custom function to insert UDT data into the table column, see 4.2. Example_Fn1 – Function.

    The example function Example_Fn1 has a signature Example_Fn1(String input) and returns an instance of TestType1 (where TestType1 is the Java class that represents the UDT).

    ij> INSERT INTO test_table1(id, type1col) VALUES(1, Example_Fn1("udt value 1"));

    The above SQL command inserts a row in the table with the UDT column value of TestType1Java object. The function Example_Fn1 invokes a TestType1 class’s constructor with a String input parameter to build an object; and that object is stored in the table column.

  • (iii) Query the inserted data.

    A custom function can be used to get the data from the UDT column. In the following example, the overridden Object class’s toString() method of the UDT class TestType1 returns the string value of the stored instance.

    ij> SELECT * FROM test_table1;
    ID         | TYPE1COL
    -------------------------
    1          | udt value 1
    

2.2.2. Using JDBC API

PreparedStatement and ResultSet interfaces defined in the java.sql package are used to insert and get the database UDT data respectively.

  • PreparedStatement‘s setObject() method is used to store the UDT data as an object in the UDT table column. The method setObject(int parameterIndex, Object obj) sets the value of the designated parameter using the given object.
  • ResultSet’s getObject() method is used to retrieve the stored UDT data from the UDT table column. The method getObject(int columnIndex) gets the value of the designated column in the current row of this ResultSet object. The returned data is an Object.

The following Java code snippets show the usage:

// insert data into a table
int idValue = 2;
TestType1 obj = new TestType1("udt value 2");
PreparedStatement ps = conn.prepareStatement("INSERT INTO test_table1 VALUES (?, ?)";
ps.setInt(1, idValue); // where 1 is the parameter index
ps.setObject(2, obj); // UDT data
ps.executeUpdate();
...
// retrieve data from a table
PreparedStatement ps = conn.prepareStatement("SELECT * FROM test_table1");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int idValue = rs.getInt(1);
    TestType1 testType1 = (TestType1) rs.getObject(2); 
// where 2 is the column index in ResultSet object
    ...
}

NOTE

  1. In the above code, conn is the database Connection object.
  2. The UDT Java class file must be in the classpath to be referred from the JDBC code.
  3. Designing UDT – considering the effects of updating UDT on the existing data

A UDT is used to store data. This UDT (and data) may change (i.e., evolve) over the lifecycle of an application. It is required to design the UDT to take this into consideration.

In addition, note that the UDT class always implements Serializable interface. The effects of data serialization and various versions of UDT data objects must be considered. In simple cases, just compiling the changed UDT class code may suffice.

Here are two ways a UDT can be designed and used.

  • UDT class implements Serializable and the application (with the UDT) uses data conversion applications as the UDT data evolves.
  • UDT class implements Externalizable (instead of Serializable) and uses data conversion functionality within the UDT class. An example, with UDT class’s code is shown below.

About Externalizable

A UDT class must implement Serializable interface; and java.io.Externalizable extends Serializable.

When this interfaceis implemented, only the identity of the class (not the state) of an Externalizable instance is written in the serialization stream. It is the responsibility of the class to save and restore the contents (state) of its instances.

There are two methods that must be implemented:

  • readExternal(ObjectInput in): The object implements the readExternal() method to restore its contents.
  • writeExternal(ObjectOutput out): The object implements the writeExternal() method to save its contents.

3.1 UDT class implements Serializable

UDT class implements Serializable and the application (that uses the UDT) uses data conversion applications as the UDT data evolves – the following are the overview steps:

  • UDT implements Serializable.
  • Create and use a UDT with an initial version.
  • Save (store) the previous version’s data, before updating the UDT with the next version.
  • Create next version UDT (update the earlier version).
  • Convert the initial version data to the current updated version data.

Note that, in this case, all the previous version data is converted to current (new) version, all at a time.

3.2 UDT class implements Externalizable

UDT class implements Externalizable (instead of Serializable) and uses data conversion functionality within it – the following are the overview steps:

  • UDT implements Externalizable.
  • Create and use a UDT with an initial version.
  • No actions are required before updating the UDT with the next version.
  • Create next version UDT (update the earlier version), with the data conversion functionality built within.

In this case, the previous version data is converted to current (new) version whenever data is queried or updated. The version information is included within the UDT class.

The following is an example with details and code for UDT class.

  1. Create a UDT class, with version 1: Testtype2.java (see code at: 4.3Testtype2.java (version 1))
  2. Compile
  3. Create the UDT in the database: type2
  4. Create a table with a column of UDT: test_table3
  5. Insert data into the UDT column
  6. Query the UDT data
  7. Update the UDT class, with version 2: Testtype2.java(see code at: 4.3 Testtype2.java (version 2))
  8. Compile
  9. Insert (version 2) data into the UDT column
  10. Query the UDT data – both the version 1 and version 2 data

NOTE

The example in this topic does not show details or code of SQL commands and functions usedfor inserting and querying UDT data. These can be similar to the example shown earlier in this post.

4. Example Code

  • 4.1. TestType1.java
  • 4.2. Example_Fn1 – Function
  • 4.3.Testtype2.java (version 1)
  • 4.4.Testtype2.java (version 2)

4.1.TestType1.java

public class TestType1
        implements java.io.Serializable {
    private String value = "DEFAULT";
    public TestType1() {
    }
    public TestType1(String s) {
        value = s;
    }
    @Override
    public String toString() {
        return value;
    }
} // class

4.2.Example_Fn1 – Function

This is the function to insert data into the UDT column, referred from the example in 2.2. Use the UDT.

  • (i) Create a Java class, with a public static method with the function’s functionality.
  • (ii) Create the function in the database using the CREATE FUNCTION command.

The following are the Java class for the function and the CREATE FUNCTION command. The Java class is compiled and the CREATE FUNCTION command is run using the ij tool interactively.

public class FunctionClass {
    public static TestType1 FnMethod1(String s) {
        return new TestType1(s);
    }
}
CREATE FUNCTION Example_Fn1(VARCHAR(25))
RETURNS type1
LANGUAGE JAVA
PARAMETER STYLE JAVA
NO SQL
EXTERNAL NAME 'FunctionClass.FnMethod1';

NOTE

The SQL commands SHOW FUNCTIONS and DROP FUNCTION are used from ijto verify the created function and to remove it from the database respectively.

4.3.Testtype2.java (version 1)

import java.io.*;
public class Testtype2
        implements Externalizable {
    private static final long serialVersionUID = 1L;
    private static final int FIRST_VERSION = 1; // initial version id
    private String value = "DEFAULT";
    public Testtype2() {
    }
    public Testtype2(String s) {
        value = s;
    }
    @Override
    public void writeExternal(ObjectOutput out)
            throws IOException {
        // first write the version id
out.writeInt(FIRST_VERSION);
        // next write the state
out.writeObject(value);
    }
    @Override
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        // read the version id
int version = in.readInt();
        if (version < FIRST_VERSION) {	
            throw new IOException("Corrupt data stream (no such version).");
        }
        if (version > FIRST_VERSION) {
            throw new IOException("Can't deserialize from the future versions.");
        }
        // read object (state)
        value = (String) in.readObject() + "_V" + version;
    } // readExternal()
    @Override
    public String toString() {
        return value;
    }
} // version 1 class

4.4.Testtype2.java (version 2)

import java.io.*;
public class Testtype2
        implements Externalizable {
    private static final long serialVersionUID = 1L;
    private static final int FIRST_VERSION = 1; // initial version id
    private static final int NEW_VERSION = 2;
    private String value = "DEFAULT";
    private double newData;
    public Testtype2() {
    }
    public Testtype2(String s, double i) {
        value = s;
newData = i;
    }
    @Override
    public void writeExternal(ObjectOutput out)
            throws IOException {
        // first write the version id
out.writeInt(NEW_VERSION);
        // next write the state
out.writeObject(value);
out.writeDouble(newData);
    }
    @Override
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        if (version < FIRST_VERSION) {	
            throw new IOException("Corrupt data stream (no such version).");
        }
        if (version > NEW_VERSION) {
            throw new IOException("Can't deserialize from the future versions.");
        }
        // read object
        value = (String) in.readObject() + "_V" + version;
        // read new version's data
if (version == NEW_VERSION) {
newData = in.readDouble();
        }
        else { // if FIRST_VERSION
            // newData is its default value, 0
        }
    } // readExternal()
    @Override
    public String toString() {
        return value + ":" + newData;
    }
} // version 2 class

NOTE

  1. Externalizable‘sreadExternal()method must read the values in the same sequence and with the same types as were written by writeExternal() method.
  2. In the above example code theserialVersionUID variable is optional.

5. Notes &References

  • An example usage in a Java Swing text editor application:  A GUI text editor creates a text document as a java.swing.text.PlainDocument class’s instance. A UDT Java class is created with contents – like the PlainDocument instance, the document name, created date, etc. and is used in the application to store the data.
  • Oracle 10g database supports creating and using Java based UDT’s; these are referred to as SQLJ types. The Java classes representing the UDT implement the java.sql.SQLData or oracle.sql.ORAData interface, not the java.io.Serializable. These UDT’s are created using the CREATE TYPE SQL statement and are stored to the server, and are accessed through SQL.
  • Link to Apache Derby > Documentation (10.8 Manuals): http://db.apache.org/derby/manuals/index.html

Prasad Saya

Prasad Saya is a software engineer with over ten years’ experience in application development, maintenance, testing and consulting on various platforms. He is also a certified Java and Java EE developer. At present his interest is in developing Java applications.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button