Imported a slightly modified version of JORA.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@39 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-02-13 05:46:56 +00:00
parent 8061ef0939
commit c2db44dd77
11 changed files with 2099 additions and 0 deletions
@@ -0,0 +1,240 @@
//-< Cursor.java >---------------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 20-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Cursor for navigation thru result of SELECT statement
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
import java.util.*;
import java.lang.reflect.*;
import java.sql.*;
/** Cursor is used for successive access to records fetched by SELECT
* statement. As far as records can be retrived from several derived tables
* (polymorphic form of select), this class can issue several requests
* to database. Cursor also provides methods for updating/deleting current
* record.
*/
public class Cursor {
/**
* A cursor is initially positioned before its first row; the
* first call to next makes the first row the current row; the
* second call makes the second row the current row, etc.
*
* <P>If an input stream from the previous row is open, it is
* implicitly closed. The ResultSet's warning chain is cleared
* when a new row is read.
*
* @return object constructed from fetched record or null if there
* are no more rows
*/
public Object next()
throws SQLException
{
// try {
do {
if (result == null) {
if (table.isAbstract) {
table = table.derived;
continue;
}
if (qbeObject != null) {
String sql = "select " + table.listOfFields
+ " from " + table.name + " " + query;
PreparedStatement qbeStmt;
synchronized(session.preparedStmtHash) {
Object s = session.preparedStmtHash.get(sql);
if (s == null) {
qbeStmt=session.connection.prepareStatement(sql);
session.preparedStmtHash.put(sql, qbeStmt);
} else {
qbeStmt = (PreparedStatement)s;
}
}
synchronized(qbeStmt) {
table.bindQueryVariables(qbeStmt, qbeObject);
result = qbeStmt.executeQuery();
qbeStmt.clearParameters();
}
} else {
if (stmt == null) {
stmt = session.connection.createStatement();
}
String sql = "select " + table.listOfFields
+ " from " + table.name + " " + query;
result = stmt.executeQuery(sql);
}
}
if (result.next()) {
return currObject = table.load(result);
}
result.close();
result = null;
currObject = null;
table = table.derived;
} while (--nTables != 0);
if (stmt != null) {
stmt.close();
}
// }
// catch (SQLException ex) { session.handleSQLException(ex); }
return null;
}
/** Update current record pointed by cursor. This method can be called
* only after next() method, which returns non-null object. This objects
* is used to update current record fields.<P>
*
* If you are going to update or delete selected records, you should add
* "for update" clause to select statement. So parameter of
* <CODE>jora.Table.select()</CODE> statement should contain "for update"
* clause:
* <CODE>record.table.Select("where name='xyz' for update");</CODE><P>
*
* <I><B>Attention!</I></B>
* Not all database drivers support update operation with
* cursor. This method will not work with such database drivers.
*/
public void update()
throws SQLException
{
if (currObject == null) {
throw new NoCurrentObjectError();
}
// try {
table.updateVariables(result, currObject);
// }
// catch (SQLException ex) { session.handleSQLException(ex); }
}
/** Delete current record pointed by cursor. This method can be called
* only after next() method, which returns non-null object.<P>
*
* If you are going to update or delete selected records, you should add
* "for update" clause to select statement. So parameter of
* <CODE>jora.Table.select()</CODE> statement should contain "for update"
* clause:
* <CODE>record.table.Select("where name='xyz' for update");</CODE><P>
*
* <I><B>Attention!</I></B>
* Not all database drivers support delete operation with cursor.
* This method will not work with such database drivers.
*/
public void delete()
throws SQLException
{
if (currObject == null) {
throw new NoCurrentObjectError();
}
// try {
result.deleteRow();
// }
// catch (SQLException ex) { session.handleSQLException(ex); }
}
/** Extracts no more than <I>maxElements</I> records from database and
* store them into array. It is possible to extract rest records
* by successive next() or toArray() calls. Selected objects should
* have now components of InputStream, Blob or Clob type, because
* their data will be not available after fetching next record.
*
* @param maxElements limitation for result array size (and also for number
* of fetched records)
* @return Array with objects constructed from fetched records.
*/
public Object[] toArray(int maxElements)
throws SQLException
{
Vector v = new Vector(maxElements < 100 ? maxElements : 100);
Object o;
while (--maxElements >= 0 && (o = next()) != null) {
v.addElement(o);
}
Object[] a = new Object[v.size()];
v.copyInto(a);
return a;
}
/** Store all objects returned by SELECT query into array of Object.
* Selected objects should have now components of InputStream, Blob or
* Clob type, because their data will be not available after fetching
* next record.
*
* @return Array with objects constructed from fetched records.
*/
public Object[] toArray()
throws SQLException
{ return toArray(Integer.MAX_VALUE); }
/** Extracts no more than <I>maxElements</I> records from database and
* store them into array. It is possible to extract rest records
* by successive next() or toArray() calls. Selected objects should
* have now components of InputStream, Blob or Clob type, because
* their data will be not available after fetching next record.
*
* @param maxElements limitation for result array size (and also for number
* of fetched records)
* @return List with objects constructed from fetched records.
*/
public List toArrayList(int maxElements)
throws SQLException
{
ArrayList al = new ArrayList(maxElements < 100 ? maxElements : 100);
Object o;
while (--maxElements >= 0 && (o = next()) != null) {
al.add(o);
}
return al;
}
/** Store all objects returned by SELECT query into a list of Object.
* Selected objects should have now components of InputStream, Blob or
* Clob type, because their data will be not available after fetching
* next record.
*
* @return Array with objects constructed from fetched records.
*/
public List toArrayList()
throws SQLException
{ return toArrayList(Integer.MAX_VALUE); }
// Internals
protected Cursor(Table table, Session session, int nTables, String query) {
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
this.table = table;
this.session = session;
this.nTables = nTables;
this.query = query;
}
protected Cursor(Table table, Session session, int nTables, Object obj) {
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
this.table = table;
this.session = session;
this.nTables = nTables;
qbeObject = obj;
query = table.buildQueryList(obj);
stmt = null;
}
private Table table;
private Session session;
private int nTables;
private ResultSet result;
private String query;
private Statement stmt;
private Object currObject;
private Object qbeObject;
}
@@ -0,0 +1,26 @@
//-< DataTransferError.java >----------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 19-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Exception raised when error is happed during data transfer between
// program and database server
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
/** This error is raised when error is happened during data transfer
* between program and database server (for example IOException was thrown
* while operation with InputStream field)
*/
public class DataTransferError extends java.lang.Error {
DataTransferError() {
super("Database data transfer error");
}
DataTransferError(Exception ex) {
super(ex.getMessage());
}
}
@@ -0,0 +1,437 @@
//-< FieldDescriptor.java >------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 25-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Table field descriptor
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
import java.sql.*;
import java.math.*;
import java.lang.reflect.*;
class FieldDescriptor {
protected int inType; // type tag for field input (see constants below)
protected int outType; // type tag for field output
protected int scale; // scale for tDecimal type,
protected String name; // full (compound) name of component
protected Field field; // field info from java.lang.reflect
protected Constructor constructor; // constructor of object component
protected static final int t_byte = 0;
protected static final int t_short = 1;
protected static final int t_int = 2;
protected static final int t_long = 3;
protected static final int t_float = 4;
protected static final int t_double = 5;
protected static final int t_boolean = 6;
protected static final int tByte = 7;
protected static final int tShort = 8;
protected static final int tInteger = 9;
protected static final int tLong = 10;
protected static final int tFloat = 11;
protected static final int tDouble = 12;
protected static final int tBoolean = 13;
protected static final int tDecimal = 14;
protected static final int tString = 15;
protected static final int tBytes = 16;
protected static final int tDate = 17;
protected static final int tTime = 18;
protected static final int tTimestamp = 19;
protected static final int tStream = 20;
protected static final int tBlob = 21;
protected static final int tClob = 22;
protected static final int tAsString = 23;
protected static final int tClosure = 24;
protected static final int tCompound = 25;
protected static final int[] sqlTypeMapping = {
Types.INTEGER, // t_byte
Types.INTEGER, // t_short
Types.INTEGER, // t_int
Types.BIGINT, // t_long
Types.FLOAT, // t_float
Types.DOUBLE, // t_double
Types.BIT, // t_boolean
Types.INTEGER, // tByte
Types.INTEGER, // tShort
Types.INTEGER, // tInteger
Types.BIGINT, // tLong
Types.FLOAT, // tFloat
Types.DOUBLE, // tDouble
Types.BIT, // tBoolean
Types.NUMERIC, // tDecimal
Types.VARCHAR, // tString
Types.VARBINARY,// tBytes
Types.DATE, // tDate
Types.TIME, // tTime
Types.TIMESTAMP, // tTimestamp
Types.LONGVARBINARY, // tStream
Types.LONGVARBINARY, // tBlob
Types.LONGVARCHAR, // tClob
Types.VARCHAR, // tAsString
Types.LONGVARBINARY // tClosure
};
protected FieldDescriptor(Field field, String name) {
this.name = name;
this.field = field;
this.scale = -1;
}
protected final boolean isAtomic() { return inType < tClosure; }
protected final boolean isCompound() { return inType >= tCompound; }
protected final boolean isBuiltin() { return inType <= t_boolean; }
protected final boolean bindVariable(PreparedStatement pstmt,
Object obj, int column)
throws SQLException
{
try {
switch (outType) {
case t_byte:
pstmt.setByte(column, field.getByte(obj));
break;
case t_short:
pstmt.setShort(column, field.getShort(obj));
break;
case t_int:
pstmt.setInt(column, field.getInt(obj));
break;
case t_long:
pstmt.setLong(column, field.getLong(obj));
break;
case t_float:
pstmt.setFloat(column, field.getFloat(obj));
break;
case t_double:
pstmt.setDouble(column, field.getDouble(obj));
break;
case t_boolean:
pstmt.setBoolean(column, field.getBoolean(obj));
break;
case tByte:
pstmt.setByte(column, ((Byte)field.get(obj)).byteValue());
break;
case tShort:
pstmt.setShort(column, ((Short)field.get(obj)).shortValue());
break;
case tInteger:
pstmt.setInt(column, ((Integer)field.get(obj)).intValue());
break;
case tLong:
pstmt.setLong(column, ((Long)field.get(obj)).longValue());
break;
case tFloat:
pstmt.setFloat(column, ((Float)field.get(obj)).floatValue());
break;
case tDouble:
pstmt.setDouble(column,((Double)field.get(obj)).doubleValue());
break;
case tBoolean:
pstmt.setBoolean(column,
((Boolean)field.get(obj)).booleanValue());
break;
case tDecimal:
pstmt.setBigDecimal(column, (BigDecimal)field.get(obj));
break;
case tString:
pstmt.setString(column, (String)field.get(obj));
break;
case tBytes:
pstmt.setBytes(column, (byte[])field.get(obj));
break;
case tDate:
pstmt.setDate(column, (java.sql.Date)field.get(obj));
break;
case tTime:
pstmt.setTime(column, (java.sql.Time)field.get(obj));
break;
case tTimestamp:
pstmt.setTimestamp(column, (java.sql.Timestamp)field.get(obj));
break;
case tStream:
java.io.InputStream in = (java.io.InputStream)field.get(obj);
pstmt.setBinaryStream(column, in, in.available());
break;
case tBlob:
pstmt.setBlob(column, (Blob)field.get(obj));
break;
case tClob:
pstmt.setClob(column, (Clob)field.get(obj));
break;
case tAsString:
pstmt.setString(column, field.get(obj).toString());
break;
case tClosure:
// There is no reason to use piped streams because
// we need to pass total number of bytes to JDBC driver
java.io.ByteArrayOutputStream out =
new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream clu =
new java.io.ObjectOutputStream(out);
clu.writeObject(field.get(obj));
clu.close();
pstmt.setBytes(column, out.toByteArray());
break;
default:
return false;
}
} catch(SQLException ex) {
if (outType != tClosure && outType != tAsString) {
outType = tAsString;
return bindVariable(pstmt, obj, column);
} else {
throw ex;
}
} catch(IllegalAccessException ex) {
ex.printStackTrace();
throw new IllegalAccessError();
} catch(java.io.IOException ex) {
throw new DataTransferError(ex);
}
return true;
}
protected final boolean updateVariable(ResultSet result,
Object obj, int column)
throws SQLException
{
try {
switch (outType) {
case t_byte:
result.updateByte(column, field.getByte(obj));
break;
case t_short:
result.updateShort(column, field.getShort(obj));
break;
case t_int:
result.updateInt(column, field.getInt(obj));
break;
case t_long:
result.updateLong(column, field.getLong(obj));
break;
case t_float:
result.updateFloat(column, field.getFloat(obj));
break;
case t_double:
result.updateDouble(column, field.getDouble(obj));
break;
case t_boolean:
result.updateBoolean(column, field.getBoolean(obj));
break;
case tByte:
result.updateByte(column, ((Byte)field.get(obj)).byteValue());
break;
case tShort:
result.updateShort(column,
((Short)field.get(obj)).shortValue());
break;
case tInteger:
result.updateInt(column, ((Integer)field.get(obj)).intValue());
break;
case tLong:
result.updateLong(column, ((Long)field.get(obj)).longValue());
break;
case tFloat:
result.updateFloat(column,
((Float)field.get(obj)).floatValue());
break;
case tDouble:
result.updateDouble(column,
((Double)field.get(obj)).doubleValue());
break;
case tBoolean:
result.updateBoolean(column,
((Boolean)field.get(obj)).booleanValue());
break;
case tDecimal:
result.updateBigDecimal(column, (BigDecimal)field.get(obj));
break;
case tString:
result.updateString(column, (String)field.get(obj));
break;
case tBytes:
result.updateBytes(column, (byte[])field.get(obj));
break;
case tDate:
result.updateDate(column, (java.sql.Date)field.get(obj));
break;
case tTime:
result.updateTime(column, (java.sql.Time)field.get(obj));
break;
case tTimestamp:
result.updateTimestamp(column,
(java.sql.Timestamp)field.get(obj));
break;
case tStream:
java.io.InputStream in = (java.io.InputStream)field.get(obj);
result.updateBinaryStream(column, in, in.available());
break;
case tBlob:
Blob blob = (Blob)field.get(obj);
result.updateBinaryStream(column,
blob.getBinaryStream(),
(int)blob.length());
break;
case tClob:
Clob clob = (Clob)field.get(obj);
result.updateCharacterStream(column,
clob.getCharacterStream(),
(int)clob.length());
break;
case tAsString:
result.updateString(column, field.get(obj).toString());
break;
case tClosure:
// There is no reason to use piped streams because
// we need to pass total number of bytes to JDBC driver
java.io.ByteArrayOutputStream out =
new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream clu =
new java.io.ObjectOutputStream(out);
clu.writeObject(field.get(obj));
clu.close();
result.updateBytes(column, out.toByteArray());
break;
default:
return false;
}
} catch(SQLException ex) {
if (outType != tClosure && outType != tAsString) {
outType = tAsString;
return updateVariable(result, obj, column);
} else {
throw ex;
}
} catch(IllegalAccessException ex) {
ex.printStackTrace();
throw new IllegalAccessError();
} catch(java.io.IOException ex) {
throw new DataTransferError(ex);
}
return true;
}
protected final boolean loadVariable(ResultSet result,
Object obj, int column)
throws SQLException, IllegalAccessException
{
switch (inType) {
case t_byte:
field.setByte(obj, result.getByte(column));
break;
case t_short:
field.setShort(obj, result.getShort(column));
break;
case t_int:
field.setInt(obj, result.getInt(column));
break;
case t_long:
field.setLong(obj, result.getLong(column));
break;
case t_float:
field.setFloat(obj, result.getFloat(column));
break;
case t_double:
field.setDouble(obj, result.getDouble(column));
break;
case t_boolean:
field.setBoolean(obj, result.getBoolean(column));
break;
case tByte:
byte b = result.getByte(column);
field.set(obj, result.wasNull() ? null : new Byte(b));
break;
case tShort:
short s = result.getShort(column);
field.set(obj, result.wasNull() ? null : new Short(s));
break;
case tInteger:
int i = result.getInt(column);
field.set(obj, result.wasNull() ? null : new Integer(i));
break;
case tLong:
long l = result.getLong(column);
field.set(obj, result.wasNull() ? null : new Long(l));
break;
case tFloat:
float f = result.getFloat(column);
field.set(obj, result.wasNull() ? null : new Float(f));
field.setFloat(obj, result.getFloat(column));
break;
case tDouble:
double d = result.getDouble(column);
field.set(obj, result.wasNull() ? null : new Double(d));
break;
case tBoolean:
boolean bl = result.getBoolean(column);
field.set(obj, result.wasNull() ? null : new Boolean(bl));
break;
case tDecimal:
if (Table.useDepricatedGetBigDecimal) {
if (scale < 0) {
scale = result.getMetaData().getScale(column);
}
field.set(obj, result.getBigDecimal(column, scale));
} else {
field.set(obj, result.getBigDecimal(column));
}
break;
case tString:
field.set(obj, result.getString(column));
break;
case tBytes:
field.set(obj, result.getBytes(column));
break;
case tDate:
field.set(obj, result.getDate(column));
break;
case tTime:
field.set(obj, result.getTime(column));
break;
case tTimestamp:
field.set(obj, result.getTimestamp(column));
break;
case tStream:
field.set(obj, result.getBinaryStream(column));
break;
case tBlob:
field.set(obj, result.getBlob(column));
break;
case tClob:
field.set(obj, result.getClob(column));
break;
case tClosure:
try {
java.io.InputStream input = result.getBinaryStream(column);
java.io.ObjectInputStream in =
new java.io.ObjectInputStream(input);
field.set(obj, in.readObject());
in.close();
} catch(ClassNotFoundException ex) {
throw new DataTransferError(ex);
} catch(java.io.IOException ex) {
throw new DataTransferError(ex);
}
break;
default:
return false;
}
return true;
}
}
@@ -0,0 +1,17 @@
#
# $Id: Makefile,v 1.1 2001/02/13 05:46:56 mdb Exp $
ROOT = ../../../..
SRCS = \
Cursor.java \
DataTransferError.java \
FieldDescriptor.java \
NoCurrentObjectError.java \
NoPrimaryKeyError.java \
SQLError.java \
Session.java \
SessionThread.java \
Table.java \
include $(ROOT)/build/Makefile.java
@@ -0,0 +1,23 @@
//-< NoCurrentObjectError.java >-------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 16-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Exception raised when UPDATE/REMOVE operation is applied to Cursor
// with no current object
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
/** Error raised when update/remove operation is applied to
* cursor with no current object (before first call of <TT>next()</TT>
* method).
*/
public class NoCurrentObjectError extends java.lang.Error {
NoCurrentObjectError() {
super("Cursor doesn't specify current object");
}
}
@@ -0,0 +1,22 @@
//-< NoPrimaryKeyError.java >----------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 16-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Exception raised when UPDATE/REMOVE operation is appllied to Table with
// no primary key
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
/** Error raised when unpdate/remove operation is invoked for the table
* with no correct primary key defined (key was not specified or
* type of the key component is not atomic).
*/
public class NoPrimaryKeyError extends java.lang.Error {
NoPrimaryKeyError(Table table) {
super("Table " + table.name + " has no atomic primary key");
}
}
@@ -0,0 +1,27 @@
What have we here?
------------------
This is a hacked version of JORA, a library for automatically mapping
RDBMS database rows to Java objects and vice versa. It's simple, it uses
reflection and doesn't require any classfile post-processing or external
configuration information.
"That's all great but why is it hacked?" wonders the astute reader. Well,
I couldn't quite cope with the error handling paradigm that it used (catch
all SQL exceptions internally and pass them to an application-wide error
handler method) and a few seconds thought on the matter led me to believe
that there is not a simple way to allow a choice of error handling
paradigms (the methods have to throw SQLException or not). So I took the
easy way out and forked the codebase.
The code is pretty stable, so I don't expect to be merging in updates with
any frequency. I promise to be a good monkey and submit any useful
modifications that I make back to the original maintainer. It's a good
thing that the license was unrestrictive enough to allow me to do this,
otherwise I'd probably be stuck reinventing the wheel or using something
that does less of what I want. Three cheers for free software.
The original version can be found here: http://www.ispras.ru/~knizhnik/
(or at least it could when I wrote this README).
- mdb (2/12/2001)
@@ -0,0 +1,21 @@
//-< SQLError.java >-------------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 16-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Database error
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
/** Database error. Exception SQLException was catched by JORA.
*/
public class SQLError extends java.lang.RuntimeException {
java.sql.SQLException ex;
SQLError(java.sql.SQLException x) {
super("Database session aborted due to critical error");
ex = x;
}
}
@@ -0,0 +1,161 @@
//-< Session.java >--------------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 20-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Database session abstraction
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
import java.util.*;
import java.sql.*;
/**
* This class is reposnsible for establishing connection with database
* and handling database errors.
*/
public class Session {
public Connection connection;
/** Session consructor
*
* @param driverClass class of database driver
*/
public Session(String driverClass)
{
driver = driverClass;
preparedStmtHash = new Hashtable();
connectionID = 0;
}
/** Session consructor for ODBC bridge driver
*/
public Session() { this("sun.jdbc.odbc.JdbcOdbcDriver"); }
/** Handler of database session errors. Programmer should override
* this method in derived class in order to provide application
* dependent error handling.
*
* @param ex exception thrown by some of JDBC methods
*/
public void handleSQLException(SQLException ex) {
// A SQLException was generated. Catch it and
// display the error information. Note that there
// could be multiple error objects chained
// together
System.out.println ("*** SQLException caught ***");
SQLException x = ex;
while (ex != null) {
System.out.println("SQLState: " + ex.getSQLState ());
System.out.println("Message: " + ex.getMessage ());
System.out.println("Vendor: " + ex.getErrorCode ());
ex = ex.getNextException();
System.out.println ("");
}
throw new SQLError(x); // terminate program execution
}
/** Open database session.
* Attempt to establish a connection to the given database URL.
* The DriverManager attempts to select an appropriate driver from
* the set of registered JDBC drivers.
*
* @param url a database url of the form
* jdbc:<em>subprotocol</em>:<em>subname</em>
* @param user the database user on whose behalf the Connection is being made
* @param password the user's password
* @return true if session is succesfully openned, false otherwise
*/
public boolean open(String dataSource, String user, String password)
throws SQLException
{
try {
Class.forName(driver);
connection =
DriverManager.getConnection(dataSource, user, password);
connectionID += 1;
}
// catch(SQLException ex) {
// handleSQLException(ex);
// return false;
// }
catch(ClassNotFoundException ex) {
return false;
}
return true;
}
/** Close database session and release all resources holded by session.
*/
public void close()
throws SQLException
{
// try {
Enumeration items = preparedStmtHash.elements();
while (items.hasMoreElements()) {
((PreparedStatement)items.nextElement()).close();
}
preparedStmtHash.clear();
connection.close();
// }
// catch (SQLException ex) { handleSQLException(ex); }
}
/**
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition,
* SQL statements that return nothing such as SQL DDL statements
* can be executed.
*
* @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL
* statement that returns nothing
* @return either the row count for INSERT, UPDATE or DELETE or 0
* for SQL statements that return nothing
*/
public int execute(String sql)
throws SQLException
{
// try {
Statement stmt = connection.createStatement();
int result = stmt.executeUpdate(sql);
stmt.close();
return result;
// } catch(SQLException ex) { handleSQLException(ex); }
// return -1;
}
/**
* Commit makes all changes made since the previous
* commit/rollback permanent and releases any database locks
* currently held by the Connection. This method should only be
* used when auto commit has been disabled.
*/
public void commit()
throws SQLException
{
// try {
connection.commit();
// } catch(SQLException ex) { handleSQLException(ex); }
}
/**
* Rollback drops all changes made since the previous
* commit/rollback and releases any database locks currently held
* by the Connection. This method should only be used when auto
* commit has been disabled.
*/
public void rollback()
throws SQLException
{
// try {
connection.rollback();
// } catch(SQLException ex) { handleSQLException(ex); }
}
protected String driver; // driver class name
protected Hashtable preparedStmtHash;
protected int connectionID;
}
@@ -0,0 +1,148 @@
//-< SessionThread.java >--------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 18-Jun-99 K.A. Knizhnik * / [] \ *
// Last update: 18-Jun-99 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Thread associated with database session
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
/**
* Class representing thread associated with users database session.
* If there is single database session opened by application, that it is
* possible to associate it with Table object statically. Otherwise it is
* needed either to explicitly specify session object in each insert,select
* or update statement or associate session with thread by means of
* SessionThread class.
*/
public class SessionThread extends Thread {
Session session;
/**
* Allocates a new <code>SessionThread</code> object and associate it with
* the specified session. This constructor has the same effect as
* <code>SessionThread(session, null, null,</code>
* <i>gname</i><code>)</code>, where <b><i>gname</i></b> is
* a newly generated name. Automatically generated names are of the
* form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer.
* <p>
* Threads created this way must have overridden their
* <code>run()</code> method to actually do anything.
*
* @param session user database session associated with this thread
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session) {
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object and associate it with
* the specified session. This constructor has the same effect as
* <code>SessionThread(session, target, null,</code>
* <i>gname</i><code>)</code>, where <b><i>gname</i></b> is
* a newly generated name. Automatically generated names are of the
* form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer.
* <p>
* Threads created this way must have overridden their
* <code>run()</code> method to actually do anything.
*
* @param session user database session associated with this thread
* @param target the object whose <code>run</code> method is called.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, Runnable target) {
super(target);
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object and associate it with
* the specified session. This constructor has the same effect as
* <code>SessionThread(session, target, group,</code>
* <i>gname</i><code>)</code>, where <b><i>gname</i></b> is
* a newly generated name. Automatically generated names are of the
* form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer.
* <p>
* Threads created this way must have overridden their
* <code>run()</code> method to actually do anything.
*
* @param session user database session associated with this thread
* @param group the thread group.
* @param target the object whose <code>run</code> method is called.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, ThreadGroup group, Runnable target) {
super(target);
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object. This constructor has
* the same effect as <code>SessionThread(session, null, null, name)</code>.
*
* @param session user database session associated with this thread
* @param name the name of the new thread.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, String name) {
super(name);
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object. This constructor has
* the same effect as <code>SessionThread(session, group, null, name)</code>.
*
* @param session user database session associated with this thread
* @param group the thread group.
* @param name the name of the new thread.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, ThreadGroup group, String name) {
super(group, name);
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object. This constructor has
* the same effect as <code>SessionThread(session, null, target, name)</code> *
* @param session user database session associated with this thread
* @param target the object whose <code>run</code> method is called.
* @param name the name of the new thread.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, Runnable target, String name) {
super(target, name);
this.session = session;
}
/**
* Allocates a new <code>SessionThread</code> object so that it has
* <code>target</code> as its run object, has the specified
* <code>name</code> as its name, and belongs to the thread group
* referred to by <code>group</code>.
* <p>
* @param session user database session associated with this thread
* @param group the thread group.
* @param target the object whose <code>run</code> method is called.
* @param name the name of the new thread.
* @see java.lang.Thread#Thread(java.lang.ThreadGroup,
* java.lang.Runnable, java.lang.String)
*/
public SessionThread(Session session, ThreadGroup group, Runnable target,
String name)
{
super(group, target, name);
this.session = session;
}
}
@@ -0,0 +1,977 @@
//-< Table.java >----------------------------------------------------*--------*
// JORA Version 2.0 (c) 1998 GARRET * ? *
// (Java Object Relational Adapter) * /\| *
// * / \ *
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ *
// Last update: 20-Jun-98 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Class representing database table
//-------------------------------------------------------------------*--------*
package com.samskivert.jdbc.jora;
import java.util.*;
import java.sql.*;
import java.lang.reflect.*;
/** Table class is used to establish mapping between corteges of database
* tables and Java classes. This class is responsible for constructing
* SQL statements for extracting, updating and deleting records of the
* database table.
*/
public class Table {
/** Constructor for table object. Make association between Java class
* and database table.
*
* @param tclassName name of Java class
* @param tableName name of database table mapped on this Java class
* @param key table's primary key. This parameter is used in UPDATE/DELETE
* operations to locate record in the table.
* @s session, which should be opened before first access to the table
*/
public Table(String className, String tableName, Session s, String key) {
String[] keys = {key};
init(className, tableName, s, keys);
}
/** Constructor for table object. Make association between Java class
* and database table.
*
* @param tclassName name of Java class
* @param tableName name of database table mapped on this Java class
* @param keys table primary keys. This parameter is used in UPDATE/DELETE
* operations to locate record in the table.
* @s session, which should be opened before first access to the table
*/
public Table(String className, String tableName, Session s, String[] keys)
{
init(className, tableName, s, keys);
}
/** Constructor for table object. Make association between Java class
* and database table. Name of Java class should be the same as name of
* the database table
*
* @param className name of Java class, which should be (without
* package prefix) be the same as the name of database table.
* @param keys table primary keys. This parameter is used in UPDATE/DELETE
* operations to locate record in the table.
* @s session, which should be opened before first access to the table
*/
public Table(String className, Session s, String[] keys) {
init(className, className.substring(className.lastIndexOf('.')+1),
s, keys);
}
/** Constructor for table object. Make association between Java class
* and database table. Name of Java class should be the same as name of
* the database table
*
* @param className name of Java class, which should be (without
* package prefix) be the same as the name of database table.
* @param key table primary key. This parameter is used in UPDATE/DELETE
* operations to locate record in the table.
* @s session, which should be opened before first access to the table
*/
public Table(String className, Session s, String key) {
String[] keys = {key};
init(className, className.substring(className.lastIndexOf('.')+1),
s, keys);
}
/** Constructor of table without explicit key specification.
* Specification of key is necessary for update/remove operations.
* If key is not specified, it is inherited from base table (if any).
*/
public Table(String className, Session s) {
init(className, className.substring(className.lastIndexOf('.')+1),
s, null);
}
/** Constructor of table with "key" and "session" parameters inherited
* from base table.
*/
public Table(String className) {
init(className, className.substring(className.lastIndexOf('.')+1),
null, null);
}
/** Select records from database table according to search condition
*
* @param condition valid SQL condition expression started with WHERE
* or empty string if all records should be fetched.
*/
public final Cursor select(String condition) {
return new Cursor(this, session, 1, condition);
}
/** Select records from database table according to search condition
*
* @param condition valid SQL condition expression started with WHERE
* or empty string if all records should be fetched.
* @param session user database session
*/
public final Cursor select(String condition, Session session) {
return new Cursor(this, session, 1, condition);
}
/** Select records from specified and derived database tables
*
* @param condition valid SQL condition expression started with WHERE
* or empty string if all records should be fetched.
*/
public final Cursor selectAll(String condition) {
return new Cursor(this, session, nDerived+1, condition);
}
/** Select records from specified and derived database tables
*
* @param condition valid SQL condition expression started with WHERE
* or empty string if all records should be fetched.
* @param session user database session
*/
public final Cursor selectAll(String condition, Session session) {
return new Cursor(this, session, nDerived+1, condition);
}
/** Select records from database table using <I>obj</I> object as
* template for selection. All non-builtin fields of this object,
* which are not null, are compared with correspondent table values.
*
* @param obj object for construction search condition: selected objects
* should match all non-null fields of specified object.
*/
public final Cursor queryByExample(Object obj) {
return new Cursor(this, session, 1, obj);
}
/** Select records from database table using <I>obj</I> object as
* template for selection. All non-builtin fields of this object,
* which are not null, are compared with correspondent table values.
*
* @param obj object for construction search condition: selected objects
* should match all non-null fields of specified object.
* @param session user database session
*/
public final Cursor queryByExample(Object obj, Session session) {
return new Cursor(this, session, 1, obj);
}
/** Select records from specified and derived database tables using
* <I>obj</I> object as template for selection.
* All non-builtin fields of this object,
* which are not null, are compared with correspondent table values.
*
* @param obj object for construction search condition: selected objects
* should match all non-null fields of specified object.
*/
public final Cursor queryAllByExample(Object obj) {
return new Cursor(this, session, nDerived+1, obj);
}
/** Select records from specified and derived database tables using
* <I>obj</I> object as template for selection.
* All non-builtin fields of this object,
* which are not null, are compared with correspondent table values.
*
* @param obj object for construction search condition: selected objects
* should match all non-null fields of specified object.
* @param session user database session
*/
public final Cursor queryAllByExample(Object obj, Session session) {
return new Cursor(this, session, nDerived+1, obj);
}
/** Insert new record in the table. Values of inserted record fields
* are taken from specifed object.
*
* @param obj object specifing values of inserted record fields
*/
public void insert(Object obj)
throws SQLException
{
insert(obj, session);
}
/** Insert new record in the table using specified database session.
* Values of inserted record fields
* are taken from specifed object.
*
* @param obj object specifing values of inserted record fields
* @param session user database session
*/
public synchronized void insert(Object obj, Session session)
throws SQLException
{
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
// try {
checkConnection(session);
if (insertStmt == null) {
String sql = "insert into " + name + " ("
+ listOfFields + ") values (?";
for (int i = 1; i < nColumns; i++) {
sql += ",?";
}
sql += ")";
insertStmt = session.connection.prepareStatement(sql);
}
bindUpdateVariables(insertStmt, obj);
insertStmt.executeUpdate();
insertStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
}
/** Insert several new records in the table. Values of inserted records
* fields are taken from objects of specified array.
*
* @param objects array with objects specifing values of inserted record
* fields
*/
public void insert(Object[] objects)
throws SQLException
{
insert(objects, session);
}
/** Insert several new records in the table. Values of inserted records
* fields are taken from objects of specified array.
*
* @param objects array with objects specifing values of inserted record
* fields
* @param session user database session
*/
public synchronized void insert(Object[] objects, Session session)
throws SQLException
{
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
// try {
checkConnection(session);
if (insertStmt == null) {
String sql = "insert into " + name + " ("
+ listOfFields + ") values (?";
for (int i = 1; i < nColumns; i++) {
sql += ",?";
}
sql += ")";
insertStmt = session.connection.prepareStatement(sql);
}
for (int i = 0; i < objects.length; i++) {
bindUpdateVariables(insertStmt, objects[i]);
insertStmt.addBatch();
}
insertStmt.executeBatch();
insertStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
}
/** Update record in the table using table's primary key to locate
* record in the table and values of fields of specified object <I>obj</I>
* to alter record fields.
*
* @param obj object specifing value of primary key and new values of
* updated record fields
*
* @return number of objects actually updated
*/
public int update(Object obj)
throws SQLException
{
return update(obj, session);
}
/** Update record in the table using table's primary key to locate
* record in the table and values of fields of specified object <I>obj</I>
* to alter record fields.
*
* @param obj object specifing value of primary key and new values of
* updated record fields
* @param session user database session
*
* @return number of objects actually updated
*/
public synchronized int update(Object obj, Session session)
throws SQLException
{
if (primaryKeys == null) {
throw new NoPrimaryKeyError(this);
}
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
int nUpdated = 0;
// try {
checkConnection(session);
if (updateStmt == null) {
String sql = "update " + name + " set " + listOfAssignments
+ " where " + primaryKeys[0] + " = ?";
for (int i = 1; i < primaryKeys.length; i++) {
sql += " and " + primaryKeys[i] + " = ?";
}
updateStmt = session.connection.prepareStatement(sql);
}
for (int i = 0; i < primaryKeys.length; i++) {
fields[primaryKeyIndices[i]].bindVariable(updateStmt, obj,
nColumns+i+1);
}
bindUpdateVariables(updateStmt, obj);
nUpdated = updateStmt.executeUpdate();
updateStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
return nUpdated;
}
/** Update set of records in the table using table's primary key to locate
* record in the table and values of fields of objects from sepecifed
* array <I>objects</I> to alter record fields.
*
* @param objects array of objects specifing primiray keys and and new
* values of updated record fields
*
* @return number of objects actually updated
*/
public int update(Object[] objects)
throws SQLException
{
return update(objects, session);
}
/** Update set of records in the table using table's primary key to locate
* record in the table and values of fields of objects from sepecifed
* array <I>objects</I> to alter record fields.
*
* @param objects array of objects specifing primiray keys and and new
* values of updated record fields
* @param session user database session
*
* @return number of objects actually updated
*/
public synchronized int update(Object[] objects, Session session)
throws SQLException
{
if (primaryKeys == null) {
throw new NoPrimaryKeyError(this);
}
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
int nUpdated = 0;
// try {
checkConnection(session);
if (updateStmt == null) {
String sql = "update " + name + " set " + listOfAssignments
+ " where " + primaryKeys[0] + " = ?";
for (int i = 1; i < primaryKeys.length; i++) {
sql += " and " + primaryKeys[i] + " = ?";
}
updateStmt = session.connection.prepareStatement(sql);
}
for (int i = 0; i < objects.length; i++) {
for (int j = 0; j < primaryKeys.length; j++) {
fields[primaryKeyIndices[j]].bindVariable(updateStmt,
objects[i],
nColumns+1+j);
}
bindUpdateVariables(updateStmt, objects[i]);
updateStmt.addBatch();
}
int rc[] = updateStmt.executeBatch();
for (int k = 0; k < rc.length; k++) {
nUpdated += rc[k];
}
updateStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
return nUpdated;
}
/** Delete record with specified value of primary key from the table.
*
* @param obj object containing value of primary key.
*
* @return number of objects actually deleted
*/
public int delete(Object obj)
throws SQLException
{
return delete(obj, session);
}
/** Delete record with specified value of primary key from the table.
*
* @param obj object containing value of primary key.
* @param session user database session
*/
public synchronized int delete(Object obj, Session session)
throws SQLException
{
if (primaryKeys == null) {
throw new NoPrimaryKeyError(this);
}
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
int nDeleted = 0;
// try {
checkConnection(session);
if (deleteStmt == null) {
String sql = "delete from " + name +
" where " + primaryKeys[0] + " = ?";
for (int i = 1; i < primaryKeys.length; i++) {
sql += " and " + primaryKeys[i] + " = ?";
}
deleteStmt = session.connection.prepareStatement(sql);
}
for (int i = 0; i < primaryKeys.length; i++) {
fields[primaryKeyIndices[i]].bindVariable(deleteStmt, obj,i+1);
}
nDeleted = deleteStmt.executeUpdate();
deleteStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
return nDeleted;
}
/** Delete records with specified primary keys from the table.
*
* @param objects array of objects containing values of primary key.
*
* @return number of objects actually deleted
*/
public int delete(Object[] objects)
throws SQLException
{
return delete(objects, session);
}
/** Delete records with specified primary keys from the table.
*
* @param objects array of objects containing values of primary key.
*
* @return number of objects actually deleted
*/
public synchronized int delete(Object[] objects, Session session)
throws SQLException
{
if (primaryKeys == null) {
throw new NoPrimaryKeyError(this);
}
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
int nDeleted = 0;
// try {
checkConnection(session);
if (deleteStmt == null) {
String sql = "delete from " + name +
" where " + primaryKeys[0] + " = ?";
for (int i = 1; i < primaryKeys.length; i++) {
sql += " and " + primaryKeys[i] + " = ?";
}
deleteStmt = session.connection.prepareStatement(sql);
}
for (int i = 0; i < objects.length; i++) {
for (int j = 0; j < primaryKeys.length; j++) {
fields[primaryKeyIndices[j]].bindVariable(deleteStmt,
objects[i], j+1);
}
deleteStmt.addBatch();
}
int rc[] = deleteStmt.executeBatch();
for (int k = 0; k < rc.length; k++) {
nDeleted += rc[k];
}
deleteStmt.clearParameters();
// } catch(SQLException ex) { session.handleSQLException(ex); }
return nDeleted;
}
/** Spearator of name components of compound field. For example, if Java
* class constains component "location" of Point class, which
* has two components "x" and "y", then database table should
* have columns "location_x" and "location_y" (if '_' is used
* as separator)
*/
public static String fieldSeparator = "_";
/** Some versions of JDBC driver doesn't support
* <code>getBigDecimal(int columnIndex)</code>. Setting this variable to
* <code>true</code> makes JORA to explicitly request scale from result
* set metadata and use deprecated version of <code>getBigDecimal</code>
* with extra <code>scale</code> parameter.
*/
public static boolean useDepricatedGetBigDecimal = true;
// --- Implementation -----------------------------------------
/** Is table abstract - not present in database.
*/
protected boolean isAbstract;
protected Table derived;
protected int nDerived;
protected String name;
protected String listOfFields;
protected String listOfAssignments;
protected Class cls;
protected Session session;
static private Class serializableClass;
private FieldDescriptor[] fields;
private int nFields; // length of "fields" array
private int nColumns; // number of atomic fields in "fields" array
private String primaryKeys[];
private int primaryKeyIndices[];
protected int connectionID;
private PreparedStatement updateStmt;
private PreparedStatement deleteStmt;
private PreparedStatement insertStmt;
private static Table allTables;
private Constructor constructor;
private static Method setBypass;
private static final Object[] bypassFlag = { new Boolean(true) };
private static final Object[] constructorArgs = {};
static {
try {
serializableClass = Class.forName("java.io.Serializable");
Class c = Class.forName("java.lang.reflect.AccessibleObject");
Class[] param = { Boolean.TYPE };
setBypass = c.getMethod("setAccessible", param);
} catch(Exception ex) {}
}
private final void init(String className, String tableName, Session s,
String[] keys)
{
name = tableName;
try {
cls = Class.forName(className);
} catch(ClassNotFoundException ex) {throw new NoClassDefFoundError();}
isAbstract = tableName == null;
session = s;
primaryKeys = keys;
listOfFields = "";
listOfAssignments = "";
connectionID = 0;
Vector fieldsVector = new Vector();
nFields = buildFieldsList(fieldsVector, cls, "");
fields = new FieldDescriptor[nFields];
fieldsVector.copyInto(fields);
try {
constructor = cls.getDeclaredConstructor(new Class[0]);
setBypass.invoke(constructor, bypassFlag);
} catch(Exception ex) {}
if (keys != null) {
if (keys.length == 0) {
throw new NoPrimaryKeyError(this);
}
primaryKeyIndices = new int[keys.length];
for (int j = keys.length; --j >= 0;) {
int i = nFields;
while (--i >= 0) {
if (fields[i].name.equals(keys[j])) {
if (!fields[i].isAtomic()) {
throw new NoPrimaryKeyError(this);
}
primaryKeyIndices[j] = i;
break;
}
}
if (i < 0) {
throw new NoSuchFieldError("No such field '" + keys[j]
+ "' in table " + name);
}
}
}
insertIntoTableHierarchy();
}
private final void insertIntoTableHierarchy()
{
Table t, prev = null;
Table after = null;
int nChilds = 0;
for (t = allTables; t != null; prev = t, t = t.derived) {
if (t.cls.isAssignableFrom(cls)) {
if (primaryKeys == null && t.primaryKeys != null) {
primaryKeys = t.primaryKeys;
primaryKeyIndices = t.primaryKeyIndices;
}
if (session == null) {
session = t.session;
}
t.nDerived += 1;
after = t;
} else if (cls.isAssignableFrom(t.cls)) {
after = prev;
do {
if (cls.isAssignableFrom(t.cls)) {
if (primaryKeys != null && t.primaryKeys == null) {
t.primaryKeys = primaryKeys;
t.primaryKeyIndices = primaryKeyIndices;
}
if (t.session == null) {
t.session = session;
}
nChilds += 1;
}
} while ((t = t.derived) != null);
break;
}
}
if (after == null) {
derived = allTables;
allTables = this;
} else {
derived = after.derived;
after.derived = this;
}
nDerived = nChilds;
}
private final void checkConnection(Session s) throws SQLException {
if (connectionID != s.connectionID) {
if (insertStmt != null) {
insertStmt.close();
insertStmt = null;
}
if (updateStmt != null) {
updateStmt.close();
updateStmt = null;
}
if (deleteStmt != null) {
deleteStmt.close();
deleteStmt = null;
}
connectionID = s.connectionID;
}
}
private final int buildFieldsList(Vector buf, Class cls, String prefix)
{
Field[] f = cls.getDeclaredFields();
Class superclass = cls;
while ((superclass = superclass.getSuperclass()) != null) {
Field[] inheritedFields = superclass.getDeclaredFields();
Field[] allFields = new Field[inheritedFields.length + f.length];
System.arraycopy(inheritedFields, 0, allFields, 0,
inheritedFields.length);
System.arraycopy(f,0, allFields, inheritedFields.length, f.length);
f = allFields;
}
try {
for (int i = f.length; --i>= 0;) {
setBypass.invoke(f[i], bypassFlag);
}
} catch(Exception ex) {
System.err.println("Failed to set bypass attribute");
}
int n = 0;
for (int i = 0; i < f.length; i++) {
if ((f[i].getModifiers()&(Modifier.TRANSIENT|Modifier.STATIC))==0)
{
String name = f[i].getName();
Class fieldClass = f[i].getType();
String fullName = prefix + name;
FieldDescriptor fd = new FieldDescriptor(f[i], fullName);
int type;
buf.addElement(fd);
n += 1;
String c = fieldClass.getName();
if (c.equals("byte")) type = FieldDescriptor.t_byte;
else if (c.equals("short")) type = FieldDescriptor.t_short;
else if (c.equals("int")) type = FieldDescriptor.t_int;
else if (c.equals("long")) type = FieldDescriptor.t_long;
else if (c.equals("float")) type = FieldDescriptor.t_float;
else if (c.equals("double")) type = FieldDescriptor.t_double;
else if (c.equals("boolean")) type = FieldDescriptor.t_boolean;
else if (c.equals("java.lang.Byte"))
type = FieldDescriptor.tByte;
else if (c.equals("java.lang.Short"))
type = FieldDescriptor.tShort;
else if (c.equals("java.lang.Integer"))
type = FieldDescriptor.tInteger;
else if (c.equals("java.lang.Long"))
type = FieldDescriptor.tLong;
else if (c.equals("java.lang.Float"))
type = FieldDescriptor.tFloat;
else if (c.equals("java.lang.Double"))
type = FieldDescriptor.tDouble;
else if (c.equals("java.lang.Boolean"))
type = FieldDescriptor.tBoolean;
else if (c.equals("java.math.BigDecimal"))
type = FieldDescriptor.tDecimal;
else if (c.equals("java.lang.String"))
type = FieldDescriptor.tString;
else if (c.equals("java.lang.[B"))
type = FieldDescriptor.tBytes;
else if (c.equals("java.sql.Date"))
type = FieldDescriptor.tDate;
else if (c.equals("java.sql.Time"))
type = FieldDescriptor.tTime;
else if (c.equals("java.sql.Timestamp"))
type = FieldDescriptor.tTimestamp;
else if (c.equals("java.lang.InputStream"))
type = FieldDescriptor.tStream;
else if (c.equals("java.sql.BlobLocator"))
type = FieldDescriptor.tBlob;
else if (c.equals("java.sql.ClobLocator"))
type = FieldDescriptor.tClob;
else if (serializableClass.isAssignableFrom(fieldClass))
type = FieldDescriptor.tClosure;
else {
int nComponents = buildFieldsList(buf, fieldClass,
fd.name+fieldSeparator);
fd.inType = fd.outType =
FieldDescriptor.tCompound + nComponents;
try {
fd.constructor =
fieldClass.getDeclaredConstructor(new Class[0]);
setBypass.invoke(fd.constructor, bypassFlag);
} catch(Exception ex) {}
n += nComponents;
continue;
}
if (listOfFields.length() != 0) {
listOfFields += ",";
listOfAssignments += ",";
}
listOfFields += fullName;
listOfAssignments += fullName + "=?";
fd.inType = fd.outType = type;
nColumns += 1;
}
}
return n;
}
protected final Object load(ResultSet result) throws SQLException {
Object obj;
try {
obj = constructor.newInstance(constructorArgs);
}
catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
catch(InstantiationException ex) { throw new InstantiationError(); }
catch(Exception ex) {
throw new InstantiationError("Exception was thrown by constructor");
}
load(obj, 0, nFields, 0, result);
return obj;
}
private final int load(Object obj, int i, int end, int column,
ResultSet result)
throws SQLException
{
try {
while (i < end) {
FieldDescriptor fd = fields[i++];
if (!fd.loadVariable(result, obj, ++column)) {
Object component =
fd.constructor.newInstance(constructorArgs);
fd.field.set(obj, component);
int nComponents = fd.inType - FieldDescriptor.tCompound;
column = load(component, i, i + nComponents,
column-1, result);
i += nComponents;
}
}
}
catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
catch(InstantiationException ex) { throw new InstantiationError(); }
catch(InvocationTargetException ex) {
throw new InstantiationError("Exception was thrown by constructor");
}
return column;
}
protected final void bindUpdateVariables(PreparedStatement pstmt,
Object obj)
throws SQLException
{
bindUpdateVariables(pstmt, obj, 0, nFields, 0);
}
protected final void bindQueryVariables(PreparedStatement pstmt,
Object obj)
throws SQLException
{
bindQueryVariables(pstmt, obj, 0, nFields, 0);
}
protected final void updateVariables(ResultSet result, Object obj)
throws SQLException
{
updateVariables(result, obj, 0, nFields, 0);
result.updateRow();
}
protected final String buildQueryList(Object qbe)
{
StringBuffer buf = new StringBuffer();
buildQueryList(buf, qbe, 0, nFields);
String condition = buf.toString();
return condition.length() != 0 ? " where " + condition : "";
}
private final int bindUpdateVariables(PreparedStatement pstmt, Object obj,
int i, int end, int column)
throws SQLException
{
try {
while (i < end) {
FieldDescriptor fd = fields[i++];
Object comp = null;
if (!fd.isBuiltin() && (comp = fd.field.get(obj)) == null) {
if (fd.isCompound()) {
int nComponents = fd.outType-FieldDescriptor.tCompound;
while (--nComponents >= 0) {
fd = fields[i++];
if (!fd.isCompound()) {
pstmt.setNull(++column,
FieldDescriptor.sqlTypeMapping[fd.outType]);
}
}
} else {
pstmt.setNull(++column,
FieldDescriptor.sqlTypeMapping[fd.outType]);
}
} else {
if (!fd.bindVariable(pstmt, obj, ++column)) {
int nComponents = fd.outType-FieldDescriptor.tCompound;
column = bindUpdateVariables(pstmt, comp,
i,i+nComponents,column-1);
i += nComponents;
}
}
}
} catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
return column;
}
private final int bindQueryVariables(PreparedStatement pstmt, Object obj,
int i, int end, int column)
throws SQLException
{
try {
while (i < end) {
Object comp;
FieldDescriptor fd = fields[i++];
if (!fd.field.getDeclaringClass().isInstance(obj)) {
return column;
}
int nComponents =
fd.isCompound() ? fd.outType-FieldDescriptor.tCompound : 0;
if (!fd.isBuiltin()
&& fd.outType != FieldDescriptor.tClosure
&& (comp = fd.field.get(obj)) != null)
{
if (!fd.bindVariable(pstmt, obj, ++column)) {
column = bindQueryVariables(pstmt, comp,
i,i+nComponents,column-1);
}
}
i += nComponents;
}
} catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
return column;
}
private final void buildQueryList(StringBuffer buf, Object qbe,
int i, int end)
{
try {
while (i < end) {
Object comp;
FieldDescriptor fd = fields[i++];
int nComponents =
fd.isCompound() ? fd.outType-FieldDescriptor.tCompound : 0;
if (!fd.isBuiltin()
&& fd.outType != FieldDescriptor.tClosure
&& (comp = fd.field.get(qbe)) != null)
{
if (nComponents != 0) {
buildQueryList(buf, comp, i, i+nComponents);
} else {
if (buf.length() != 0) {
buf.append(",");
}
buf.append(fd.name);
buf.append("=?");
}
}
i += nComponents;
}
} catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
}
protected final int updateVariables(ResultSet result, Object obj,
int i, int end, int column)
throws SQLException
{
try {
while (i < end) {
FieldDescriptor fd = fields[i++];
Object comp = null;
if (!fd.isBuiltin() && (comp = fd.field.get(obj)) == null) {
if (fd.isCompound()) {
int nComponents = fd.outType-FieldDescriptor.tCompound;
while (--nComponents >= 0) {
fd = fields[i++];
if (!fd.isCompound()) {
result.updateNull(++column);
}
}
} else {
result.updateNull(++column);
}
} else {
if (!fd.updateVariable(result, obj, ++column)) {
int nComponents = fd.outType-FieldDescriptor.tCompound;
column = updateVariables(result, comp,
i, i+nComponents, column-1);
i += nComponents;
}
}
}
} catch(IllegalAccessException ex) { throw new IllegalAccessError(); }
return column;
}
}