@ -23,16 +23,16 @@ public class Serialize
{
// This is the connection that the instance refers to
protected org . postgresql . Connection conn ;
// This is the table name
protected String tableName ;
// This is the class name
protected String className ;
// This is the Class for this serialzed object
protected Class ourClass ;
/ * *
* This creates an instance that can be used to serialize or deserialize
* a Java object from a PostgreSQL table .
@ -41,16 +41,16 @@ public class Serialize
{
try {
conn = c ;
tableName = type . toLowerCase ( ) ;
className = toClassName ( t ype ) ;
tableName = toPostgreSQL ( type ) ;
className = type ;
ourClass = Class . forName ( className ) ;
} catch ( ClassNotFoundException cnfe ) {
throw new PSQLException ( "postgresql.serial.noclass" , type ) ;
}
// Second check, the type must be a table
boolean status = false ;
ResultSet rs = conn . ExecSQL ( "select typname from pg_type,pg_class where typname=relname and typname='" + type + "'" ) ;
ResultSet rs = conn . ExecSQL ( "select typname from pg_type,pg_class where typname=relname and typname='" + tableName + "'" ) ;
if ( rs ! = null ) {
if ( rs . next ( ) )
status = true ;
@ -59,10 +59,26 @@ public class Serialize
// This should never occur, as org.postgresql has it's own internal checks
if ( ! status )
throw new PSQLException ( "postgresql.serial.table" , type ) ;
// Finally cache the fields within the table
}
/ * *
* Constructor when Object is passed in
* /
public Serialize ( org . postgresql . Connection c , Object o ) throws SQLException
{
this ( c , o . getClass ( ) . getName ( ) ) ;
}
/ * *
* Constructor when Class is passed in
* /
public Serialize ( org . postgresql . Connection c , Class cls ) throws SQLException
{
this ( c , cls . getName ( ) ) ;
}
/ * *
* This fetches an object from a table , given it ' s OID
* @param oid The oid of the object
@ -73,14 +89,20 @@ public class Serialize
{
try {
Object obj = ourClass . newInstance ( ) ;
// NB: we use java.lang.reflect here to prevent confusion with
// the org.postgresql.Field
java . lang . reflect . Field f [ ] = ourClass . getDeclaredFields ( ) ;
// used getFields to get only public fields. We have no way to set values
// for other declarations. Maybe look for setFieldName() methods?
java . lang . reflect . Field f [ ] = ourClass . getFields ( ) ;
boolean hasOID = false ;
int oidFIELD = - 1 ;
StringBuffer sb = new StringBuffer ( "select" ) ;
char sep = ' ' ;
// build a select for the fields. Look for the oid field to use in the where
for ( int i = 0 ; i < f . length ; i + + ) {
String n = f [ i ] . getName ( ) ;
if ( n . equals ( "oid" ) ) {
@ -95,13 +117,37 @@ public class Serialize
sb . append ( tableName ) ;
sb . append ( " where oid=" ) ;
sb . append ( oid ) ;
DriverManager . println ( "store: " + sb . toString ( ) ) ;
ResultSet rs = conn . ExecSQL ( sb . toString ( ) ) ;
if ( rs ! = null ) {
if ( rs . next ( ) ) {
for ( int i = 0 ; i < f . length ; i + + ) {
f [ i ] . set ( obj , rs . getObject ( i + 1 ) ) ;
if ( ! Modifier . isFinal ( f [ i ] . getModifiers ( ) ) ) {
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "short" ) ) {
f [ i ] . setShort ( obj , rs . getShort ( i + 1 ) ) ;
}
else
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "char" ) ) {
f [ i ] . setChar ( obj , rs . getString ( i + 1 ) . toCharArray ( ) [ 0 ] ) ;
}
else
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "byte" ) ) {
f [ i ] . setByte ( obj , rs . getByte ( i + 1 ) ) ;
}
else
// booleans come out of pgsql as a t or an f
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "boolean" ) ) {
if ( rs . getString ( i + 1 ) . equals ( "t" ) )
f [ i ] . setBoolean ( obj , true ) ;
else
f [ i ] . setBoolean ( obj , false ) ;
}
else {
f [ i ] . set ( obj , rs . getObject ( i + 1 ) ) ;
}
}
}
}
rs . close ( ) ;
@ -114,7 +160,7 @@ public class Serialize
throw new SQLException ( ie . toString ( ) ) ;
}
}
/ * *
* This stores an object into a table , returning it ' s OID . < p >
*
@ -138,24 +184,27 @@ public class Serialize
try {
// NB: we use java.lang.reflect here to prevent confusion with
// the org.postgresql.Field
java . lang . reflect . Field f [ ] = ourClass . getDeclaredFields ( ) ;
// don't save private fields since we would not be able to fetch them
java . lang . reflect . Field f [ ] = ourClass . getFields ( ) ;
boolean hasOID = false ;
int oidFIELD = - 1 ;
boolean update = false ;
// Find out if we have an oid value
for ( int i = 0 ; i < f . length ; i + + ) {
String n = f [ i ] . getName ( ) ;
if ( n . equals ( "oid" ) ) {
hasOID = true ;
oidFIELD = i ;
// We are an update if oid != 0
update = f [ i ] . getInt ( o ) > 0 ;
}
}
StringBuffer sb = new StringBuffer ( update ? "update " + tableName + " set" : "insert into " + tableName + " values " ) ;
StringBuffer sb = new StringBuffer ( update ? "update " + tableName + " set" : "insert into " + tableName ) ;
char sep = update ? ' ' : '(' ;
for ( int i = 0 ; i < f . length ; i + + ) {
String n = f [ i ] . getName ( ) ;
@ -164,49 +213,108 @@ public class Serialize
sep = ',' ;
if ( update ) {
sb . append ( '=' ) ;
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "java.lang.String" ) ) {
// handle unset values
if ( f [ i ] . get ( o ) = = null )
sb . append ( "null" ) ;
else
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "java.lang.String" ) | |
f [ i ] . getType ( ) . getName ( ) . equals ( "char" ) ) {
sb . append ( '\'' ) ;
sb . append ( f [ i ] . get ( o ) . toString ( ) ) ;
// don't allow single qoutes or newlines in the string
sb . append ( fixString ( f [ i ] . get ( o ) . toString ( ) ) ) ;
sb . append ( '\'' ) ;
} else
sb . append ( f [ i ] . get ( o ) . toString ( ) ) ;
}
}
if ( ! update ) {
sb . append ( ") values " ) ;
sep = '(' ;
for ( int i = 0 ; i < f . length ; i + + ) {
String n = f [ i ] . getName ( ) ;
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "java.lang.String" ) ) {
sb . append ( sep ) ;
sep = ',' ;
// handle unset values
if ( f [ i ] . get ( o ) = = null )
sb . append ( "null" ) ;
else
if ( f [ i ] . getType ( ) . getName ( ) . equals ( "java.lang.String" ) | |
f [ i ] . getType ( ) . getName ( ) . equals ( "char" ) ) {
sb . append ( '\'' ) ;
sb . append ( f [ i ] . get ( o ) . toString ( ) ) ;
// don't allow single quotes or newlines in the string
sb . append ( fixString ( f [ i ] . get ( o ) . toString ( ) ) ) ;
sb . append ( '\'' ) ;
} else
sb . append ( f [ i ] . get ( o ) . toString ( ) ) ;
}
sb . append ( ')' ) ;
}
DriverManager . println ( "store: " + sb . toString ( ) ) ;
ResultSet rs = conn . ExecSQL ( sb . toString ( ) ) ;
if ( rs ! = null ) {
rs . close ( ) ;
}
org . postgresql . ResultSet rs = ( org . postgresql . ResultSet ) conn . ExecSQL ( sb . toString ( ) ) ;
// fetch the OID for returning
int oid = 0 ;
if ( hasOID ) {
// set the oid in the object
// If an update u se the existing oid in the object
f [ oidFIELD ] . setInt ( o , oid ) ;
}
else {
String statStr = rs . getStatusString ( ) ;
oid = Integer . parseInt ( statStr . substring ( statStr . indexOf ( " " ) + 1 , statStr . lastIndexOf ( " " ) ) ) ;
}
if ( rs ! = null ) {
rs . close ( ) ;
}
return oid ;
} catch ( IllegalAccessException iae ) {
throw new SQLException ( iae . toString ( ) ) ;
}
}
/ * *
*
* /
private String fixString ( String s ) {
int idx = - 1 ;
// handle null
if ( s = = null )
return "" ;
// if the string has single quotes in it escape them
if ( ( idx = s . indexOf ( "'" ) ) > - 1 ) {
StringBuffer buf = new StringBuffer ( ) ;
StringTokenizer tok = new StringTokenizer ( s , "'" ) ;
// handle quote as 1St charater
if ( idx > 0 ) buf . append ( tok . nextToken ( ) ) ;
while ( tok . hasMoreTokens ( ) )
buf . append ( "\\'" ) . append ( tok . nextToken ( ) ) ;
s = buf . toString ( ) ;
}
// if the string has newlines in it convert them to \n
if ( ( idx = s . indexOf ( "\n" ) ) > - 1 ) {
StringBuffer buf = new StringBuffer ( ) ;
StringTokenizer tok = new StringTokenizer ( s , "\n" ) ;
if ( idx > 0 ) buf . append ( tok . nextToken ( ) ) ;
while ( tok . hasMoreTokens ( ) )
buf . append ( "\\n" ) . append ( tok . nextToken ( ) ) ;
s = buf . toString ( ) ;
}
return s ;
}
/ * *
* This method is not used by the driver , but it creates a table , given
* a Serializable Java Object . It should be used before serializing any
@ -219,7 +327,7 @@ public class Serialize
{
create ( con , o . getClass ( ) ) ;
}
/ * *
* This method is not used by the driver , but it creates a table , given
* a Serializable Java Object . It should be used before serializing any
@ -232,49 +340,50 @@ public class Serialize
{
if ( c . isInterface ( ) )
throw new PSQLException ( "postgresql.serial.interface" ) ;
// See if the table exists
String tableName = toPostgreSQL ( c . getName ( ) ) ;
ResultSet rs = con . ExecSQL ( "select relname from pg_class where relname = '" + tableName + "'" ) ;
if ( ! rs . next ( ) ) {
DriverManager . println ( "found " + rs . getString ( 1 ) ) ;
// DriverManager.println("found "+rs.getString(1));
// No entries returned, so the table doesn't exist
StringBuffer sb = new StringBuffer ( "create table " ) ;
sb . append ( tableName ) ;
char sep = '(' ;
java . lang . reflect . Field [ ] fields = c . getDeclaredFields ( ) ;
// java.lang.reflect.Field[] fields = c.getDeclaredFields();
java . lang . reflect . Field [ ] fields = c . getFields ( ) ;
for ( int i = 0 ; i < fields . length ; i + + ) {
Class type = fields [ i ] . getType ( ) ;
// oid is a special field
if ( ! fields [ i ] . getName ( ) . equals ( "oid" ) ) {
sb . append ( sep ) ;
sb . append ( fields [ i ] . getName ( ) ) ;
sb . append ( ' ' ) ;
sep = ',' ;
if ( type . isArray ( ) ) {
// array handling
} else {
// convert the java type to org.postgresql, recursing if a class
// is found
String n = fields [ i ] . ge tT ype( ) . getName ( ) ;
String n = type . getName ( ) ;
int j = 0 ;
for ( ; j < tp . length & & ! tp [ j ] [ 0 ] . equals ( n ) ; j + + ) ;
if ( j < tp . length )
sb . append ( tp [ j ] [ 1 ] ) ;
else {
create ( con , fields [ i ] . ge tT ype( ) ) ;
create ( con , type ) ;
sb . append ( toPostgreSQL ( n ) ) ;
}
}
}
}
sb . append ( ")" ) ;
// Now create the table
DriverManager . println ( "Serialize.create:" + sb ) ;
con . ExecSQL ( sb . toString ( ) ) ;
@ -283,22 +392,26 @@ public class Serialize
DriverManager . println ( "Serialize.create: table " + tableName + " exists, skipping" ) ;
}
}
// This is used to translate between Java primitives and PostgreSQL types.
private static final String tp [ ] [ ] = {
{ "boolean" , "int1" } ,
// {"boolean", "int1"},
{ "boolean" , "bool" } ,
{ "double" , "float8" } ,
{ "float" , "float4" } ,
{ "int" , "int4" } ,
{ "long" , "int4" } ,
// {"long", "int4"},
{ "long" , "int8" } ,
{ "short" , "int2" } ,
{ "java.lang.String" , "text" } ,
{ "java.lang.Integer" , "int4" } ,
{ "java.lang.Float" , "float4" } ,
{ "java.lang.Double" , "float8" } ,
{ "java.lang.Short" , "int2" }
{ "java.lang.Short" , "int2" } ,
{ "char" , "char" } ,
{ "byte" , "int2" }
} ;
/ * *
* This converts a Java Class name to a org . postgresql table , by replacing . with
* _ < p >
@ -314,17 +427,25 @@ public class Serialize
public static String toPostgreSQL ( String name ) throws SQLException
{
name = name . toLowerCase ( ) ;
if ( name . indexOf ( "_" ) > - 1 )
throw new PSQLException ( "postgresql.serial.underscore" ) ;
if ( name . length ( ) > 32 )
throw new PSQLException ( "postgresql.serial.namelength" , name , new Integer ( name . length ( ) ) ) ;
// Postgres table names can only be 32 character long
// If the full class name with package is too long
// then just use the class name. If the class name is
// too long throw an exception.
if ( name . length ( ) > 32 ) {
name = name . substring ( name . lastIndexOf ( "." ) + 1 ) ;
if ( name . length ( ) > 32 )
throw new PSQLException ( "postgresql.serial.namelength" , name , new Integer ( name . length ( ) ) ) ;
}
return name . replace ( '.' , '_' ) ;
}
/ * *
* This converts a org . postgresql table to a Java Class name , by replacing _ with
* . < p >
@ -338,5 +459,5 @@ public class Serialize
name = name . toLowerCase ( ) ;
return name . replace ( '_' , '.' ) ;
}
}