Serialize.java
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:10k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. package postgresql.util;
  2. import java.io.*;
  3. import java.lang.*;
  4. import java.lang.reflect.*;
  5. import java.net.*;
  6. import java.util.*;
  7. import java.sql.*;
  8. /**
  9.  * This class uses PostgreSQL's object oriented features to store Java Objects.
  10.  *
  11.  * It does this by mapping a Java Class name to a table in the database. Each
  12.  * entry in this new table then represents a Serialized instance of this
  13.  * class. As each entry has an OID (Object IDentifier), this OID can be
  14.  * included in another table.
  15.  *
  16.  * This is too complex to show here, and will be documented in the main
  17.  * documents in more detail.
  18.  *
  19.  */
  20. public class Serialize
  21. {
  22.   // This is the connection that the instance refers to
  23.   protected postgresql.Connection conn;
  24.   
  25.   // This is the table name
  26.   protected String tableName;
  27.   
  28.   // This is the class name
  29.   protected String className;
  30.   
  31.   // This is the Class for this serialzed object
  32.   protected Class ourClass;
  33.   
  34.   /**
  35.    * This creates an instance that can be used to serialize or deserialize
  36.    * a Java object from a PostgreSQL table.
  37.    */
  38.   public Serialize(postgresql.Connection c,String type) throws SQLException
  39.   {
  40.     try {
  41.       conn = c;
  42.       tableName = type.toLowerCase();
  43.       className = toClassName(type);
  44.       ourClass = Class.forName(className);
  45.     } catch(ClassNotFoundException cnfe) {
  46.       throw new PSQLException("postgresql.serial.noclass",type);
  47.     }
  48.     
  49.     // Second check, the type must be a table
  50.     boolean status = false;
  51.     ResultSet rs = conn.ExecSQL("select typname from pg_type,pg_class where typname=relname and typname='"+type+"'");
  52.     if(rs!=null) {
  53.       if(rs.next())
  54. status=true;
  55.       rs.close();
  56.     }
  57.     // This should never occur, as postgresql has it's own internal checks
  58.     if(!status)
  59.       throw new PSQLException("postgresql.serial.table",type);
  60.     
  61.     // Finally cache the fields within the table
  62.   }
  63.   
  64.   /**
  65.    * This fetches an object from a table, given it's OID
  66.    * @param oid The oid of the object
  67.    * @return Object relating to oid
  68.    * @exception SQLException on error
  69.    */
  70.   public Object fetch(int oid) throws SQLException
  71.   {
  72.     try {
  73.       Object obj = ourClass.newInstance();
  74.       
  75.       // NB: we use java.lang.reflect here to prevent confusion with
  76.       // the postgresql.Field
  77.       java.lang.reflect.Field f[] = ourClass.getDeclaredFields();
  78.       boolean hasOID=false;
  79.       int oidFIELD=-1;
  80.       StringBuffer sb = new StringBuffer("select");
  81.       char sep=' ';
  82.       for(int i=0;i<f.length;i++) {
  83. String n = f[i].getName();
  84. if(n.equals("oid")) {
  85.   hasOID=true;
  86.   oidFIELD=i;
  87. }
  88. sb.append(sep);
  89. sb.append(n);
  90. sep=',';
  91.       }
  92.       sb.append(" from ");
  93.       sb.append(tableName);
  94.       sb.append(" where oid=");
  95.       sb.append(oid);
  96.       
  97.       DriverManager.println("store: "+sb.toString());
  98.       ResultSet rs = conn.ExecSQL(sb.toString());
  99.       if(rs!=null) {
  100. if(rs.next()) {
  101.   for(int i=0;i<f.length;i++) {
  102.     f[i].set(obj,rs.getObject(i+1));
  103.   }
  104. }
  105. rs.close();
  106.       } else
  107.        throw new PSQLException("postgresql.unexpected");
  108.       return obj;
  109.     } catch(IllegalAccessException iae) {
  110.       throw new SQLException(iae.toString());
  111.     } catch(InstantiationException ie) {
  112.       throw new SQLException(ie.toString());
  113.     }
  114.   }
  115.   
  116.   /**
  117.    * This stores an object into a table, returning it's OID.<p>
  118.    *
  119.    * If the object has an int called OID, and it is > 0, then
  120.    * that value is used for the OID, and the table will be updated.
  121.    * If the value of OID is 0, then a new row will be created, and the
  122.    * value of OID will be set in the object. This enables an object's
  123.    * value in the database to be updateable.
  124.    *
  125.    * If the object has no int called OID, then the object is stored. However
  126.    * if the object is later retrieved, amended and stored again, it's new
  127.    * state will be appended to the table, and will not overwrite the old
  128.    * entries.
  129.    *
  130.    * @param o Object to store (must implement Serializable)
  131.    * @return oid of stored object
  132.    * @exception SQLException on error
  133.    */
  134.   public int store(Object o) throws SQLException
  135.   {
  136.     try {
  137.       // NB: we use java.lang.reflect here to prevent confusion with
  138.       // the postgresql.Field
  139.       java.lang.reflect.Field f[] = ourClass.getDeclaredFields();
  140.       boolean hasOID=false;
  141.       int oidFIELD=-1;
  142.       boolean update=false;
  143.       
  144.       // Find out if we have an oid value
  145.       for(int i=0;i<f.length;i++) {
  146. String n = f[i].getName();
  147. if(n.equals("oid")) {
  148.   hasOID=true;
  149.   oidFIELD=i;
  150.   
  151.   // We are an update if oid != 0
  152.   update = f[i].getInt(o)>0;
  153. }
  154.       }
  155.       
  156.       StringBuffer sb = new StringBuffer(update?"update "+tableName+" set":"insert into "+tableName+" values ");
  157.       char sep=update?' ':'(';
  158.       for(int i=0;i<f.length;i++) {
  159. String n = f[i].getName();
  160. sb.append(sep);
  161. sb.append(n);
  162. sep=',';
  163. if(update) {
  164.   sb.append('=');
  165.   if(f[i].getType().getName().equals("java.lang.String")) {
  166.     sb.append(''');
  167.     sb.append(f[i].get(o).toString());
  168.     sb.append(''');
  169.   } else
  170.     sb.append(f[i].get(o).toString());
  171. }
  172.       }
  173.       
  174.       if(!update) {
  175. sb.append(") values ");
  176. sep='(';
  177. for(int i=0;i<f.length;i++) {
  178.   String n = f[i].getName();
  179.   if(f[i].getType().getName().equals("java.lang.String")) {
  180.     sb.append(''');
  181.     sb.append(f[i].get(o).toString());
  182.     sb.append(''');
  183.   } else
  184.     sb.append(f[i].get(o).toString());
  185. }
  186. sb.append(')');
  187.       }
  188.       
  189.       DriverManager.println("store: "+sb.toString());
  190.       ResultSet rs = conn.ExecSQL(sb.toString());
  191.       if(rs!=null) {
  192. rs.close();
  193.       }
  194.       
  195.       // fetch the OID for returning
  196.       int oid=0;
  197.       if(hasOID) {
  198. // set the oid in the object
  199. f[oidFIELD].setInt(o,oid);
  200.       }
  201.       return oid;
  202.       
  203.     } catch(IllegalAccessException iae) {
  204.       throw new SQLException(iae.toString());
  205.     }
  206.   }
  207.   
  208.   /**
  209.    * This method is not used by the driver, but it creates a table, given
  210.    * a Serializable Java Object. It should be used before serializing any
  211.    * objects.
  212.    * @param c Connection to database
  213.    * @param o Object to base table on
  214.    * @exception SQLException on error
  215.    */
  216.   public static void create(postgresql.Connection con,Object o) throws SQLException
  217.   {
  218.     create(con,o.getClass());
  219.   }
  220.   
  221.   /**
  222.    * This method is not used by the driver, but it creates a table, given
  223.    * a Serializable Java Object. It should be used before serializing any
  224.    * objects.
  225.    * @param c Connection to database
  226.    * @param o Class to base table on
  227.    * @exception SQLException on error
  228.    */
  229.   public static void create(postgresql.Connection con,Class c) throws SQLException
  230.   {
  231.     if(c.isInterface())
  232.       throw new PSQLException("postgresql.serial.interface");
  233.     
  234.     // See if the table exists
  235.     String tableName = toPostgreSQL(c.getName());
  236.     
  237.     ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '"+tableName+"'");
  238.     if(!rs.next()) {
  239.       DriverManager.println("found "+rs.getString(1));
  240.       // No entries returned, so the table doesn't exist
  241.       
  242.       StringBuffer sb = new StringBuffer("create table ");
  243.       sb.append(tableName);
  244.       char sep='(';
  245.       
  246.       java.lang.reflect.Field[] fields = c.getDeclaredFields();
  247.       for(int i=0;i<fields.length;i++) {
  248. Class type = fields[i].getType();
  249. // oid is a special field
  250. if(!fields[i].getName().equals("oid")) {
  251.   sb.append(sep);
  252.   sb.append(fields[i].getName());
  253.   sb.append(' ');
  254.   sep=',';
  255.   
  256.   if(type.isArray()) {
  257.     // array handling
  258.   } else {
  259.     // convert the java type to postgresql, recursing if a class
  260.     // is found
  261.     String n = fields[i].getType().getName();
  262.     int j=0;
  263.     for(;j<tp.length && !tp[j][0].equals(n);j++);
  264.     if(j<tp.length)
  265.       sb.append(tp[j][1]);
  266.     else {
  267.       create(con,fields[i].getType());
  268.       sb.append(toPostgreSQL(n));
  269.     }
  270.   }
  271. }
  272.       }
  273.       sb.append(")");
  274.       
  275.       // Now create the table
  276.       DriverManager.println("Serialize.create:"+sb);
  277.       con.ExecSQL(sb.toString());
  278.       rs.close();
  279.     } else {
  280.       DriverManager.println("Serialize.create: table "+tableName+" exists, skipping");
  281.     }
  282.   }
  283.   
  284.   // This is used to translate between Java primitives and PostgreSQL types.
  285.   private static final String tp[][] = {
  286.     {"boolean", "int1"},
  287.     {"double", "float8"},
  288.     {"float", "float4"},
  289.     {"int", "int4"},
  290.     {"long", "int4"},
  291.     {"short", "int2"},
  292.     {"java.lang.String", "text"},
  293.     {"java.lang.Integer", "int4"},
  294.     {"java.lang.Float", "float4"},
  295.     {"java.lang.Double", "float8"},
  296.     {"java.lang.Short", "int2"}
  297.   };
  298.   
  299.   /**
  300.    * This converts a Java Class name to a postgresql table, by replacing . with
  301.    * _<p>
  302.    *
  303.    * Because of this, a Class name may not have _ in the name.<p>
  304.    * Another limitation, is that the entire class name (including packages)
  305.    * cannot be longer than 32 characters (a limit forced by PostgreSQL).
  306.    *
  307.    * @param name Class name
  308.    * @return PostgreSQL table name
  309.    * @exception SQLException on error
  310.    */
  311.   public static String toPostgreSQL(String name) throws SQLException
  312.   {
  313.     name = name.toLowerCase();
  314.     
  315.     if(name.indexOf("_")>-1)
  316.       throw new PSQLException("postgresql.serial.underscore");
  317.     
  318.     if(name.length()>32)
  319.       throw new PSQLException("postgresql.serial.namelength",name,new Integer(name.length()));
  320.     
  321.     return name.replace('.','_');
  322.   }
  323.   
  324.   
  325.   /**
  326.    * This converts a postgresql table to a Java Class name, by replacing _ with
  327.    * .<p>
  328.    *
  329.    * @param name PostgreSQL table name
  330.    * @return Class name
  331.    * @exception SQLException on error
  332.    */
  333.   public static String toClassName(String name) throws SQLException
  334.   {
  335.     name = name.toLowerCase();
  336.     return name.replace('_','.');
  337.   }
  338.   
  339. }