Revamped JORA to avoid keeping connection objects around which is not thread

safe and particularly not so in the webapp where one thread might come slip a
read-only connection into the session before an update was about to happen.

This ends up being a giant refactor, but it's much cleaner this way (to pass a
connection into the Table whenever it is doing anything), so I've taken the
pain. Fortuanately, code that uses the convenience methods doesn't need to
change, so as a part of this refactor I've taken the opportunity to switch a
bunch of old stuff to the new, concise, future proof hotness.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1830 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2006-04-21 21:26:07 +00:00
parent ca9adfe063
commit c67926d14d
11 changed files with 1073 additions and 1705 deletions
@@ -30,7 +30,7 @@ import com.samskivert.jdbc.jora.*;
* The JORA repository simplifies the process of building persistence * The JORA repository simplifies the process of building persistence
* services that make use of the JORA object relational mapping package. * services that make use of the JORA object relational mapping package.
* *
* @see com.samskivert.jdbc.jora.Session * @see com.samskivert.jdbc.jora.Table
*/ */
public abstract class JORARepository extends SimpleRepository public abstract class JORARepository extends SimpleRepository
{ {
@@ -47,17 +47,12 @@ public abstract class JORARepository extends SimpleRepository
{ {
super(provider, dbident); super(provider, dbident);
// our parent class will have already obtained a database connection
// and therefore ended up calling getConnection() which will create our
// session, so we can make use of it straight away
// create our tables // create our tables
createTables(_session); createTables();
} }
/** /**
* Inserts the supplied object into the specified table. The table must be * Inserts the supplied object into the specified table.
* configured to store items of the supplied type.
* *
* @return a call to {@link DatabaseLiaison#lastInsertedId} made * @return a call to {@link DatabaseLiaison#lastInsertedId} made
* immediately following the insert. * immediately following the insert.
@@ -69,15 +64,14 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
table.insert(object); table.insert(conn, object);
return liaison.lastInsertedId(conn); return liaison.lastInsertedId(conn);
} }
}); });
} }
/** /**
* Updates the supplied object in the specified table. The table must * Updates the supplied object in the specified table.
* be configured to store items of the supplied type.
* *
* @return the number of rows modified by the update. * @return the number of rows modified by the update.
*/ */
@@ -88,7 +82,26 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.update(object); return table.update(conn, object);
}
});
}
/**
* Updates fields specified by the supplied field mask in the supplied
* object in the specified table.
*
* @return the number of rows modified by the update.
*/
protected <T> int update (final Table<T> table, final T object,
final FieldMask mask)
throws PersistenceException
{
return executeUpdate(new Operation<Integer>() {
public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
return table.update(conn, object, mask);
} }
}); });
} }
@@ -106,7 +119,7 @@ public abstract class JORARepository extends SimpleRepository
Connection conn, DatabaseLiaison liaison) Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.select(query).toArrayList(); return table.select(conn, query).toArrayList();
} }
}); });
} }
@@ -124,7 +137,43 @@ public abstract class JORARepository extends SimpleRepository
Connection conn, DatabaseLiaison liaison) Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.select(auxtable, query).toArrayList(); return table.select(conn, auxtable, query).toArrayList();
}
});
}
/**
* Loads all objects from the specified table that match the supplied
* example.
*/
protected <T> ArrayList<T> loadAllByExample (
final Table<T> table, final T example)
throws PersistenceException
{
return execute(new Operation<ArrayList<T>>() {
public ArrayList<T> invoke (
Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
return table.queryByExample(conn, example).toArrayList();
}
});
}
/**
* Loads all objects from the specified table that match the supplied
* example.
*/
protected <T> ArrayList<T> loadAllByExample (
final Table<T> table, final T example, final FieldMask mask)
throws PersistenceException
{
return execute(new Operation<ArrayList<T>>() {
public ArrayList<T> invoke (
Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
return table.queryByExample(conn, example, mask).toArrayList();
} }
}); });
} }
@@ -141,7 +190,25 @@ public abstract class JORARepository extends SimpleRepository
public T invoke (Connection conn, DatabaseLiaison liaison) public T invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.select(query).get(); return table.select(conn, query).get();
}
});
}
/**
* Loads a single object from the specified table that matches the supplied
* query, joining with the supplied auxiliary table(s). <em>Note:</em> the
* query should match one or zero records, not more.
*/
protected <T> T load (
final Table<T> table, final String auxtable, final String query)
throws PersistenceException
{
return execute(new Operation<T>() {
public T invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
return table.select(conn, auxtable, query).get();
} }
}); });
} }
@@ -158,7 +225,7 @@ public abstract class JORARepository extends SimpleRepository
public T invoke (Connection conn, DatabaseLiaison liaison) public T invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.queryByExample(example).get(); return table.queryByExample(conn, example).get();
} }
}); });
} }
@@ -169,14 +236,14 @@ public abstract class JORARepository extends SimpleRepository
* records, not more. * records, not more.
*/ */
protected <T> T loadByExample ( protected <T> T loadByExample (
final Table<T> table, final FieldMask mask, final T example) final Table<T> table, final T example, final FieldMask mask)
throws PersistenceException throws PersistenceException
{ {
return execute(new Operation<T>() { return execute(new Operation<T>() {
public T invoke (Connection conn, DatabaseLiaison liaison) public T invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.queryByExample(example, mask).get(); return table.queryByExample(conn, example, mask).get();
} }
}); });
} }
@@ -196,8 +263,8 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
if (table.update(object) == 0) { if (table.update(conn, object) == 0) {
table.insert(object); table.insert(conn, object);
return liaison.lastInsertedId(conn); return liaison.lastInsertedId(conn);
} }
return -1; return -1;
@@ -221,7 +288,7 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.update(object, mask); return table.update(conn, object, mask);
} }
}); });
} }
@@ -244,7 +311,7 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.update(object, mask); return table.update(conn, object, mask);
} }
}); });
} }
@@ -261,30 +328,14 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison) public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
return table.delete(object); return table.delete(conn, object);
} }
}); });
} }
/** /**
* After the database session is begun, this function will be called * During construction, this function will be called to give the repository
* to give the repository implementation the opportunity to create its * implementation the opportunity to create its table objects.
* table objects.
*
* @param session the session instance to use when creating your table
* instances.
*/ */
protected abstract void createTables (Session session); protected abstract void createTables ();
protected void gotConnection (Connection conn)
{
// create or update our JORA session
if (_session == null) {
_session = new Session(conn);
} else {
_session.setConnection(conn);
}
}
protected Session _session;
} }
@@ -46,7 +46,7 @@ public class MySQLLiaison implements DatabaseLiaison
String msg = sqe.getMessage(); String msg = sqe.getMessage();
return (msg != null && return (msg != null &&
(msg.indexOf("Lost connection") != -1 || (msg.indexOf("Lost connection") != -1 ||
msg.indexOf("Communication link failure") != -1 || msg.indexOf("link failure") != -1 ||
msg.indexOf("Broken pipe") != -1)); msg.indexOf("Broken pipe") != -1));
} }
+139 -151
View File
@@ -1,12 +1,22 @@
//-< Cursor.java >---------------------------------------------------*--------* //
// JORA Version 2.0 (c) 1998 GARRET * ? * // $Id$
// (Java Object Relational Adapter) * /\| * //
// * / \ * // samskivert library - useful routines for java programs
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ * // Copyright (C) 2001-6 Konstantin Knizhnik, Michael Bayne
// Last update: 20-Jun-98 K.A. Knizhnik * GARRET * //
//-------------------------------------------------------------------*--------* // This library is free software; you can redistribute it and/or modify it
// Cursor for navigation thru result of SELECT statement // under the terms of the GNU Lesser General Public License as published
//-------------------------------------------------------------------*--------* // by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.jora; package com.samskivert.jdbc.jora;
@@ -15,81 +25,67 @@ import java.sql.*;
import com.samskivert.Log; import com.samskivert.Log;
/** Cursor is used for successive access to records fetched by SELECT /**
* statement. As far as records can be retrived from several derived tables * Cursor is used for successive access to records fetched by SELECT statement.
* (polymorphic form of select), this class can issue several requests * As far as records can be retrived from several derived tables (polymorphic
* to database. Cursor also provides methods for updating/deleting current * form of select), this class can issue several requests to database. Cursor
* record. * also provides methods for updating/deleting current record.
*/ */
public class Cursor<V> public class Cursor<V>
{ {
/** /**
* A cursor is initially positioned before its first row; the * A cursor is initially positioned before its first row; the first call to
* first call to next makes the first row the current row; the * next makes the first row the current row; the second call makes the
* second call makes the second row the current row, etc. * second row the current row, etc.
* *
* <P>If an input stream from the previous row is open, it is * <P> If an input stream from the previous row is open, it is implicitly
* implicitly closed. The ResultSet's warning chain is cleared * closed. The ResultSet's warning chain is cleared when a new row is read.
* when a new row is read.
* *
* @return object constructed from fetched record or null if there * @return object constructed from fetched record or null if there are no
* are no more rows * more rows
*/ */
public V next () public V next ()
throws SQLException throws SQLException
{ {
// // if we closed everything up after the last call to next(), // if we closed everything up after the last call to next(),
// // table will be null here and we should bail immediately // table will be null here and we should bail immediately
if (table == null) { if (_table == null) {
return null; return null;
} }
// if (nTables == 0) {
// return null;
// }
// do { if (_result == null) {
if (result == null) { if (_qbeObject != null) {
// if (table.isAbstract) { PreparedStatement qbeStmt;
// table = table.derived; synchronized (_table.preparedStmtHash) {
// continue; qbeStmt = _table.preparedStmtHash.get(_query);
// } if (qbeStmt == null) {
if (qbeObject != null) { qbeStmt = _conn.prepareStatement(_query);
PreparedStatement qbeStmt; _table.preparedStmtHash.put(_query, qbeStmt);
synchronized(session.preparedStmtHash) {
Object s = session.preparedStmtHash.get(query);
if (s == null) {
qbeStmt=
session.connection.prepareStatement(query);
session.preparedStmtHash.put(query, qbeStmt);
} else {
qbeStmt = (PreparedStatement)s;
}
} }
synchronized(qbeStmt) { }
table.bindQueryVariables( synchronized (qbeStmt) {
qbeStmt, qbeObject, qbeMask); _table.bindQueryVariables(qbeStmt, _qbeObject, _qbeMask);
result = qbeStmt.executeQuery(); _result = qbeStmt.executeQuery();
qbeStmt.clearParameters(); qbeStmt.clearParameters();
} }
} else { } else {
if (stmt == null) { if (_stmt == null) {
stmt = session.connection.createStatement(); _stmt = _conn.createStatement();
} }
result = stmt.executeQuery(query); _result = _stmt.executeQuery(_query);
}
} }
if (result.next()) { }
return currObject = table.load(result); if (_result.next()) {
} return _currObject = _table.load(_result);
result.close(); }
result = null;
currObject = null;
table = null;
// table = table.derived;
// } while (--nTables != 0);
if (stmt != null) { _result.close();
stmt.close(); _result = null;
_currObject = null;
_table = null;
if (_stmt != null) {
_stmt.close();
} }
return null; return null;
} }
@@ -111,77 +107,81 @@ public class Cursor<V>
if (spurious > 0) { if (spurious > 0) {
Log.warning("Cursor.get() quietly tossed " + spurious + Log.warning("Cursor.get() quietly tossed " + spurious +
" spurious additional records. " + " spurious additional records. " +
"[query=" + query + "]."); "[query=" + _query + "].");
} }
} }
return result; return result;
} }
/** Update current record pointed by cursor. This method can be called /**
* only after next() method, which returns non-null object. This objects * Update current record pointed by cursor. This method can be called only
* is used to update current record fields.<P> * after next() method, which returns non-null object. This objects is used
* to update current record fields.
* *
* If you are going to update or delete selected records, you should add * <P> If you are going to update or delete selected records, you should
* "for update" clause to select statement. So parameter of * add "for update" clause to select statement. So parameter of
* <CODE>jora.Table.select()</CODE> statement should contain "for update" * <CODE>jora.Table.select()</CODE> statement should contain "for update"
* clause: * clause: <CODE>record.table.Select("where name='xyz' for
* <CODE>record.table.Select("where name='xyz' for update");</CODE><P> * update");</CODE><P>
* *
* <I><B>Attention!</I></B> * <I><B>Attention!</I></B> Not all database drivers support update
* Not all database drivers support update operation with * operation with cursor. This method will not work with such database
* cursor. This method will not work with such database drivers. * drivers.
*/ */
public void update () public void update ()
throws SQLException throws SQLException
{ {
if (currObject == null) { if (_currObject == null) {
throw new NoCurrentObjectError(); throw new IllegalStateException("No current object");
} }
table.updateVariables(result, currObject); _table.updateVariables(_result, _currObject);
} }
/** Delete current record pointed by cursor. This method can be called /**
* only after next() method, which returns non-null object.<P> * Delete current record pointed by cursor. This method can be called only
* after next() method, which returns non-null object.
* *
* If you are going to update or delete selected records, you should add * <P> If you are going to update or delete selected records, you should
* "for update" clause to select statement. So parameter of * add "for update" clause to select statement. So parameter of
* <CODE>jora.Table.select()</CODE> statement should contain "for update" * <CODE>jora.Table.select()</CODE> statement should contain "for update"
* clause: * clause: <CODE>record.table.Select("where name='xyz' for
* <CODE>record.table.Select("where name='xyz' for update");</CODE><P> * update");</CODE><P>
* *
* <I><B>Attention!</I></B> * <I><B>Attention!</I></B> Not all database drivers support delete
* Not all database drivers support delete operation with cursor. * operation with cursor. This method will not work with such database
* This method will not work with such database drivers. * drivers.
*/ */
public void delete () public void delete ()
throws SQLException throws SQLException
{ {
if (currObject == null) { if (_currObject == null) {
throw new NoCurrentObjectError(); throw new IllegalStateException("No current object");
} }
result.deleteRow(); _result.deleteRow();
} }
/** /**
* Close the Cursor, even if we haven't read all the possible * Close the Cursor, even if we haven't read all the possible objects.
* objects.
*/ */
public void close () public void close ()
throws SQLException throws SQLException
{ {
if (stmt != null) { if (_result != null) {
stmt.close(); // also automatically closes result _result.close();
result = null; _result = null;
stmt = null; }
if (_stmt != null) {
_stmt.close();
_stmt = null;
} }
// nTables = 0;
} }
/** Extracts no more than <I>maxElements</I> records from database and /**
* store them into array. It is possible to extract rest records * Extracts no more than <I>maxElements</I> records from database and store
* by successive next() or toArray() calls. Selected objects should * them into array. It is possible to extract rest records by successive
* have now components of InputStream, Blob or Clob type, because * next() or toArray() calls. Selected objects should have now components
* their data will be not available after fetching next record. * 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 * @param maxElements limitation for result array size (and also for number
* of fetched records) * of fetched records)
@@ -198,58 +198,46 @@ public class Cursor<V>
return al; return al;
} }
/** Store all objects returned by SELECT query into a list of Object. /**
* Selected objects should have now components of InputStream, Blob or * Store all objects returned by SELECT query into a list of Object.
* Clob type, because their data will be not available after fetching * Selected objects should have now components of InputStream, Blob or Clob
* next record. * type, because their data will be not available after fetching next
* record.
* *
* @return Array with objects constructed from fetched records. * @return Array with objects constructed from fetched records.
*/ */
public ArrayList<V> toArrayList() public ArrayList<V> toArrayList ()
throws SQLException throws SQLException
{ {
return toArrayList(Integer.MAX_VALUE); return toArrayList(Integer.MAX_VALUE);
} }
// Internals protected Cursor (Table<V> table, Connection conn, String query)
protected Cursor (
Table<V> table, Session session, int nTables, String query)
{ {
if (session == null) { _table = table;
session = ((SessionThread)Thread.currentThread()).session; _conn = conn;
} _query = query;
this.table = table;
this.session = session;
// this.nTables = nTables;
this.query = query;
} }
protected Cursor (Table<V> table, Session session, int nTables, V obj, protected Cursor (Table<V> table, Connection conn, V obj,
FieldMask mask, boolean like) FieldMask mask, boolean like)
{ {
if (session == null) { _table = table;
session = ((SessionThread)Thread.currentThread()).session; _conn = conn;
} _like = like;
this.table = table; _qbeObject = obj;
this.session = session; _qbeMask = mask;
// this.nTables = nTables; _query = table.buildQueryList(obj, mask, like);
this.like = like; _stmt = null;
qbeObject = obj;
qbeMask = mask;
query = table.buildQueryList(obj, mask, like);
stmt = null;
} }
private Table<V> table; protected Table<V> _table;
private Session session; protected Connection _conn;
// private int nTables; protected ResultSet _result;
private ResultSet result; protected String _query;
private String query; protected Statement _stmt;
private Statement stmt; protected V _currObject, _qbeObject;
private V currObject; protected FieldMask _qbeMask;
private V qbeObject; protected boolean _like;
private FieldMask qbeMask;
private boolean like;
} }
@@ -1,12 +1,22 @@
//-< FieldDescriptor.java >------------------------------------------*--------* //
// JORA Version 2.0 (c) 1998 GARRET * ? * // $Id$
// (Java Object Relational Adapter) * /\| * //
// * / \ * // samskivert library - useful routines for java programs
// Created: 10-Jun-98 K.A. Knizhnik * / [] \ * // Copyright (C) 2001-6 Konstantin Knizhnik, Michael Bayne
// Last update: 25-Jun-98 K.A. Knizhnik * GARRET * //
//-------------------------------------------------------------------*--------* // This library is free software; you can redistribute it and/or modify it
// Table field descriptor // under the terms of the GNU Lesser General Public License as published
//-------------------------------------------------------------------*--------* // by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.jora; package com.samskivert.jdbc.jora;
@@ -14,10 +24,370 @@ import java.sql.*;
import java.math.*; import java.math.*;
import java.lang.reflect.*; import java.lang.reflect.*;
class FieldDescriptor { class FieldDescriptor
{
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:
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;
}
protected int inType; // type tag for field input (see constants below) protected int inType; // type tag for field input (see constants below)
protected int outType; // type tag for field output protected int outType; // type tag for field output
protected int scale; // scale for tDecimal type, protected int scale; // scale for tDecimal type,
protected String name; // full (compound) name of component protected String name; // full (compound) name of component
protected Field field; // field info from java.lang.reflect protected Field field; // field info from java.lang.reflect
@@ -52,379 +422,30 @@ class FieldDescriptor {
protected static final int tCompound = 25; protected static final int tCompound = 25;
protected static final int[] sqlTypeMapping = { protected static final int[] sqlTypeMapping = {
Types.INTEGER, // t_byte Types.INTEGER, // t_byte
Types.INTEGER, // t_short Types.INTEGER, // t_short
Types.INTEGER, // t_int Types.INTEGER, // t_int
Types.BIGINT, // t_long Types.BIGINT, // t_long
Types.FLOAT, // t_float Types.FLOAT, // t_float
Types.DOUBLE, // t_double Types.DOUBLE, // t_double
Types.BIT, // t_boolean Types.BIT, // t_boolean
Types.INTEGER, // tByte Types.INTEGER, // tByte
Types.INTEGER, // tShort Types.INTEGER, // tShort
Types.INTEGER, // tInteger Types.INTEGER, // tInteger
Types.BIGINT, // tLong Types.BIGINT, // tLong
Types.FLOAT, // tFloat Types.FLOAT, // tFloat
Types.DOUBLE, // tDouble Types.DOUBLE, // tDouble
Types.BIT, // tBoolean Types.BIT, // tBoolean
Types.NUMERIC, // tDecimal Types.NUMERIC, // tDecimal
Types.VARCHAR, // tString Types.VARCHAR, // tString
Types.VARBINARY,// tBytes Types.VARBINARY,// tBytes
Types.DATE, // tDate Types.DATE, // tDate
Types.TIME, // tTime Types.TIME, // tTime
Types.TIMESTAMP, // tTimestamp Types.TIMESTAMP, // tTimestamp
Types.LONGVARBINARY, // tStream Types.LONGVARBINARY, // tStream
Types.LONGVARBINARY, // tBlob Types.LONGVARBINARY, // tBlob
Types.LONGVARCHAR, // tClob Types.LONGVARCHAR, // tClob
Types.VARCHAR, // tAsString Types.VARCHAR, // tAsString
Types.LONGVARBINARY // tClosure 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:
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;
}
} }
@@ -1,23 +0,0 @@
//-< 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");
}
}
@@ -1,22 +0,0 @@
//-< 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");
}
}
@@ -1,21 +0,0 @@
//-< 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;
}
}
@@ -1,198 +0,0 @@
//-< 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.*;
import com.samskivert.Log;
/**
* 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<String,PreparedStatement>();
connectionID = 0;
}
/** Construct a session with a pre-existing connection instance. In
* this case, {@link #open} should not be called on this session.
*
* @param connection the connection to use.
*/
public Session(Connection connection)
{
connectionID = 0;
preparedStmtHash = new Hashtable<String,PreparedStatement>();
setConnection(connection);
}
/** Session consructor for ODBC bridge driver
*/
public Session() { this("sun.jdbc.odbc.JdbcOdbcDriver"); }
/** Sets the connection that should be used by this session.
*/
public void setConnection(Connection conn)
{
// only up the connection id if this is a new connection
if (connection != conn) {
// clear out our prepared statement hash because we've got a
// new connection
for (PreparedStatement pstmt : preparedStmtHash.values()) {
try {
pstmt.close();
} catch (SQLException sqe) {
Log.warning("Error closing cached prepared statement " +
"[error=" + sqe + "].");
}
}
preparedStmtHash.clear();
// switch to our new connection
connection = conn;
connectionID += 1;
}
}
/** 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 dataSource 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
{
for (PreparedStatement pstmt : preparedStmtHash.values()) {
pstmt.close();
}
preparedStmtHash.clear();
if (connection != null) {
connection.close();
}
}
/**
* 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<String,PreparedStatement> preparedStmtHash;
protected int connectionID;
}
@@ -1,148 +0,0 @@
//-< 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;
}
}
File diff suppressed because it is too large Load Diff
@@ -39,7 +39,6 @@ import com.samskivert.jdbc.JORARepository;
import com.samskivert.jdbc.StaticConnectionProvider; import com.samskivert.jdbc.StaticConnectionProvider;
import com.samskivert.jdbc.jora.Cursor; import com.samskivert.jdbc.jora.Cursor;
import com.samskivert.jdbc.jora.FieldMask; import com.samskivert.jdbc.jora.FieldMask;
import com.samskivert.jdbc.jora.Session;
import com.samskivert.jdbc.jora.Table; import com.samskivert.jdbc.jora.Table;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
@@ -134,30 +133,15 @@ public class UserRepository extends JORARepository
* @return the user associated with the specified session or null of * @return the user associated with the specified session or null of
* no session exists with the supplied identifier. * no session exists with the supplied identifier.
*/ */
public User loadUserBySession (final String sessionKey) public User loadUserBySession (String sessionKey)
throws PersistenceException throws PersistenceException
{ {
return execute(new Operation<User>() { User user = load(_utable, "where authcode = '" + sessionKey + "' AND " +
public User invoke (Connection conn, DatabaseLiaison liaison) "sessions.userId = users.userId");
throws PersistenceException, SQLException if (user != null) {
{ user.setDirtyMask(_utable.getFieldMask());
String query = "where authcode = '" + sessionKey + }
"' AND sessions.userId = users.userId"; return user;
// look up the user
Cursor<User> ec = _utable.select("sessions", query);
// fetch the user from the cursor
User user = ec.next();
if (user != null) {
// call next() again to cause the cursor to close itself
ec.next();
// configure the user record with its field mask
user.setDirtyMask(_utable.getFieldMask());
}
return user;
}
});
} }
/** /**
@@ -168,29 +152,15 @@ public class UserRepository extends JORARepository
public HashIntMap<User> loadUsersFromId (int[] userIds) public HashIntMap<User> loadUsersFromId (int[] userIds)
throws PersistenceException throws PersistenceException
{ {
// make sure we actually have something to do HashIntMap<User> data = new HashIntMap<User>();
if (userIds.length == 0) { if (userIds.length > 0) {
return new HashIntMap<User>(); String query = "where userid in (" + genIdString(userIds) + ")";
} for (User user : loadAll(_utable, query)) {
user.setDirtyMask(_utable.getFieldMask());
final String ids = genIdString(userIds); data.put(user.userId, user);
return execute(new Operation<HashIntMap<User>>() {
public HashIntMap<User> invoke (Connection conn,
DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
Cursor<User> ec = _utable.select(
"where userid in (" + ids + ")");
User user;
HashIntMap<User> data = new HashIntMap<User>();
while ((user = ec.next()) != null) {
user.setDirtyMask(_utable.getFieldMask());
data.put(user.userId, user);
}
return data;
} }
}); }
return data;
} }
/** /**
@@ -204,16 +174,7 @@ public class UserRepository extends JORARepository
public ArrayList<User> lookupUsersByEmail (String email) public ArrayList<User> lookupUsersByEmail (String email)
throws PersistenceException throws PersistenceException
{ {
final String where = "where email = " + return loadAll(_utable, "where email = " + JDBCUtil.escape(email));
JDBCUtil.escape(email);
return execute(new Operation<ArrayList<User>>() {
public ArrayList<User> invoke (Connection conn,
DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
return _utable.select(where).toArrayList();
}
});
} }
/** /**
@@ -227,20 +188,12 @@ public class UserRepository extends JORARepository
public ArrayList<User> lookupUsersWhere (final String where) public ArrayList<User> lookupUsersWhere (final String where)
throws PersistenceException throws PersistenceException
{ {
return execute(new Operation<ArrayList<User>>() { ArrayList<User> users = loadAll(_utable, where);
public ArrayList<User> invoke ( for (User user : users) {
Connection conn, DatabaseLiaison liaison) // configure the user record with its field mask
throws PersistenceException, SQLException user.setDirtyMask(_utable.getFieldMask());
{ }
ArrayList<User> users = return users;
_utable.select("where " + where).toArrayList();
for (User user : users) {
// configure the user record with its field mask
user.setDirtyMask(_utable.getFieldMask());
}
return users;
}
});
} }
/** /**
@@ -259,17 +212,7 @@ public class UserRepository extends JORARepository
// nothing doing! // nothing doing!
return false; return false;
} }
update(_utable, user, user.getDirtyMask());
executeUpdate(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_utable.update(user, user.getDirtyMask());
// nothing to return
return null;
}
});
return true; return true;
} }
@@ -317,7 +260,7 @@ public class UserRepository extends JORARepository
try { try {
user.username = StringUtil.truncate( user.username = StringUtil.truncate(
ii + "=" + oldName, 24); ii + "=" + oldName, 24);
_utable.update(user, mask); _utable.update(conn, user, mask);
return null; // nothing to return return null; // nothing to return
} catch (SQLException se) { } catch (SQLException se) {
if (!liaison.isDuplicateRowException(se)) { if (!liaison.isDuplicateRowException(se)) {
@@ -350,18 +293,8 @@ public class UserRepository extends JORARepository
final Date expires = new Date(cal.getTime().getTime()); final Date expires = new Date(cal.getTime().getTime());
// insert the session into the database // insert the session into the database
executeUpdate(new Operation<Object>() { update("insert into sessions (authcode, userId, expires) values('" +
public Object invoke (Connection conn, DatabaseLiaison liaison) authcode + "', " + user.userId + ", '" + expires + "')");
throws PersistenceException, SQLException
{
_session.execute("insert into sessions " +
"(authcode, userId, expires) values('" +
authcode + "', " + user.userId + ", '" +
expires + "')");
// nothing to return
return null;
}
});
// and let the user know what the session identifier is // and let the user know what the session identifier is
return authcode; return authcode;
@@ -373,16 +306,7 @@ public class UserRepository extends JORARepository
public void pruneSessions () public void pruneSessions ()
throws PersistenceException throws PersistenceException
{ {
executeUpdate(new Operation<Object>() { update("delete from sessions where expires <= CURRENT_DATE()");
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_session.execute("delete from sessions where " +
"expires <= CURRENT_DATE()");
// nothing to return
return null;
}
});
} }
/** /**
@@ -420,7 +344,7 @@ public class UserRepository extends JORARepository
public Object invoke (Connection conn, DatabaseLiaison liaison) public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException throws PersistenceException, SQLException
{ {
Statement stmt = _session.connection.createStatement(); Statement stmt = conn.createStatement();
try { try {
String query = "select realname from users"; String query = "select realname from users";
ResultSet rs = stmt.executeQuery(query); ResultSet rs = stmt.executeQuery(query);
@@ -469,7 +393,7 @@ public class UserRepository extends JORARepository
throws PersistenceException, SQLException throws PersistenceException, SQLException
{ {
try { try {
_utable.insert(user); _utable.insert(conn, user);
// update the userid now that it's known // update the userid now that it's known
user.userId = liaison.lastInsertedId(conn); user.userId = liaison.lastInsertedId(conn);
// nothing to return // nothing to return
@@ -489,31 +413,17 @@ public class UserRepository extends JORARepository
} }
/** /**
* Loads up a user record that matches the specified where clause. * Loads up a user record that matches the specified where clause. Returns
* Returns null if no record matches. * null if no record matches.
*/ */
protected User loadUserWhere (final String whereClause) protected User loadUserWhere (String where)
throws PersistenceException throws PersistenceException
{ {
return execute(new Operation<User>() { User user = load(_utable, where);
public User invoke (Connection conn, DatabaseLiaison liaison) if (user != null) {
throws PersistenceException, SQLException user.setDirtyMask(_utable.getFieldMask());
{ }
// look up the user return user;
Cursor<User> ec = _utable.select(whereClause);
// fetch the user from the cursor
User user = ec.next();
if (user != null) {
// call next() again to cause the cursor to close itself
ec.next();
// configure the user record with its field mask
user.setDirtyMask(_utable.getFieldMask());
}
return user;
}
});
} }
protected String[] loadNames (int[] userIds, final String column) protected String[] loadNames (int[] userIds, final String column)
@@ -524,16 +434,14 @@ public class UserRepository extends JORARepository
return new String[0]; return new String[0];
} }
final String ids = genIdString(userIds);
final HashIntMap<String> map = new HashIntMap<String>();
// do the query // do the query
final String ids = genIdString(userIds);
final HashIntMap<String> map = new HashIntMap<String>();
execute(new Operation<Object>() { execute(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison) public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException throws PersistenceException, SQLException
{ {
Statement stmt = _session.connection.createStatement(); Statement stmt = conn.createStatement();
try { try {
String query = "select userId, " + column + String query = "select userId, " + column +
" from users " + " from users " +
@@ -559,14 +467,13 @@ public class UserRepository extends JORARepository
for (int i = 0; i < userIds.length; i++) { for (int i = 0; i < userIds.length; i++) {
result[i] = map.get(userIds[i]); result[i] = map.get(userIds[i]);
} }
return result; return result;
} }
/** /**
* Take the passed in int array and create the a string suitable for * Take the passed in int array and create the a string suitable for using
* using in a SQL set query (I.e., "select foo, from bar where userid * in a SQL set query (I.e., "select foo, from bar where userid in
* in (genIdString(userIds))"; ) * (genIdString(userIds))"; )
*/ */
protected String genIdString (int[] userIds) protected String genIdString (int[] userIds)
{ {
@@ -583,10 +490,10 @@ public class UserRepository extends JORARepository
} }
// documentation inherited // documentation inherited
protected void createTables (Session session) protected void createTables ()
{ {
// create our table object // create our table object
_utable = new Table<User>(User.class, "users", session, "userId"); _utable = new Table<User>(User.class, "users", "userId");
} }
/** A wrapper that provides access to the userstable. */ /** A wrapper that provides access to the userstable. */