The following example illustrates a Java UDT class with nondefault definitions. JavaType is the new Java UDT, and JavaBuffer is the buffer type for the SQL data being converted, as I/O Function Sets and Related Types shows. For a complete set of required and optional code, see Usage Example.
public class JavaType implements SQLData { // Java data Object declarations for this Class.... // non-default Data Input function public static JavaType JavaTypeInput( JavaBuffer in ) { JavaType x = new JavaType(); // make a new object // convert JavaBuffer fields to Java data objects in // this Class return( x );// return the new object } // non-default Data Output function public static JavaBuffer JavaTypeOutput( JavaType out ) { JavaBuffer x = new JavaBuffer(); // Do whatever it takes to translate object to output // buffer format return x; // return the initialized buffer } // required SQLData implementation private String type; public String getSQLTypeName() { return type; } public void readSQL ( SQLInput instream, String typeName ) throws SQLException { type = typeName; // cast up to Informix specific stream type IfmxUDTSQLInput in = (IfmxUDTSQLInput) instream; // read stream fields into Java data objects in this Class return; } public void writeSQL( SQLOutput outstream ) throws SQLException { // cast up to Informix specific stream type IfmxUDTSQLOutput out = (IfmxUDTSQLOutput) outstream; // write object to output stream return; } }
For an example of the SQL definitions required to use the explicit methods in the preceding code, see SQL Definitions for a Variable-Length UDT Example.
All Java UDT classes must implement the readSQL() and writeSQL() methods for the SQLData interface. The readSQL() method initializes a Java object using data from the database server in a C-language format. The writeSQL() method converts a Java object back to the representation of the database server. The readSQL() and writeSQL() methods receive a Stream argument that encapsulates the conversion methods for each built-in type that the database server uses, for example, int, float, decimal.
In the case of a fixed-length UDT, the readSQL() and writeSQL() methods know the order and number of fields they are to process. In the case of a variable-length UDT, the programmer must rely on the stream.available() method and/or the SQLException to find the end of the data as this example shows.
/* Variable Length UDT example type: Record3 ** Example of required and explicit method implementations. ** ** The C language structure equivalent of this JUDT is: ** ** typedef struct ** { ** mi_double_precision d; ** mi_chara[4]; ** mi_integerb; ** mi_realc; ** mi_datee; ** mi_smallintf; ** mi_booleang[MAXBOOLS]; ** } NewFixUDT; ** ** Where the last boolean array can contain up to MAX values ** but only valid values will be written to disk. */ // Put this in our test package, // could be anywhere but needs to match SQL definitons for UDRs. package informix.testclasses.jlm.udt; // get the usual suspect classes import java.sql.*; // get informix specific interfaces, etal. import com.informix.jdbc.*; // These are only needed for the non-default Input/Output // functions, remove if you use defaults. import informix.jvp.dbapplet.impl.IfmxTextInStream; import informix.jvp.dbapplet.impl.IfmxTextOutStream; /**************** Now here's our UDT *************/ public class Record3 implements SQLData { // to turn debug print lines on and off private static boolean classDebug = true; // define storage for Java members of UDT private double d_double; private String a_char; private int b_int; private float c_float; private java.sql.Date e_date; private short f_smint; // could use a Vector for booleans, but would then need Boolean // objects ...so I've left it as an exercise for the reader... private static final int MAXBOOLS = 20; private boolean g_boolvals[] = new boolean[MAXBOOLS]; private int numbools = 0; // dummy constructor just so we can log instantiation public Record3() { super(); if( classDebug ) System.out.println( "Record3() " + super.toString() + " created" ); } // dummy finalizer just so we can log our own destruction protected void finalize() { super.finalize(); if( classDebug ) System.out.println( "Record3() " + super.toString() + " deleted" ); } /*********** REQUIRED SQLData implementation: ***********/ // needed for SQLData interface private String type; public String getSQLTypeName() { return type; } // Called to convert an SQL buffer TYPE to JAVA class. // note: we need to use SQLInput as the argument type or this // method signature won't resolve correctly. public void readSQL (SQLInput in, String typeName) throws SQLException { if( classDebug ) System.out.println( "Record3.readSQL() entered" ); // save the type name type = typeName; // cast the _real_ type of Stream for IFMX extensions. IfmxUDTSQLInput stream = (IfmxUDTSQLInput) in; // trap exceptions; don't really know how many bytes // are in the input. try { d_double = stream.readDouble(); a_char = stream.readString(4); b_int = stream.readInt(); c_float = stream.readFloat(); e_date = stream.readDate(); f_smint = stream.readShort(); // Read booleans until we get an exception: // converting a non-existant boolean will throw cookies. // but we can use available() to make sure there is more // to read... for( int count = 0; (stream.available() > 0) && (count < MAXBOOLS); ++count ) { g_boolvals[count] = stream.readBoolean(); ++numbools; } } catch (SQLException e) { // if we got something besides end of input rethrow, // otherwise just assume we're done. if( e.getErrorCode() != IfxErrMsg.S_BADSQLDATA ) { if( classDebug ) System.out.println("Record3.readSQL() exception = " + e.toString()); throw e; } } } // Called to convert JAVA class to SQL buffer TYPE. // note: we need to use SQLOutput as the argument type or this // method signature won't resolve correctly. public void writeSQL( SQLOutput out ) throws SQLException { if( classDebug ) System.out.println( "Record3.writeSQL() entered" ); // cast up to _real_ type of Stream to use IFMX extensions. IfmxUDTSQLOutput stream = (IfmxUDTSQLOutput) out; stream.writeDouble(d_double); stream.writeString(a_char, 4); stream.writeInt(b_int); stream.writeFloat(c_float); stream.writeDate(e_date); stream.writeShort(f_smint); for( int i = 0; i < numbools; i++ ) stream.writeBoolean(g_boolvals[i]); } /*********** END SQLData implementation ***********/ /**** NON-DEFAULT implementation of Input and Output functions ****/ /* Remove all this if you only use the Defaults */
The following example illustrates the implementation of user-defined input and output functions that override the default I/O methods. If you use the default methods, you do not need to implement overriding methods like those that follow:
// Called as Input function to convert SQL lvarchar to JAVA class public static Record3 fromString( String str ) { if( classDebug ) System.out.println( "Record3.fromString(String) entered" ); // Make a stream of the right kind. IfmxTextInStream stream = new IfmxTextInStream(str); // Make a new Java object of the right type. Record3 record = new Record3(); // Just call readSQL ourselves. // For a real implementation you would probably copy all the // readXXX()'s and intersperse delimiting chars as needed... try { readSQL( stream, "Record3" ); } catch (Exception e) { System.err.println(e.getMessage()); } return record; } // Called as Output function; convert JAVA class to SQL lvarchar. // note: could use toString() directly, // except that the UDR method must be "static", and // it needs to take a Record3 as an argument.... public static String makeString(Record3 x) { if( classDebug ) System.out.println( "Record3.makeString() entered" ); return x.toString(); } // Might as well implement the standard toString() as long as // we're doing non-defaults. If a different method name is // used here, Object.toString() will be called when the class // gets printed out in debug lines.... public String toString() { // Need to use a StringBuffer because we can't pass a // reference to a String to be initialized. // We could optimize by guessing at size of buffer, too. // StringBuffer str = new StringBuffer(); // IfmxTextOutStream stream = new IfmxTextOutStream(str); // Just call writeSQL. // For a real implementation you would probably copy all the // writeXXX()'s and intersperse delimiting chars as needed... try { writeSQL( stream ); } catch (Exception e) { System.err.println(e.getMessage()); // not sure if we need to clear out result string? str.setLength(0); } return str.toString(); }
The SQL definitions for this example are:
-- VarLen UDT and support functions ---------------------------- create opaque type Record3 (internallength = variable, alignment = 8, maxlen = 2048, cannothash ); grant usage on type Record3 to public; -- register JUDT implementation.... -- note package name needs to match class file package execute procedure setUDTExtName("Record3", "informix.testclasses.jlm.udt.Record3"); -- Definitions for NON_DEFAULT Input/Output functions. -- this overrides the defaults setup above -- LVARCHAR INPUT drop cast (Record3 as lvarchar); create implicit cast (Record3 as lvarchar with record3_output); create function record3_input (l lvarchar) returns Record3 external name 'informix.testclasses.jlm.udt.Record3.fromString(java.lang.String)' language java not varient; grant execute on function record3_input to public; -- CHAR INPUT drop cast (Record3 as char(100)); create implicit cast (Record3 as char(100) with record3_rout); create function record3_rin (c char(100)) returns Record3 external name 'informix.testclasses.jlm.udt.Record3.fromString(java.lang.String)' language java not varient; grant execute on function record3_rin to public; -- LVARCHAR OUTPUT drop cast (lvarchar as Record3); create explicit cast (lvarchar as Record3 with record3_input); create function record3_output (c Record3) returns lvarchar external name 'informix.testclasses.jlm.udt.Record3.makeString(informix.testclasses.jlm.udt.Record3)' language java not varient; grant execute on function record3_output to public; -- CHAR OUTPUT drop cast (char(100) as Record3); create explicit cast (char(100) as Record3 with record3_rin); create function record3_rout (c Record3) returns varchar(100) external name 'informix.testclasses.jlm.udt.Record3.makeString(informix.testclasses.jlm.udt.Record3)' language java not varient; grant execute on function record3_rout to public; -- END definitions for NON_DEFAULT Input/Output functions. -- end VarLen UDT and support functions -------------------------- -- Example Usage --- create table rec3tab (record_col Record3); insert into rec3tab values ('665.999 JAVA 398 197.236 1952-04-10 47 f t t'); insert into rec3tab values ('667.000 Jive 983 791.632 2002-04-11 42 f f f f f'); select * from rec3tab;Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]