There are a number of changes. The main ones are:

return oid on insert
 handle all primitive data types
 handle single quotes and newlines in Strings
 handle null variables
 deal with non public and final variables (not very
   well, though)

Ken K
This commit is contained in:
Bruce Momjian 2001-05-24 15:48:32 +00:00
parent 23287c106d
commit 6f101c806b

View File

@ -23,16 +23,16 @@ public class Serialize
{ {
// This is the connection that the instance refers to // This is the connection that the instance refers to
protected org.postgresql.Connection conn; protected org.postgresql.Connection conn;
// This is the table name // This is the table name
protected String tableName; protected String tableName;
// This is the class name // This is the class name
protected String className; protected String className;
// This is the Class for this serialzed object // This is the Class for this serialzed object
protected Class ourClass; protected Class ourClass;
/** /**
* This creates an instance that can be used to serialize or deserialize * This creates an instance that can be used to serialize or deserialize
* a Java object from a PostgreSQL table. * a Java object from a PostgreSQL table.
@ -41,16 +41,16 @@ public class Serialize
{ {
try { try {
conn = c; conn = c;
tableName = type.toLowerCase(); tableName = toPostgreSQL(type);
className = toClassName(type); className = type;
ourClass = Class.forName(className); ourClass = Class.forName(className);
} catch(ClassNotFoundException cnfe) { } catch(ClassNotFoundException cnfe) {
throw new PSQLException("postgresql.serial.noclass",type); throw new PSQLException("postgresql.serial.noclass",type);
} }
// Second check, the type must be a table // Second check, the type must be a table
boolean status = false; 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!=null) {
if(rs.next()) if(rs.next())
status=true; status=true;
@ -59,10 +59,26 @@ public class Serialize
// This should never occur, as org.postgresql has it's own internal checks // This should never occur, as org.postgresql has it's own internal checks
if(!status) if(!status)
throw new PSQLException("postgresql.serial.table",type); throw new PSQLException("postgresql.serial.table",type);
// Finally cache the fields within the table // 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 * This fetches an object from a table, given it's OID
* @param oid The oid of the object * @param oid The oid of the object
@ -73,14 +89,20 @@ public class Serialize
{ {
try { try {
Object obj = ourClass.newInstance(); Object obj = ourClass.newInstance();
// NB: we use java.lang.reflect here to prevent confusion with // NB: we use java.lang.reflect here to prevent confusion with
// the org.postgresql.Field // 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; boolean hasOID=false;
int oidFIELD=-1; int oidFIELD=-1;
StringBuffer sb = new StringBuffer("select"); StringBuffer sb = new StringBuffer("select");
char sep=' '; 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++) { for(int i=0;i<f.length;i++) {
String n = f[i].getName(); String n = f[i].getName();
if(n.equals("oid")) { if(n.equals("oid")) {
@ -95,13 +117,37 @@ public class Serialize
sb.append(tableName); sb.append(tableName);
sb.append(" where oid="); sb.append(" where oid=");
sb.append(oid); sb.append(oid);
DriverManager.println("store: "+sb.toString()); DriverManager.println("store: "+sb.toString());
ResultSet rs = conn.ExecSQL(sb.toString()); ResultSet rs = conn.ExecSQL(sb.toString());
if(rs!=null) { if(rs!=null) {
if(rs.next()) { if(rs.next()) {
for(int i=0;i<f.length;i++) { 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(); rs.close();
@ -114,7 +160,7 @@ public class Serialize
throw new SQLException(ie.toString()); throw new SQLException(ie.toString());
} }
} }
/** /**
* This stores an object into a table, returning it's OID.<p> * This stores an object into a table, returning it's OID.<p>
* *
@ -138,24 +184,27 @@ public class Serialize
try { try {
// NB: we use java.lang.reflect here to prevent confusion with // NB: we use java.lang.reflect here to prevent confusion with
// the org.postgresql.Field // 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; boolean hasOID=false;
int oidFIELD=-1; int oidFIELD=-1;
boolean update=false; boolean update=false;
// Find out if we have an oid value // Find out if we have an oid value
for(int i=0;i<f.length;i++) { for(int i=0;i<f.length;i++) {
String n = f[i].getName(); String n = f[i].getName();
if(n.equals("oid")) { if(n.equals("oid")) {
hasOID=true; hasOID=true;
oidFIELD=i; oidFIELD=i;
// We are an update if oid != 0 // We are an update if oid != 0
update = f[i].getInt(o)>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?' ':'('; char sep=update?' ':'(';
for(int i=0;i<f.length;i++) { for(int i=0;i<f.length;i++) {
String n = f[i].getName(); String n = f[i].getName();
@ -164,49 +213,108 @@ public class Serialize
sep=','; sep=',';
if(update) { if(update) {
sb.append('='); 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('\'');
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('\''); sb.append('\'');
} else } else
sb.append(f[i].get(o).toString()); sb.append(f[i].get(o).toString());
} }
} }
if(!update) { if(!update) {
sb.append(") values "); sb.append(") values ");
sep='('; sep='(';
for(int i=0;i<f.length;i++) { for(int i=0;i<f.length;i++) {
String n = f[i].getName(); sb.append(sep);
if(f[i].getType().getName().equals("java.lang.String")) { 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('\'');
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('\''); sb.append('\'');
} else } else
sb.append(f[i].get(o).toString()); sb.append(f[i].get(o).toString());
} }
sb.append(')'); sb.append(')');
} }
DriverManager.println("store: "+sb.toString()); DriverManager.println("store: "+sb.toString());
ResultSet rs = conn.ExecSQL(sb.toString()); org.postgresql.ResultSet rs = (org.postgresql.ResultSet)conn.ExecSQL(sb.toString());
if(rs!=null) {
rs.close();
}
// fetch the OID for returning // fetch the OID for returning
int oid=0; int oid=0;
if(hasOID) { if(hasOID) {
// set the oid in the object // If an update use the existing oid in the object
f[oidFIELD].setInt(o,oid); 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; return oid;
} catch(IllegalAccessException iae) { } catch(IllegalAccessException iae) {
throw new SQLException(iae.toString()); 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 * 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 * a Serializable Java Object. It should be used before serializing any
@ -219,7 +327,7 @@ public class Serialize
{ {
create(con,o.getClass()); create(con,o.getClass());
} }
/** /**
* This method is not used by the driver, but it creates a table, given * 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 * a Serializable Java Object. It should be used before serializing any
@ -232,49 +340,50 @@ public class Serialize
{ {
if(c.isInterface()) if(c.isInterface())
throw new PSQLException("postgresql.serial.interface"); throw new PSQLException("postgresql.serial.interface");
// See if the table exists // See if the table exists
String tableName = toPostgreSQL(c.getName()); String tableName = toPostgreSQL(c.getName());
ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '"+tableName+"'"); ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '"+tableName+"'");
if(!rs.next()) { if(!rs.next()) {
DriverManager.println("found "+rs.getString(1)); // DriverManager.println("found "+rs.getString(1));
// No entries returned, so the table doesn't exist // No entries returned, so the table doesn't exist
StringBuffer sb = new StringBuffer("create table "); StringBuffer sb = new StringBuffer("create table ");
sb.append(tableName); sb.append(tableName);
char sep='('; 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++) { for(int i=0;i<fields.length;i++) {
Class type = fields[i].getType(); Class type = fields[i].getType();
// oid is a special field // oid is a special field
if(!fields[i].getName().equals("oid")) { if(!fields[i].getName().equals("oid")) {
sb.append(sep); sb.append(sep);
sb.append(fields[i].getName()); sb.append(fields[i].getName());
sb.append(' '); sb.append(' ');
sep=','; sep=',';
if(type.isArray()) { if(type.isArray()) {
// array handling // array handling
} else { } else {
// convert the java type to org.postgresql, recursing if a class // convert the java type to org.postgresql, recursing if a class
// is found // is found
String n = fields[i].getType().getName(); String n = type.getName();
int j=0; int j=0;
for(;j<tp.length && !tp[j][0].equals(n);j++); for(;j<tp.length && !tp[j][0].equals(n);j++);
if(j<tp.length) if(j<tp.length)
sb.append(tp[j][1]); sb.append(tp[j][1]);
else { else {
create(con,fields[i].getType()); create(con, type);
sb.append(toPostgreSQL(n)); sb.append(toPostgreSQL(n));
} }
} }
} }
} }
sb.append(")"); sb.append(")");
// Now create the table // Now create the table
DriverManager.println("Serialize.create:"+sb); DriverManager.println("Serialize.create:"+sb);
con.ExecSQL(sb.toString()); con.ExecSQL(sb.toString());
@ -283,22 +392,26 @@ public class Serialize
DriverManager.println("Serialize.create: table "+tableName+" exists, skipping"); DriverManager.println("Serialize.create: table "+tableName+" exists, skipping");
} }
} }
// This is used to translate between Java primitives and PostgreSQL types. // This is used to translate between Java primitives and PostgreSQL types.
private static final String tp[][] = { private static final String tp[][] = {
{"boolean", "int1"}, // {"boolean", "int1"},
{"boolean", "bool"},
{"double", "float8"}, {"double", "float8"},
{"float", "float4"}, {"float", "float4"},
{"int", "int4"}, {"int", "int4"},
{"long", "int4"}, // {"long", "int4"},
{"long", "int8"},
{"short", "int2"}, {"short", "int2"},
{"java.lang.String", "text"}, {"java.lang.String", "text"},
{"java.lang.Integer", "int4"}, {"java.lang.Integer", "int4"},
{"java.lang.Float", "float4"}, {"java.lang.Float", "float4"},
{"java.lang.Double", "float8"}, {"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 * This converts a Java Class name to a org.postgresql table, by replacing . with
* _<p> * _<p>
@ -314,17 +427,25 @@ public class Serialize
public static String toPostgreSQL(String name) throws SQLException public static String toPostgreSQL(String name) throws SQLException
{ {
name = name.toLowerCase(); name = name.toLowerCase();
if(name.indexOf("_")>-1) if(name.indexOf("_")>-1)
throw new PSQLException("postgresql.serial.underscore"); throw new PSQLException("postgresql.serial.underscore");
if(name.length()>32) // Postgres table names can only be 32 character long
throw new PSQLException("postgresql.serial.namelength",name,new Integer(name.length())); // 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('.','_'); return name.replace('.','_');
} }
/** /**
* This converts a org.postgresql table to a Java Class name, by replacing _ with * This converts a org.postgresql table to a Java Class name, by replacing _ with
* .<p> * .<p>
@ -338,5 +459,5 @@ public class Serialize
name = name.toLowerCase(); name = name.toLowerCase();
return name.replace('_','.'); return name.replace('_','.');
} }
} }