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
* 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
{
@@ -47,17 +47,12 @@ public abstract class JORARepository extends SimpleRepository
{
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
createTables(_session);
createTables();
}
/**
* Inserts the supplied object into the specified table. The table must be
* configured to store items of the supplied type.
* Inserts the supplied object into the specified table.
*
* @return a call to {@link DatabaseLiaison#lastInsertedId} made
* immediately following the insert.
@@ -69,15 +64,14 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
table.insert(object);
table.insert(conn, object);
return liaison.lastInsertedId(conn);
}
});
}
/**
* Updates the supplied object in the specified table. The table must
* be configured to store items of the supplied type.
* Updates the supplied object in the specified table.
*
* @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)
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)
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)
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)
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)
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.
*/
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
{
return execute(new Operation<T>() {
public T invoke (Connection conn, DatabaseLiaison liaison)
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)
throws SQLException, PersistenceException
{
if (table.update(object) == 0) {
table.insert(object);
if (table.update(conn, object) == 0) {
table.insert(conn, object);
return liaison.lastInsertedId(conn);
}
return -1;
@@ -221,7 +288,7 @@ public abstract class JORARepository extends SimpleRepository
public Integer invoke (Connection conn, DatabaseLiaison liaison)
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)
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)
throws SQLException, PersistenceException
{
return table.delete(object);
return table.delete(conn, object);
}
});
}
/**
* After the database session is begun, this function will be called
* to give the repository implementation the opportunity to create its
* table objects.
*
* @param session the session instance to use when creating your table
* instances.
* During construction, this function will be called to give the repository
* implementation the opportunity to create its table objects.
*/
protected abstract void createTables (Session session);
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;
protected abstract void createTables ();
}
@@ -46,7 +46,7 @@ public class MySQLLiaison implements DatabaseLiaison
String msg = sqe.getMessage();
return (msg != null &&
(msg.indexOf("Lost connection") != -1 ||
msg.indexOf("Communication link failure") != -1 ||
msg.indexOf("link failure") != -1 ||
msg.indexOf("Broken pipe") != -1));
}
+139 -151
View File
@@ -1,12 +1,22 @@
//-< 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
//-------------------------------------------------------------------*--------*
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-6 Konstantin Knizhnik, Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// 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;
@@ -15,81 +25,67 @@ import java.sql.*;
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
* (polymorphic form of select), this class can issue several requests
* to database. Cursor also provides methods for updating/deleting current
* record.
/**
* 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<V>
{
/**
* 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.
* 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.
* <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
* @return object constructed from fetched record or null if there are no
* more rows
*/
public V next ()
throws SQLException
{
// // if we closed everything up after the last call to next(),
// // table will be null here and we should bail immediately
if (table == null) {
{
// if we closed everything up after the last call to next(),
// table will be null here and we should bail immediately
if (_table == null) {
return null;
}
// if (nTables == 0) {
// return null;
// }
// do {
if (result == null) {
// if (table.isAbstract) {
// table = table.derived;
// continue;
// }
if (qbeObject != null) {
PreparedStatement 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;
}
if (_result == null) {
if (_qbeObject != null) {
PreparedStatement qbeStmt;
synchronized (_table.preparedStmtHash) {
qbeStmt = _table.preparedStmtHash.get(_query);
if (qbeStmt == null) {
qbeStmt = _conn.prepareStatement(_query);
_table.preparedStmtHash.put(_query, qbeStmt);
}
synchronized(qbeStmt) {
table.bindQueryVariables(
qbeStmt, qbeObject, qbeMask);
result = qbeStmt.executeQuery();
qbeStmt.clearParameters();
}
} else {
if (stmt == null) {
stmt = session.connection.createStatement();
}
result = stmt.executeQuery(query);
}
}
synchronized (qbeStmt) {
_table.bindQueryVariables(qbeStmt, _qbeObject, _qbeMask);
_result = qbeStmt.executeQuery();
qbeStmt.clearParameters();
}
} else {
if (_stmt == null) {
_stmt = _conn.createStatement();
}
_result = _stmt.executeQuery(_query);
}
if (result.next()) {
return currObject = table.load(result);
}
result.close();
result = null;
currObject = null;
table = null;
// table = table.derived;
// } while (--nTables != 0);
}
if (_result.next()) {
return _currObject = _table.load(_result);
}
if (stmt != null) {
stmt.close();
_result.close();
_result = null;
_currObject = null;
_table = null;
if (_stmt != null) {
_stmt.close();
}
return null;
}
@@ -111,77 +107,81 @@ public class Cursor<V>
if (spurious > 0) {
Log.warning("Cursor.get() quietly tossed " + spurious +
" spurious additional records. " +
"[query=" + query + "].");
"[query=" + _query + "].");
}
}
return result;
}
/** 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>
/**
* 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.
*
* 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.
* <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();
if (_currObject == null) {
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
* "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.
* <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();
if (_currObject == null) {
throw new IllegalStateException("No current object");
}
result.deleteRow();
_result.deleteRow();
}
/**
* Close the Cursor, even if we haven't read all the possible
* objects.
* Close the Cursor, even if we haven't read all the possible objects.
*/
public void close ()
throws SQLException
{
if (stmt != null) {
stmt.close(); // also automatically closes result
result = null;
stmt = null;
if (_result != null) {
_result.close();
_result = 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
* 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.
/**
* 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)
@@ -198,58 +198,46 @@ public class Cursor<V>
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.
/**
* 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 ArrayList<V> toArrayList()
public ArrayList<V> toArrayList ()
throws SQLException
{
return toArrayList(Integer.MAX_VALUE);
}
// Internals
protected Cursor (
Table<V> table, Session session, int nTables, String query)
protected Cursor (Table<V> table, Connection conn, String query)
{
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
this.table = table;
this.session = session;
// this.nTables = nTables;
this.query = query;
_table = table;
_conn = conn;
_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)
{
if (session == null) {
session = ((SessionThread)Thread.currentThread()).session;
}
this.table = table;
this.session = session;
// this.nTables = nTables;
this.like = like;
qbeObject = obj;
qbeMask = mask;
query = table.buildQueryList(obj, mask, like);
stmt = null;
{
_table = table;
_conn = conn;
_like = like;
_qbeObject = obj;
_qbeMask = mask;
_query = table.buildQueryList(obj, mask, like);
_stmt = null;
}
private Table<V> table;
private Session session;
// private int nTables;
private ResultSet result;
private String query;
private Statement stmt;
private V currObject;
private V qbeObject;
private FieldMask qbeMask;
private boolean like;
protected Table<V> _table;
protected Connection _conn;
protected ResultSet _result;
protected String _query;
protected Statement _stmt;
protected V _currObject, _qbeObject;
protected FieldMask _qbeMask;
protected boolean _like;
}
@@ -1,12 +1,22 @@
//-< 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
//-------------------------------------------------------------------*--------*
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-6 Konstantin Knizhnik, Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// 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;
@@ -14,10 +24,370 @@ import java.sql.*;
import java.math.*;
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 outType; // type tag for field output
protected int scale; // scale for tDecimal type,
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
@@ -52,379 +422,30 @@ class FieldDescriptor {
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
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:
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.jora.Cursor;
import com.samskivert.jdbc.jora.FieldMask;
import com.samskivert.jdbc.jora.Session;
import com.samskivert.jdbc.jora.Table;
import com.samskivert.util.HashIntMap;
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
* no session exists with the supplied identifier.
*/
public User loadUserBySession (final String sessionKey)
public User loadUserBySession (String sessionKey)
throws PersistenceException
{
return execute(new Operation<User>() {
public User invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
String query = "where authcode = '" + sessionKey +
"' AND sessions.userId = users.userId";
// 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;
}
});
User user = load(_utable, "where authcode = '" + sessionKey + "' AND " +
"sessions.userId = users.userId");
if (user != null) {
user.setDirtyMask(_utable.getFieldMask());
}
return user;
}
/**
@@ -168,29 +152,15 @@ public class UserRepository extends JORARepository
public HashIntMap<User> loadUsersFromId (int[] userIds)
throws PersistenceException
{
// make sure we actually have something to do
if (userIds.length == 0) {
return new HashIntMap<User>();
}
final String ids = genIdString(userIds);
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;
HashIntMap<User> data = new HashIntMap<User>();
if (userIds.length > 0) {
String query = "where userid in (" + genIdString(userIds) + ")";
for (User user : loadAll(_utable, query)) {
user.setDirtyMask(_utable.getFieldMask());
data.put(user.userId, user);
}
});
}
return data;
}
/**
@@ -204,16 +174,7 @@ public class UserRepository extends JORARepository
public ArrayList<User> lookupUsersByEmail (String email)
throws PersistenceException
{
final String where = "where 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();
}
});
return loadAll(_utable, "where email = " + JDBCUtil.escape(email));
}
/**
@@ -227,20 +188,12 @@ public class UserRepository extends JORARepository
public ArrayList<User> lookupUsersWhere (final String where)
throws PersistenceException
{
return execute(new Operation<ArrayList<User>>() {
public ArrayList<User> invoke (
Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
ArrayList<User> users =
_utable.select("where " + where).toArrayList();
for (User user : users) {
// configure the user record with its field mask
user.setDirtyMask(_utable.getFieldMask());
}
return users;
}
});
ArrayList<User> users = loadAll(_utable, where);
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!
return false;
}
executeUpdate(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_utable.update(user, user.getDirtyMask());
// nothing to return
return null;
}
});
update(_utable, user, user.getDirtyMask());
return true;
}
@@ -317,7 +260,7 @@ public class UserRepository extends JORARepository
try {
user.username = StringUtil.truncate(
ii + "=" + oldName, 24);
_utable.update(user, mask);
_utable.update(conn, user, mask);
return null; // nothing to return
} catch (SQLException se) {
if (!liaison.isDuplicateRowException(se)) {
@@ -350,18 +293,8 @@ public class UserRepository extends JORARepository
final Date expires = new Date(cal.getTime().getTime());
// insert the session into the database
executeUpdate(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_session.execute("insert into sessions " +
"(authcode, userId, expires) values('" +
authcode + "', " + user.userId + ", '" +
expires + "')");
// nothing to return
return null;
}
});
update("insert into sessions (authcode, userId, expires) values('" +
authcode + "', " + user.userId + ", '" + expires + "')");
// and let the user know what the session identifier is
return authcode;
@@ -373,16 +306,7 @@ public class UserRepository extends JORARepository
public void pruneSessions ()
throws PersistenceException
{
executeUpdate(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_session.execute("delete from sessions where " +
"expires <= CURRENT_DATE()");
// nothing to return
return null;
}
});
update("delete from sessions where expires <= CURRENT_DATE()");
}
/**
@@ -420,7 +344,7 @@ public class UserRepository extends JORARepository
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
Statement stmt = _session.connection.createStatement();
Statement stmt = conn.createStatement();
try {
String query = "select realname from users";
ResultSet rs = stmt.executeQuery(query);
@@ -469,7 +393,7 @@ public class UserRepository extends JORARepository
throws PersistenceException, SQLException
{
try {
_utable.insert(user);
_utable.insert(conn, user);
// update the userid now that it's known
user.userId = liaison.lastInsertedId(conn);
// nothing to return
@@ -489,31 +413,17 @@ public class UserRepository extends JORARepository
}
/**
* Loads up a user record that matches the specified where clause.
* Returns null if no record matches.
* Loads up a user record that matches the specified where clause. Returns
* null if no record matches.
*/
protected User loadUserWhere (final String whereClause)
protected User loadUserWhere (String where)
throws PersistenceException
{
return execute(new Operation<User>() {
public User invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
// look up the 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;
}
});
User user = load(_utable, where);
if (user != null) {
user.setDirtyMask(_utable.getFieldMask());
}
return user;
}
protected String[] loadNames (int[] userIds, final String column)
@@ -524,16 +434,14 @@ public class UserRepository extends JORARepository
return new String[0];
}
final String ids = genIdString(userIds);
final HashIntMap<String> map = new HashIntMap<String>();
// do the query
final String ids = genIdString(userIds);
final HashIntMap<String> map = new HashIntMap<String>();
execute(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
Statement stmt = _session.connection.createStatement();
Statement stmt = conn.createStatement();
try {
String query = "select userId, " + column +
" from users " +
@@ -559,14 +467,13 @@ public class UserRepository extends JORARepository
for (int i = 0; i < userIds.length; i++) {
result[i] = map.get(userIds[i]);
}
return result;
}
/**
* Take the passed in int array and create the a string suitable for
* using in a SQL set query (I.e., "select foo, from bar where userid
* in (genIdString(userIds))"; )
* Take the passed in int array and create the a string suitable for using
* in a SQL set query (I.e., "select foo, from bar where userid in
* (genIdString(userIds))"; )
*/
protected String genIdString (int[] userIds)
{
@@ -583,10 +490,10 @@ public class UserRepository extends JORARepository
}
// documentation inherited
protected void createTables (Session session)
protected void createTables ()
{
// 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. */