Index: doc/release-notes.xml =================================================================== RCS file: /scm/castor/castor/src/doc/release-notes.xml,v retrieving revision 1.67 diff -u -r1.67 release-notes.xml --- doc/release-notes.xml 20 Jun 2005 12:00:53 -0000 1.67 +++ doc/release-notes.xml 28 Jun 2005 21:38:22 -0000 @@ -30,6 +30,24 @@ + + + + Added support for polymorphism. + + + Werner Guttmann + werner.guttmann@gmx.net + + + Werner Guttmann + werner.guttmann@gmx.net + + Werner Guttmann + Bug + JDO + 20050626 + Complete rewrite of TransactionContext with regards to obejct tracking. Index: etc/CHANGELOG =================================================================== RCS file: /scm/castor/castor/src/etc/CHANGELOG,v retrieving revision 1.230 diff -u -r1.230 CHANGELOG --- etc/CHANGELOG 20 Jun 2005 12:00:57 -0000 1.230 +++ etc/CHANGELOG 28 Jun 2005 21:38:34 -0000 @@ -2,6 +2,11 @@ Version (CVS) ------------- +JDO: Fixed bug CASTOR-1018 using contribution from Werner Guttmann[werner.guttmann@gmx.net] + Added support for polymorphism. + Details: http://jira.codehaus.org/browse/CASTOR-1018 + (Werner - 20050626) + JDO: Fixed bug CASTOR-1085 using contribution from Gregory Block [gblock@ctoforaday.com] Complete rewrite of TransactionContext with regards to obejct tracking. Details: http://jira.codehaus.org/browse/CASTOR-1085 Index: main/org/castor/persist/TransactionContext.java =================================================================== RCS file: /scm/castor/castor/src/main/org/castor/persist/TransactionContext.java,v retrieving revision 1.1 diff -u -r1.1 TransactionContext.java --- main/org/castor/persist/TransactionContext.java 20 Jun 2005 12:00:59 -0000 1.1 +++ main/org/castor/persist/TransactionContext.java 28 Jun 2005 21:38:39 -0000 @@ -548,10 +548,10 @@ public synchronized Object load(final LockEngine engine, final ClassMolder molder, final Object identity, - Object objectToBeLoaded, + ProposedObject proposedObject, final AccessMode suggestedAccessMode) throws ObjectNotFoundException, LockNotGrantedException, PersistenceException { - return load(engine, molder, identity, objectToBeLoaded, + return load(engine, molder, identity, proposedObject, suggestedAccessMode, null); } @@ -571,7 +571,7 @@ * The class persistence molder * @param identity * The object's identity - * @param objectToBeLoaded + * @param proposedObject * The object to fetch (single instance per transaction) * @param suggestedAccessMode * The access mode (see {@link AccessMode}) the values in @@ -590,7 +590,7 @@ public synchronized Object load(final LockEngine engine, final ClassMolder molder, final Object identity, - Object objectToBeLoaded, + ProposedObject proposedObject, final AccessMode suggestedAccessMode, QueryResults results) throws ObjectNotFoundException, LockNotGrantedException, PersistenceException { @@ -605,12 +605,11 @@ // Test that the object to be loaded (which we will fill in) is of an // appropriate type for our molder. - if (objectToBeLoaded != null + if (proposedObject.getObject() != null && !molder.getJavaClass(_db.getClassLoader()).isAssignableFrom( - objectToBeLoaded.getClass())) { + proposedObject.getProposedClass())) { throw new PersistenceException(Messages.format( - "persist.typeMismatch", molder.getName(), objectToBeLoaded - .getClass())); + "persist.typeMismatch", molder.getName(), proposedObject.getProposedClass())); } oid = new OID(engine, molder, identity); @@ -627,8 +626,10 @@ // If the object has been loaded, but the instance sugguested to // be loaded into is not the same as the loaded instance, // error is reported. - if (objectToBeLoaded != null - && objectToBeLoaded != objectInTransaction) { + + // TODO [WG]: could read && propsedObject != objectInTransaction + if (proposedObject.getObject() != null + && proposedObject.getObject() != objectInTransaction) { throw new PersistenceException(Messages.format( "persist.multipleLoad", molder.getName(), identity)); } @@ -687,13 +688,13 @@ return objectInTransaction; } - // Load (or reload) the object through the persistence engine with the - // requested lock. This might report failure (object no longer exists), - // hold until a suitable lock is granted (or fail to grant), or + // Load (or reload, in case the object is stored in a acache) the object through the + // persistence engine with the requested lock. This might report failure (object no + // longer exists), hold until a suitable lock is granted (or fail to grant), or // report error with the persistence engine. try { - if (objectToBeLoaded != null) { - objectInTransaction = objectToBeLoaded; + if (proposedObject.getObject() != null) { + objectInTransaction = proposedObject.getObject(); } else { // ssa, multi classloader feature // ssa, FIXME : No better way to do that ? @@ -705,15 +706,29 @@ objectInTransaction = molder.newInstance(_db .getClassLoader()); } + + proposedObject.setProposedClass(objectInTransaction.getClass()); + proposedObject.setActualClass(objectInTransaction.getClass()); + proposedObject.setObject(objectInTransaction); } _tracker.trackObject(engine, molder, oid, objectInTransaction); - OID newoid = engine.load(this, oid, objectInTransaction, + OID newoid = engine.load(this, oid, proposedObject, suggestedAccessMode, _lockTimeout, results); - - // rehash the object entry, because oid might have changed! - _tracker.trackOIDChange(objectInTransaction, engine, oid, newoid); - + + if (proposedObject.isExpanded()) { + // Remove old OID from ObjectTracker + _tracker.untrackObject(objectInTransaction); + // Create new OID + OID newOID = new OID(engine, proposedObject.getActualClassMolder(), identity); + // Add new OID to ObjectTracker + _tracker.trackObject(engine, molder, oid, proposedObject.getObject()); + objectInTransaction = proposedObject.getObject(); + } else { + // rehash the object entry, because oid might have changed! + _tracker.trackOIDChange(objectInTransaction, engine, oid, newoid); + } + } catch (ObjectNotFoundException except) { _tracker.untrackObject(objectInTransaction); throw except; Index: main/org/exolab/castor/jdo/drivers/ConnectionProxy.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/drivers/ConnectionProxy.java,v retrieving revision 1.1 diff -u -r1.1 ConnectionProxy.java --- main/org/exolab/castor/jdo/drivers/ConnectionProxy.java 3 May 2005 15:26:47 -0000 1.1 +++ main/org/exolab/castor/jdo/drivers/ConnectionProxy.java 28 Jun 2005 21:38:41 -0000 @@ -22,16 +22,18 @@ */ public class ConnectionProxy implements java.sql.Connection { - /** - * Default calling location, equals 'unknwon'. - */ + /** Default calling location, equals 'unknwon'. */ private static final String DEFAULT_CALLED_BY = "unknown"; - /** - * Jakarta Common Log instance. - */ + /** Jakarta Common Log instance. */ private static final Log _log = LogFactory.getLog(ConnectionProxy.class); + /** Has property of LocalConfiguration been read? */ + private static boolean _isConfigured = false; + + /** Should connections been wrapped by a proxy? */ + private static boolean _useProxies = false; + /** * The JDBC Connection instance to proxy. */ @@ -47,15 +49,8 @@ * @param connection The JDBC connection to proxy. * @return The JDBC connection proxy. */ - public static Connection newConnectionProxy (Connection connection) { - boolean useProxies = Boolean.getBoolean(LocalConfiguration.getInstance().getProperty("org.exolab.castor.persist.useProxies", "true")); - - if (useProxies) { - return new ConnectionProxy (connection, DEFAULT_CALLED_BY); - } - - return connection; - + public static Connection newConnectionProxy(Connection connection) { + return newConnectionProxy(connection, DEFAULT_CALLED_BY); } /** @@ -64,8 +59,19 @@ * @param calledBy Name of the class using creating and this proxy class. * @return The JDBC connection proxy. */ - public static ConnectionProxy newConnectionProxy (Connection connection, String calledBy) { - return new ConnectionProxy (connection, calledBy); + public static Connection newConnectionProxy(Connection connection, String calledBy) { + if (!_isConfigured) { + String propertyValue = LocalConfiguration.getInstance().getProperty( + "org.exolab.castor.persist.useProxies", "true"); + _useProxies = Boolean.valueOf(propertyValue).booleanValue(); + _isConfigured = true; + } + + if (!_useProxies) { + return connection; + } else { + return new ConnectionProxy(connection, calledBy); + } } /** Index: main/org/exolab/castor/jdo/drivers/MultiRSCallQuery.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/drivers/MultiRSCallQuery.java,v retrieving revision 1.4 diff -u -r1.4 MultiRSCallQuery.java --- main/org/exolab/castor/jdo/drivers/MultiRSCallQuery.java 25 Mar 2004 12:31:06 -0000 1.4 +++ main/org/exolab/castor/jdo/drivers/MultiRSCallQuery.java 28 Jun 2005 21:38:41 -0000 @@ -54,6 +54,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.castor.persist.ProposedObject; import org.exolab.castor.jdo.ObjectNotFoundException; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.jdo.QueryException; @@ -235,10 +236,9 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException - { - Object stamp = null; - + public Object fetch(ProposedObject proposedObject, Object identity) + throws ObjectNotFoundException, PersistenceException { + Object[] fields = proposedObject.getFields(); try { // Load all the fields of the object including one-one relations // index 0 belongs to the identity @@ -251,7 +251,7 @@ } catch ( SQLException except ) { throw new PersistenceException( Messages.format( "persist.nested", except ) ); } - return stamp; + return null; } } Index: main/org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java,v retrieving revision 1.4 diff -u -r1.4 PostgreSQLCallQuery.java --- main/org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java 25 Mar 2004 12:32:01 -0000 1.4 +++ main/org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java 28 Jun 2005 21:38:42 -0000 @@ -54,6 +54,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.castor.persist.ProposedObject; import org.exolab.castor.jdo.ObjectNotFoundException; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.jdo.QueryException; @@ -248,12 +249,10 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException - { - Object stamp = null; - + public Object fetch(ProposedObject proposedObject, Object identity) + throws ObjectNotFoundException, PersistenceException { + Object[] fields = proposedObject.getFields(); try { - // Load all the fields of the object including one-one relations // index 0 belongs to the identity for ( int i = 1 ; i < _sqlTypes.length ; ++i ) @@ -265,7 +264,7 @@ } catch ( SQLException except ) { throw new PersistenceException( Messages.format( "persist.nested", except ) ); } - return stamp; + return null; } } Index: main/org/exolab/castor/jdo/drivers/PreparedStatementProxy.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/drivers/PreparedStatementProxy.java,v retrieving revision 1.1 diff -u -r1.1 PreparedStatementProxy.java --- main/org/exolab/castor/jdo/drivers/PreparedStatementProxy.java 3 May 2005 15:26:47 -0000 1.1 +++ main/org/exolab/castor/jdo/drivers/PreparedStatementProxy.java 28 Jun 2005 21:38:43 -0000 @@ -36,34 +36,28 @@ */ public class PreparedStatementProxy implements PreparedStatement { - /** - * Commons logger. - */ + /** Commons logger. */ private static final Log log = LogFactory.getLog (PreparedStatementProxy.class); - /** - * PreparedStatement to be proxied. - */ + /** Has property of LocalConfiguration been read? */ + private static boolean _isConfigured = false; + + /** Should connections been wrapped by a proxy? */ + private static boolean _useProxies = false; + + /** PreparedStatement to be proxied. */ private PreparedStatement preparedStatement; - /** - * Connection instance associated with this PreparedStatement - */ + /** Connection instance associated with this PreparedStatement */ private Connection connection; - /** - * SQL Parameter mapping - */ + /** SQL Parameter mapping */ private Map parameters = new HashMap(); - /** - * The SQL statement to be executed - */ + /** The SQL statement to be executed */ private String sqlStatement = null; - /** - * List of batch statements associated with this instance. - */ + /** List of batch statements associated with this instance. */ private List batchStatements = new ArrayList(); /** @@ -73,14 +67,21 @@ * @param connection JDBC connection * @return Prepared statement proxy. */ - public static PreparedStatement newPreparedStatementProxy (PreparedStatement statement, String sql, Connection connection) { - boolean useProxies = Boolean.getBoolean(LocalConfiguration.getInstance().getProperty("org.exolab.castor.persist.useProxies", "true")); + public static PreparedStatement newPreparedStatementProxy( + PreparedStatement statement, String sql, Connection connection) { + + if (!_isConfigured) { + String propertyValue = LocalConfiguration.getInstance().getProperty( + "org.exolab.castor.persist.useProxies", "true"); + _useProxies = Boolean.valueOf(propertyValue).booleanValue(); + _isConfigured = true; + } - if (useProxies) { - return new PreparedStatementProxy (statement, sql, connection); - } - - return statement; + if (!_useProxies) { + return statement; + } else { + return new PreparedStatementProxy(statement, sql, connection); + } } /** Index: main/org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java,v retrieving revision 1.3 diff -u -r1.3 ReturnedRSCallQuery.java --- main/org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java 25 Mar 2004 12:24:22 -0000 1.3 +++ main/org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java 28 Jun 2005 21:38:44 -0000 @@ -54,6 +54,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.castor.persist.ProposedObject; import org.exolab.castor.jdo.ObjectNotFoundException; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.jdo.QueryException; @@ -228,12 +229,10 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException - { - Object stamp = null; - + public Object fetch(ProposedObject proposedObject, Object identity) + throws ObjectNotFoundException, PersistenceException { + Object[] fields = proposedObject.getFields(); try { - // Load all the fields of the object including one-one relations // index 0 belongs to the identity for ( int i = 1 ; i < _sqlTypes.length ; ++i ) @@ -245,7 +244,7 @@ } catch ( SQLException except ) { throw new PersistenceException( Messages.format( "persist.nested", except ) ); } - return stamp; + return null; } } Index: main/org/exolab/castor/jdo/engine/BaseFactory.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/BaseFactory.java,v retrieving revision 1.3 diff -u -r1.3 BaseFactory.java --- main/org/exolab/castor/jdo/engine/BaseFactory.java 19 Jan 2004 11:52:47 -0000 1.3 +++ main/org/exolab/castor/jdo/engine/BaseFactory.java 28 Jun 2005 21:38:44 -0000 @@ -47,6 +47,9 @@ package org.exolab.castor.jdo.engine; +import java.util.HashMap; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exolab.castor.mapping.ClassDescriptor; @@ -73,17 +76,27 @@ * Commons Logging instance used for all logging. */ private static Log _log = LogFactory.getFactory().getInstance( BaseFactory.class ); + + /** + * Maps class descriptor to persistence engines .... + */ + private Map classDescriptorToPersistence = new HashMap(); - - public Persistence getPersistence( ClassDescriptor clsDesc ) - throws MappingException - { - if ( ! ( clsDesc instanceof JDOClassDescriptor ) ) - return null; + /** + * @see org.exolab.castor.persist.spi.PersistenceFactory#getPersistence(org.exolab.castor.mapping.ClassDescriptor) + */ + public Persistence getPersistence(final ClassDescriptor clsDesc) { + if (!(clsDesc instanceof JDOClassDescriptor)) { return null; } + try { - return new SQLEngine( (JDOClassDescriptor) clsDesc, this, null); - } catch ( MappingException except ) { - _log.fatal( Messages.format( "jdo.fatalException", except ) ); + Persistence sqlEngine = (SQLEngine) classDescriptorToPersistence.get(clsDesc); + if (sqlEngine == null) { + sqlEngine = new SQLEngine((JDOClassDescriptor) clsDesc, this, null); + classDescriptorToPersistence.put(clsDesc, sqlEngine); + } + return sqlEngine; + } catch (MappingException except) { + _log.fatal(Messages.format("jdo.fatalException", except)); return null; } } Index: main/org/exolab/castor/jdo/engine/DatabaseImpl.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/DatabaseImpl.java,v retrieving revision 1.25 diff -u -r1.25 DatabaseImpl.java --- main/org/exolab/castor/jdo/engine/DatabaseImpl.java 20 Jun 2005 12:00:55 -0000 1.25 +++ main/org/exolab/castor/jdo/engine/DatabaseImpl.java 28 Jun 2005 21:38:46 -0000 @@ -54,6 +54,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.castor.persist.ProposedObject; import org.castor.persist.TransactionContext; import org.exolab.castor.jdo.*; import org.exolab.castor.mapping.AccessMode; @@ -343,7 +344,8 @@ TransactionContext tx = getTransaction(); PersistenceInfo info = _scope.getPersistenceInfo(type); - return tx.load(info.engine, info.molder, identity, object, mode); + ProposedObject proposedObject = new ProposedObject(); + return tx.load( info.engine, info.molder, identity, proposedObject, mode ); } public void create( Object object ) Index: main/org/exolab/castor/jdo/engine/SQLEngine.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/SQLEngine.java,v retrieving revision 1.31 diff -u -r1.31 SQLEngine.java --- main/org/exolab/castor/jdo/engine/SQLEngine.java 16 Jun 2005 12:14:28 -0000 1.31 +++ main/org/exolab/castor/jdo/engine/SQLEngine.java 28 Jun 2005 21:38:52 -0000 @@ -49,8 +49,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.castor.engine.CounterRef; +import org.castor.persist.ProposedObject; import org.exolab.castor.jdo.*; -import org.exolab.castor.jdo.drivers.PreparedStatementProxy; import org.exolab.castor.mapping.*; import org.exolab.castor.mapping.loader.FieldHandlerImpl; import org.exolab.castor.persist.spi.*; @@ -117,7 +119,7 @@ private ColumnInfo[] _ids; private SQLEngine _extends; - + private QueryExpression _sqlFinder; private PersistenceFactory _factory; @@ -142,6 +144,16 @@ */ private boolean hasFieldsToPersist = false; + /** + * Number of ClassDescriptor that extend this one. + */ + private int _numberOfExtendLevels; + + /** + * Collection of all the ClassDescriptor that extend this one (closure) + */ + private Collection _extendingClassDescriptors; + SQLEngine( JDOClassDescriptor clsDesc, PersistenceFactory factory, String stampField ) throws MappingException { @@ -293,6 +305,10 @@ _fields = new FieldInfo[fieldsInfo.size()]; fieldsInfo.copyInto( _fields ); + // obtain the number of ClassDescriptor that extend this one. + _numberOfExtendLevels = numberOfExtendingClassDescriptors(getDescriptor()); + _extendingClassDescriptors = getDescriptor().getExtendedBy(); + // iterate through all fields to check whether there is a field // to persist at all; in the case of extend relationships where no // additional attributes are defined in the extending class, this @@ -327,6 +343,10 @@ } } + public Persistence.ColumnInfo[] getColumnInfoForIdentities() { + return _ids; + } + public Persistence.FieldInfo[] getInfo() { return _fields; } @@ -941,7 +961,7 @@ if(_log.isDebugEnabled()) { _log.debug( Messages.format( "jdo.storing", _clsDesc.getJavaClass().getName(), - _sqlLoad ) ); + stmt.toString() ) ); } // bind the identity to the prepareStatement @@ -964,15 +984,20 @@ for(int i = 0; i < fields.length; i++){ currentField = toJava(i, 0, res.getObject(_fields[i].columns[0].name)); - if ( currentField != null && - _fields[i].tableName.compareTo(_mapTo) == 0 && - !original[i].equals(currentField) ) { - if (numberOfFieldsNotMatching >= 1) { - enlistFieldsNotMatching.append (", "); + if (_fields[i].tableName.compareTo(_mapTo) == 0) { + if ((original[i] == null && currentField != null) || + (currentField == null && original[i] != null) || + (original[i] == null && currentField == null)) { + enlistFieldsNotMatching.append ("(" + _clsDesc.getJavaClass().getName() + ")." + _fields[i].columns[0].name + ": "); + enlistFieldsNotMatching.append ("[" + original[i] + "/" + currentField + "]"); + } else if (!original[i].equals(currentField) ) { + if (numberOfFieldsNotMatching >= 1) { + enlistFieldsNotMatching.append (", "); + } + enlistFieldsNotMatching.append ("(" + _clsDesc.getJavaClass().getName() + ")." + _fields[i].columns[0].name + ": "); + enlistFieldsNotMatching.append ("[" + original[i] + "/" + currentField + "]"); + numberOfFieldsNotMatching++; } - enlistFieldsNotMatching.append ("(" + _clsDesc.getJavaClass().getName() + ")." + _fields[i].columns[0].name + ": "); - enlistFieldsNotMatching.append ("[" + original[i] + "/" + currentField + "]"); - numberOfFieldsNotMatching++; } } throw new ObjectModifiedException( Messages.format("persist.objectModified", _clsDesc.getJavaClass().getName(), identity, enlistFieldsNotMatching.toString()) ); @@ -1106,25 +1131,42 @@ } - public Object load( Object conn, Object[] fields, Object identity, AccessMode accessMode ) - throws ObjectNotFoundException, PersistenceException { - + /** + * Loads the object from persistence storage. This method will load + * the object fields from persistence storage based on the object's + * identity. This method may return a stamp which can be used at a + * later point to determine whether the copy of the object in + * persistence storage is newer than the cached copy (see {@link + * #store}). If lock is true the object must be + * locked in persistence storage to prevent concurrent updates. + * + * @param conn An open connection + * @param fields An Object[] to load field values into + * @param identity Identity of the object to load. + * @param accessMode The access mode (null equals shared) + * @return The object's stamp, or null + * @throws ObjectNotFoundException The object was not found in persistent storage + * @throws PersistenceException A persistence error occured + */ + public Object load( Object conn, ProposedObject proposedObject, Object identity, AccessMode accessMode ) + throws ObjectNotFoundException, PersistenceException { PreparedStatement stmt = null; ResultSet rs = null; Object stamp = null; boolean notNull; + + Object[] fields = proposedObject.getFields(); + try { - String sql = ( accessMode == AccessMode.DbLocked ) ? _sqlLoadLock : _sqlLoad; - stmt = ( (Connection) conn ).prepareStatement(sql); - + String sqlString = (accessMode == AccessMode.DbLocked) ? _sqlLoadLock : _sqlLoad; + stmt = ((Connection) conn).prepareStatement(sqlString); + if (_log.isDebugEnabled()) { - String generatedSQL = ( accessMode == AccessMode.DbLocked ) ? _sqlLoadLock : _sqlLoad; -// _log.debug( Messages.format( "jdo.loading", _clsDesc.getJavaClass().getName(), generatedSQL ) ); - _log.debug( Messages.format( "jdo.loading", _clsDesc.getJavaClass().getName(), stmt.toString()) ); + _log.debug( Messages.format("jdo.loading", _clsDesc.getJavaClass().getName(), stmt.toString())); } - int count = 1; + int fieldIndex = 1; // bind the identity of the preparedStatement if ( identity instanceof Complex ) { Complex id = (Complex) identity; @@ -1132,37 +1174,67 @@ throw new PersistenceException( "Size of complex field mismatched! expected: "+_ids.length+" found: "+id.size() ); for ( int i=0; i<_ids.length; i++ ) - stmt.setObject( count++, idToSQL( i, id.get(i) ) ); - + stmt.setObject(fieldIndex++, idToSQL(i, id.get(i))); } else { if ( _ids.length != 1 ) throw new PersistenceException( "Complex field expected!" ); - stmt.setObject( count++, idToSQL( 0, identity ) ); + stmt.setObject(fieldIndex++, idToSQL(0, identity)); } if (_log.isDebugEnabled()) { _log.debug( Messages.format( "jdo.loading", _clsDesc.getJavaClass().getName(), stmt.toString()) ); } - // query the object + // execute the SQL query rs = stmt.executeQuery(); if ( ! rs.next() ) throw new ObjectNotFoundException( Messages.format("persist.objectNotFound", _clsDesc.getJavaClass().getName(), identity) ); + if (_extendingClassDescriptors.size() > 0) { + Object[] returnValues = + calculateNumberOfFields(_extendingClassDescriptors, + _ids.length, _fields.length, _numberOfExtendLevels, rs); + JDOClassDescriptor potentialLeafDescriptor = (JDOClassDescriptor) returnValues[0]; + + if (potentialLeafDescriptor != null && + !potentialLeafDescriptor.getJavaClass().getName().equals (getDescriptor().getJavaClass().getName())) { + Object[] expandedFields = new Object[potentialLeafDescriptor.getFields().length]; + + fields = expandedFields; + proposedObject.setFields (expandedFields); + proposedObject.setActualClass (potentialLeafDescriptor.getJavaClass()); + proposedObject.setExpanded(true); + } + + return null; + } + // Load all the fields of the object including one-one relations - count = 1; + // index to use during ResultSet.getXXX() + int columnIndex = 1; + // index in fields[] for storing result of SQLTypes.getObject() + fieldIndex = 1; + String tableName = null; + String tableNameOld = tableName; Object[] temp = new Object[10]; // assume complex field max at 10 - for ( int i = 0 ; i < _fields.length ; ++i ) { + for (int i = 0 ; i < _fields.length ; ++i ) { + tableName = _fields[i].tableName; + if (!tableName.equals (tableNameOld) && !_fields[i].joined) { + columnIndex = columnIndex + _ids.length; + } + if ( !_fields[i].load ) continue; if ( !_fields[i].multi ) { notNull = false; if ( _fields[i].columns.length == 1 ) { - fields[i] = toJava( i, 0, SQLTypes.getObject( rs, count++, _fields[i].columns[0].sqlType ) ); + fields[i] = toJava(i, 0, SQLTypes.getObject(rs, columnIndex++, _fields[i].columns[0].sqlType)); + fieldIndex++; } else { - for ( int j=0; j<_fields[i].columns.length; j++ ) { - temp[j] = toJava( i, j, SQLTypes.getObject( rs, count++, _fields[i].columns[j].sqlType ) ); + for (int j = 0; j < _fields[i].columns.length; j++) { + temp[j] = toJava(i, j, SQLTypes.getObject(rs, columnIndex++, _fields[i].columns[j].sqlType)); + fieldIndex++; if ( temp[j] != null ) { notNull = true; } @@ -1176,11 +1248,12 @@ ArrayList res = new ArrayList(); notNull = false; for ( int j=0; j<_fields[i].columns.length; j++ ) { - temp[j] = toJava( i, j, SQLTypes.getObject( rs, count, _fields[i].columns[j].sqlType ) ); + temp[j] = toJava(i, j, SQLTypes.getObject(rs, columnIndex, _fields[i].columns[j].sqlType)); if ( temp[j] != null ) { notNull = true; } - count++; + fieldIndex++; + columnIndex++; } if ( notNull ) { if ( _fields[i].columns.length == 1 ) @@ -1190,24 +1263,36 @@ } fields[i] = res; } + + tableNameOld = tableName; } - while ( rs.next() ) { - count = 1; - for ( int i = 0; i < _fields.length ; ++i ) { - if ( !_fields[i].load ) + while (rs.next()) { + fieldIndex = 1; + columnIndex = 1; + + tableName = null; + tableNameOld = tableName; + + for (int i = 0; i < _fields.length ; ++i) { + + tableName = _fields[i].tableName; + if (!tableName.equals (tableNameOld) && !_fields[i].joined) { + columnIndex = columnIndex + _ids.length; + } + + if ( !_fields[i].load ) continue; if ( _fields[i].multi ) { ArrayList res = (ArrayList)fields[i]; notNull = false; - for ( int j=0; j<_fields[i].columns.length; j++ ) { - temp[j] = toJava( i, j, SQLTypes.getObject( rs, count, _fields[i].columns[j].sqlType ) ); - if ( temp[j] != null ) { - notNull = true; - } - count++; + for (int j = 0; j < _fields[i].columns.length; j++) { + temp[j] = toJava(i, j, SQLTypes.getObject(rs, columnIndex, _fields[i].columns[j].sqlType)); + if (temp[j] != null) { notNull = true; } + columnIndex++; } + fieldIndex++; if ( notNull ) { if ( _fields[i].columns.length == 1 ) { if ( !res.contains( temp[0] ) ) @@ -1219,29 +1304,120 @@ } } } else { - count += _fields[i].columns.length; + fieldIndex++; + columnIndex += _fields[i].columns.length; } + tableNameOld = tableName; } + + proposedObject.setFields(fields); } } catch ( SQLException except ) { _log.fatal( Messages.format( "jdo.loadFatal", _type, (( accessMode == AccessMode.DbLocked ) ? _sqlLoadLock : _sqlLoad ) ), except ); - - throw new PersistenceException( Messages.format("persist.nested", except), except ); + throw new PersistenceException(Messages.format("persist.nested", except), except); } finally { - try { - if ( rs != null ) rs.close(); - } catch ( SQLException sqle ) { - _log.warn("Problem closing JDBC Connection instance", sqle); + Utils.closeResultSet(rs); + Utils.closeStatement(stmt); + } + return stamp; + } + + private int numberOfExtendingClassDescriptors (JDOClassDescriptor classDescriptor) { + int numberOfExtendLevels = 1; + JDOClassDescriptor currentClassDescriptor = getDescriptor(); + while (currentClassDescriptor.getExtends() != null) { + currentClassDescriptor = (JDOClassDescriptor) currentClassDescriptor.getExtends(); + numberOfExtendLevels++; + } + return numberOfExtendLevels; + } + + private Object[] calculateNumberOfFields (Collection extendingClassDescriptors, + int numberOfIdentityColumns, + int numberOfFields, + int numberOfExtendLevels, + ResultSet rs) + throws SQLException + { + JDOClassDescriptor potentialLeafDescriptor = null; + int suggestedNumberOfFields = numberOfFields; + Collection potentialActualClassDescriptor = new LinkedList(); + int numberOfIdentitiesToAnalyze = 0; + addExtendingClassDescriptors(potentialActualClassDescriptor, extendingClassDescriptors); + + JDOClassDescriptor potentialClassDescriptor = null; + JDOClassDescriptor potentialClassDescriptorPrevious = null; + int initialColumnIndex = numberOfFields + numberOfIdentityColumns * numberOfExtendLevels + 1; + int columnIndex = initialColumnIndex; + int numberOfExtendingClassDescriptors = 0; + for (Iterator iter = potentialActualClassDescriptor.iterator(); iter.hasNext(); ) { + potentialClassDescriptor = (JDOClassDescriptor) iter.next(); + numberOfExtendingClassDescriptors += 1; + _log.debug ("Potential extending class descriptor: " + potentialClassDescriptor.getJavaClass().getName()); + FieldDescriptor[] identityDescriptors = potentialClassDescriptor.getIdentities(); + boolean isNull = true; + + for (int i = 0; i < identityDescriptors.length; i++) { + Object temp; + Object[] temps; + JDOFieldDescriptor jdoFieldDescriptor = (JDOFieldDescriptor) identityDescriptors[i]; + if (jdoFieldDescriptor.getSQLName().length == 1 ) { + temp = SQLTypes.getObject( rs, columnIndex++, java.sql.Types.JAVA_OBJECT); + isNull = (temp == null); + } else { + temps = new Object[jdoFieldDescriptor.getSQLName().length]; + for ( int j=0; j 0) { + potentialLeafDescriptor = potentialClassDescriptor; + suggestedNumberOfFields += potentialClassDescriptor.getFields().length; + } else if (!iter.hasNext() && isNull && numberOfIdentitiesToAnalyze > 0){ + potentialLeafDescriptor = potentialClassDescriptorPrevious; + // suggestedNumberOfFields += potentialClassDescriptor.getFields().length; + } else { + FieldDescriptor[] potentialFields = + (FieldDescriptor[]) potentialClassDescriptor.getFields(); + for (int i = 0; i < potentialFields.length; i++) { + JDOFieldDescriptor jdoFieldDescriptor = (JDOFieldDescriptor) potentialFields[i]; + String[] columnNames = jdoFieldDescriptor.getSQLName(); + columnIndex = columnIndex + columnNames.length; + } + + // the JDOClassDescriptor we just looked at is definitely part of the extends hierarchy, + // and as such we need to increase the number of potential fields + if (!isNull) { + suggestedNumberOfFields += potentialClassDescriptor.getFields().length; + } } } - return stamp; + + _log.debug ("In total " + numberOfIdentitiesToAnalyze + " (extending) identities analyzed."); + + if ((potentialLeafDescriptor != null) && _log.isDebugEnabled()) { + _log.debug ("Most likely of type " + potentialLeafDescriptor.getJavaClass().getName()); + _log.debug ("After analysis, " + suggestedNumberOfFields + " fields need to be loaded."); + } + + return new Object[] {potentialLeafDescriptor, new Integer (suggestedNumberOfFields) }; + } - private void buildSqlCreate () throws QueryException { StringBuffer sql; @@ -1399,7 +1575,7 @@ } - private void buildFinder( JDOClassDescriptor clsDesc ) throws QueryException { + private void buildFinder(JDOClassDescriptor clsDesc) throws QueryException { QueryExpression expr; QueryExpression find; @@ -1427,14 +1603,27 @@ baseDesc.getTableName(), baseDesc.getIdentityColumnNames()); curDesc = baseDesc; } - for ( int i=0; i<_ids.length; i++ ) { - find.addColumn( _mapTo, idnames[i] ); - } - + // join all the related/depended table Vector joinTables = new Vector(); - for ( int i=0; i<_fields.length; i++ ) { - String alias = _fields[i].tableName; + String aliasOld = null; + String alias = null; + + for (int i = 0; i < _fields.length; i++) { + if (i > 0) { aliasOld = alias; } + alias = _fields[i].tableName; + + // add id columns to select statement + if (!alias.equals(aliasOld) && !_fields[i].joined) { + JDOClassDescriptor classDescriptor = (JDOClassDescriptor) + _fields[i].fieldDescriptor.getContainingClassDescriptor(); + String[] ids = classDescriptor.getIdentityColumnNames(); + for (int j = 0; j < ids.length; j++) { + expr.addColumn(alias, ids[j]); + find.addColumn(alias, ids[j]); + } + } + if ( _fields[i].load ) { if ( _fields[i].joined /*&& !joinTables.contains( _fields[i].tableName )*/ ) { int offset = 0; @@ -1458,20 +1647,93 @@ expr.addColumn( alias, _fields[i].columns[j].name ); find.addColumn( alias, _fields[i].columns[j].name ); } + expr.addTable(_fields[i].tableName, alias); find.addTable(_fields[i].tableName, alias); } } + + // 'join' all the extending tables + curDesc = clsDesc; + List classDescriptorsToAdd = new LinkedList(); + JDOClassDescriptor classDescriptor = null; + addExtendingClassDescriptors(classDescriptorsToAdd, curDesc.getExtendedBy()); + + if (classDescriptorsToAdd.size() > 0) { + for (Iterator iter = classDescriptorsToAdd.iterator(); iter.hasNext(); ) { + classDescriptor = (JDOClassDescriptor) iter.next(); + + if (_log.isDebugEnabled()) { + _log.debug("Adding outer left join for " + classDescriptor.getJavaClass().getName() + + " on table " + classDescriptor.getTableName()); + } + + expr.addOuterJoin( _mapTo, + curDesc.getIdentityColumnNames(), + classDescriptor.getTableName(), + classDescriptor.getIdentityColumnNames()); + find.addOuterJoin( _mapTo, + curDesc.getIdentityColumnNames(), + classDescriptor.getTableName(), + classDescriptor.getIdentityColumnNames()); + + Persistence persistenceEngine; + try { + persistenceEngine = _factory.getPersistence (classDescriptor); + } catch (MappingException e) { + throw new QueryException("Problem obtaining persistence engine for ClassDescriptor " + classDescriptor.getJavaClass().getName(), e); + } + + SQLEngine.ColumnInfo[] idInfos = + (SQLEngine.ColumnInfo[]) persistenceEngine.getColumnInfoForIdentities(); + for (int i = 0; i < idInfos.length; i++) { + expr.addColumn (classDescriptor.getTableName(), idInfos[i].name); + find.addColumn (classDescriptor.getTableName(), idInfos[i].name); + } + + SQLEngine.FieldInfo[] fieldInfos = (SQLEngine.FieldInfo[]) persistenceEngine.getInfo(); + for (int i = 0; i < fieldInfos.length; i++) { + boolean hasFieldToAdd = false; + SQLEngine.ColumnInfo[] columnInfos = fieldInfos[i].columns; + if (classDescriptor.getTableName().equals(fieldInfos[i].tableName)) { + for ( int j = 0; j < columnInfos.length; j++ ) { + expr.addColumn (classDescriptor.getTableName(), fieldInfos[i].columns[j].name); + find.addColumn (classDescriptor.getTableName(), fieldInfos[i].columns[j].name); + } + hasFieldToAdd = true; + } + + if (hasFieldToAdd) { + expr.addTable(classDescriptor.getTableName()); + find.addTable(classDescriptor.getTableName()); + } + } + } + } + + _sqlLoad = expr.getStatement( false ); _sqlLoadLock = expr.getStatement( true ); _sqlFinder = find; if(_log.isDebugEnabled()) { - _log.debug( Messages.format( "jdo.loading", _type, _sqlLoad ) ); + _log.debug(Messages.format("jdo.loading", _type, _sqlLoad)); + _log.debug(Messages.format("jdo.loading.with.lock", _type, _sqlLoadLock)); + _log.debug(Messages.format("jdo.finding", _type, _sqlFinder)); } } + + private void addExtendingClassDescriptors (Collection classDescriptorsToAdd, Collection extendingClassDescriptors) { + JDOClassDescriptor classDescriptor = null; + for (Iterator iter = extendingClassDescriptors.iterator(); iter.hasNext(); ) { + classDescriptor = (JDOClassDescriptor) iter.next(); + classDescriptorsToAdd.add (classDescriptor); + addExtendingClassDescriptors(classDescriptorsToAdd, classDescriptor.getExtendedBy()); + } + + } public String toString() { @@ -1497,10 +1759,18 @@ final String[] joinFields; ColumnInfo[] columns; + + final FieldDescriptor fieldDescriptor; + + final ClassDescriptor classDescriptor; FieldInfo( JDOClassDescriptor clsDesc, FieldDescriptor fieldDesc, String classTable, boolean ext ) throws MappingException{ + fieldDescriptor = fieldDesc; + + classDescriptor = clsDesc; + // for readability final int FIELD_TYPE = 0; @@ -1645,16 +1915,31 @@ } } - static final class ColumnInfo { + static final class ColumnInfo implements Persistence.ColumnInfo{ + /** + * Name of the column + */ final String name; + /** + * SQL type of teh coplumn + */ final int sqlType; + /** + * TypeConvertor to use when converting to the SQLType of this column. + */ final TypeConvertor convertTo; + /** + * TypeConvertor to use when converting from the SQLType of this column. + */ final TypeConvertor convertFrom; + /** + * Type conversion parameters + */ final String convertParam; ColumnInfo( String name, int type, TypeConvertor convertTo, @@ -1665,6 +1950,41 @@ this.convertFrom = convertFrom; this.convertParam = convertParam; } + + /* (non-Javadoc) + * @see org.exolab.castor.persist.spi.Persistence.ColumnInfo#getName() + */ + public String getName() { + return name; + } + + /* (non-Javadoc) + * @see org.exolab.castor.persist.spi.Persistence.ColumnInfo#getSqlType() + */ + public int getSqlType() { + return sqlType; + } + + /* (non-Javadoc) + * @see org.exolab.castor.persist.spi.Persistence.ColumnInfo#getConvertTo() + */ + public TypeConvertor getConvertTo() { + return convertTo; + } + + /* (non-Javadoc) + * @see org.exolab.castor.persist.spi.Persistence.ColumnInfo#getConvertFrom() + */ + public TypeConvertor getConvertFrom() { + return convertFrom; + } + + /* (non-Javadoc) + * @see org.exolab.castor.persist.spi.Persistence.ColumnInfo#getConvertParam() + */ + public String getConvertParam() { + return convertParam; + } } static final class SQLQuery implements PersistenceQuery { @@ -1673,7 +1993,8 @@ private ResultSet _rs; - private final SQLEngine _engine; + private /*final*/ SQLEngine _engine; + private SQLEngine _requestedEngine; private final Class[] _types; @@ -1692,6 +2013,7 @@ SQLQuery( SQLEngine engine, String sql, Class[] types ) { _engine = engine; + _requestedEngine = engine; _types = types; _values = new Object[ _types.length ]; _sql = sql; @@ -1749,7 +2071,7 @@ } catch (SQLException e) { - throw new PersistenceException(e.getMessage()); + throw new PersistenceException(e.getMessage(), e); } return retval; } @@ -1956,19 +2278,36 @@ } - private Object loadSingleField( int i, int count ) throws SQLException, PersistenceException - { + private Object loadSingleField(int i, CounterRef counterReference) + throws SQLException, PersistenceException { + String currentTableName = counterReference.getTableName(); + int count = counterReference.getCounter(); Object[] temp = new Object[_engine._fields[i].columns.length]; boolean notNull = false; Object field; + + String fieldTableName = _engine._fields[i].tableName; + + ResultSetMetaData metaData = _rs.getMetaData(); + String columnTableName = metaData.getTableName(count); + + while (!columnTableName.equalsIgnoreCase(fieldTableName)) { + count++; + columnTableName = metaData.getTableName(count); + } + String firstColumnOfField = _engine._fields[i].columns[0].name; + while (!metaData.getColumnName(count).equalsIgnoreCase(firstColumnOfField)) { + count++; + } + if ( _engine._fields[i].columns.length == 1 ) { - field = _engine.toJava( i, 0, SQLTypes.getObject( _rs, count++, - _engine._fields[i].columns[0].sqlType ) ); + field = _engine.toJava(i, 0, SQLTypes.getObject(_rs, count, _engine._fields[i].columns[0].sqlType)); + count++; } else { for ( int j=0; j<_engine._fields[i].columns.length; j++ ) { - temp[j] = _engine.toJava( i, j, SQLTypes.getObject( _rs, count++, - _engine._fields[i].columns[j].sqlType ) ); + temp[j] = _engine.toJava(i, j, SQLTypes.getObject(_rs, count, _engine._fields[i].columns[j].sqlType)); + count++; if ( temp[j] != null ) { notNull = true; } @@ -1978,24 +2317,41 @@ else field = null; } + counterReference.setCounter(count); + counterReference.setTableName(currentTableName); return field; } - private Object loadMultiField( int i, int count, Object field ) throws SQLException, PersistenceException - { + private Object loadMultiField(int i, CounterRef counterReference, Object field) + throws SQLException, PersistenceException { + int count = counterReference.getCounter(); Object[] temp = new Object[_engine._fields[i].columns.length]; boolean notNull = false; ArrayList res; + String fieldTableName = _engine._fields[i].tableName; + + ResultSetMetaData metaData = _rs.getMetaData(); + String columnTableName = metaData.getTableName(count); + + while (!columnTableName.equalsIgnoreCase(fieldTableName)) { + count++; + columnTableName = metaData.getTableName(count); + } + + String firstColumnOfField = _engine._fields[i].columns[0].name; + while (!metaData.getColumnName(count).equalsIgnoreCase(firstColumnOfField)) { + count++; + } + if( field == null ) res = new ArrayList(); else res = (ArrayList) field; for ( int j=0; j<_engine._fields[i].columns.length; j++ ) { - temp[j] = _engine.toJava( i, j, - SQLTypes.getObject( _rs, count, _engine._fields[i].columns[j].sqlType ) ); + temp[j] = _engine.toJava(i, j, SQLTypes.getObject(_rs, count, _engine._fields[i].columns[j].sqlType)); if ( temp[j] != null ) { notNull = true; } @@ -2011,27 +2367,41 @@ res.add( com ); } } + counterReference.setCounter(count); + return res; } - - private int loadRow( Object[] fields, boolean isFirst ) throws SQLException, PersistenceException - { + private int loadRow(Object[] fields, int numberOfFields, boolean isFirst) + throws SQLException, PersistenceException { int count = _engine._ids.length + 1; - // Load all the fields. - for ( int i = 0 ; i < _engine._fields.length ; ++i ) { - if ( !_engine._fields[i].load ) - continue; + String tableName = null; - if ( _engine._fields[i].multi ) { - fields[i] = loadMultiField( i, count, fields[i] ); - } else if( isFirst ) { - // Non-multi fields have to be done one only once, so this is skipped - // if we have already read the first row. - fields[i] = loadSingleField( i, count ); + // TODO: wrong, as it could be that the first field is not part of the root class. + if (numberOfFields > 0) { + tableName = _engine._fields[0].tableName; + + // Load all the fields. + CounterRef counterReference = new CounterRef (); + counterReference.setCounter(count); + counterReference.setTableName(tableName); + + for ( int i = 0 ; i < numberOfFields ; ++i ) { + if (!_engine._fields[i].load) { continue; } + + if ( _engine._fields[i].multi ) { + counterReference.setCounter(count); + fields[i] = loadMultiField( i, counterReference, fields[i] ); + count = counterReference.getCounter(); + } else if( isFirst ) { + // Non-multi fields have to be done one only once, so this is skipped + // if we have already read the first row. + counterReference.setCounter (count); + fields[i] = loadSingleField( i, counterReference); + count = counterReference.getCounter(); + } } - count += _engine._fields[i].columns.length; } return count; } @@ -2064,12 +2434,18 @@ } - // Fill the given fields[] with the "cached" stuff from our _fields[] . - public Object fetch( Object[] fields, Object identity ) throws ObjectNotFoundException, PersistenceException - { + /** + * @see org.exolab.castor.persist.spi.PersistenceQuery#fetch(org.exolab.castor.persist.ProposedObject, java.lang.Object) + */ + public Object fetch(ProposedObject proposedObject, Object identity) + throws ObjectNotFoundException, PersistenceException { + Object[] fields = proposedObject.getFields(); + + // Fill the given fields[] with the "cached" stuff from our _fields[] . for( int i = 0; i < _fields.length; i++ ) { fields[i] = _fields[i]; } + return null; } @@ -2079,7 +2455,40 @@ // maybe we can optimize a little bit here when we have time. // Instead of creating new Object[] and ArrayList for each // "multi field" each fetchRaw is called, we might reuse them. - _fields = new Object[_engine._fields.length]; + + SQLEngine oldEngine = null; + int originalFieldNumber = _requestedEngine._fields.length; + if (_requestedEngine.getDescriptor().isExtended()) { + Collection extendingClassDescriptors = _requestedEngine.getDescriptor().getExtendedBy(); + int numberOfExtendLevels = _requestedEngine.numberOfExtendingClassDescriptors(_requestedEngine.getDescriptor()); + JDOClassDescriptor leafDescriptor = null; + Object[] returnValues = null; + try { + returnValues =_requestedEngine.calculateNumberOfFields(extendingClassDescriptors, _requestedEngine._ids.length, _requestedEngine._fields.length, numberOfExtendLevels, this._rs); + } catch (SQLException e) { + _log.error ("Problem calculating number of concrete fields.", e); + throw new PersistenceException ("Problem calculating number of concrete fields.", e); + } + + leafDescriptor = (JDOClassDescriptor) returnValues[0]; + + if (leafDescriptor != null) { + if (!leafDescriptor.getJavaClass().getName().equals(_requestedEngine.getDescriptor().getJavaClass().getName())) { + originalFieldNumber = ((Integer) returnValues[1]).intValue(); + + Persistence newEngine =null; + try { + newEngine = _requestedEngine._factory.getPersistence(leafDescriptor); + } catch (MappingException e) { + _log.error ("Problem obtaining persistence engine for " + leafDescriptor.getJavaClass().getName(), e); + throw new PersistenceException ("Problem obtaining persistence engine for " + leafDescriptor.getJavaClass().getName(), e); + } + _engine = (SQLEngine) newEngine; + } + } + } + + _fields = new Object[originalFieldNumber]; // It would prove a little difficult to fetch if we don't have any rows with data left :-) if ( _resultSetDone ) @@ -2102,7 +2511,7 @@ // As we assume that we have called fetch() immediatly after nextIdentity(), // we can be sure that it belongs to the object we want. This is probably not the // safest programming style, but has to suffice currently :-) - loadRow( _fields, true ); + loadRow(_fields, originalFieldNumber, true); // We move forward in the ResultSet, until we see another identity or run out of rows. while ( _rs.next() ) { @@ -2114,7 +2523,7 @@ if( identitiesEqual( wantedIdentity, currentIdentity ) ) { // Load next row of object data from _rs into <_fields> array. - loadRow( _fields, false ); + loadRow(_fields, originalFieldNumber, false); } else { // We are done with all the rows for our obj. and still have rows left. @@ -2129,7 +2538,6 @@ // We are done with all the rows for our obj. and don't have any rows left. _resultSetDone = true; _lastIdentity = null; - } catch ( SQLException except ) { throw new PersistenceException( Messages.format("persist.nested", except), except ); } Index: main/org/exolab/castor/jdo/engine/SimpleQueryExecutor.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/SimpleQueryExecutor.java,v retrieving revision 1.6 diff -u -r1.6 SimpleQueryExecutor.java --- main/org/exolab/castor/jdo/engine/SimpleQueryExecutor.java 3 May 2005 15:26:47 -0000 1.6 +++ main/org/exolab/castor/jdo/engine/SimpleQueryExecutor.java 28 Jun 2005 21:38:53 -0000 @@ -49,7 +49,6 @@ import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.jdo.QueryException; import org.exolab.castor.jdo.QueryResults; -import org.exolab.castor.jdo.drivers.PreparedStatementProxy; import org.exolab.castor.persist.spi.QueryExpression; import org.exolab.castor.util.SqlBindParser; Index: main/org/exolab/castor/mapping/loader/ClassDescriptorImpl.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/mapping/loader/ClassDescriptorImpl.java,v retrieving revision 1.2 diff -u -r1.2 ClassDescriptorImpl.java --- main/org/exolab/castor/mapping/loader/ClassDescriptorImpl.java 5 Mar 2005 13:41:52 -0000 1.2 +++ main/org/exolab/castor/mapping/loader/ClassDescriptorImpl.java 28 Jun 2005 21:38:54 -0000 @@ -47,6 +47,12 @@ package org.exolab.castor.mapping.loader; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.exolab.castor.mapping.ValidityException; import org.exolab.castor.mapping.ClassDescriptor; import org.exolab.castor.mapping.FieldDescriptor; @@ -66,9 +72,9 @@ public class ClassDescriptorImpl implements ClassDescriptor { + private static final Log _log = LogFactory.getLog(ClassDescriptorImpl.class); - - private ClassMapping _map; + private ClassMapping _map; /** * The Java class for this descriptor. */ @@ -85,11 +91,15 @@ * or null if this is a top-level class. */ private final ClassDescriptor _extends; - + + /** + * A collection of class descriptors that extend this class, or + * an empty collection if this is a leaf class. + */ + private final Collection _extendedBy = new LinkedList(); private final ClassDescriptor _depends; - /** * The field of the identity for this class. */ @@ -166,6 +176,12 @@ throw new MappingException( "mapping.classDoesNotExtend", _javaClass.getName(), extend.getJavaClass().getName() ); _extends = extend; + + if ( _extends.getClass().getName().equals("org.exolab.castor.jdo.engine.JDOClassDescriptor") && + this.getClass().getName().equals("org.exolab.castor.jdo.engine.JDOClassDescriptor")) { + ((ClassDescriptorImpl) _extends).addExtendedBy(this); + } + if ( _extends instanceof ClassDescriptorImpl ) _identities = ( identities == null ? ((ClassDescriptorImpl)_extends).getIdentities() : identities ); else @@ -232,6 +248,27 @@ public ClassDescriptor getExtends() { return _extends; + } + + public boolean isExtending() { + return (_extends != null); + } + + /** + * Returns a collection of class descriptors that extend this class descriptor. + * + * @return A collection of class descriptors. + */ + public Collection getExtendedBy() { + return _extendedBy; + } + + public boolean isExtended() { + return (_extendedBy.size() > 0); + } + + public void addExtendedBy(ClassDescriptor classDesc) { + _extendedBy.add(classDesc); } public ClassDescriptor getDepends() { Index: main/org/exolab/castor/persist/ClassMolder.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/persist/ClassMolder.java,v retrieving revision 1.24 diff -u -r1.24 ClassMolder.java --- main/org/exolab/castor/persist/ClassMolder.java 20 Jun 2005 12:00:50 -0000 1.24 +++ main/org/exolab/castor/persist/ClassMolder.java 28 Jun 2005 21:39:03 -0000 @@ -76,6 +76,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.castor.persist.ProposedObject; import org.castor.persist.TransactionContext; import org.exolab.castor.jdo.DuplicateIdentityException; import org.exolab.castor.jdo.ObjectDeletedException; @@ -487,17 +489,23 @@ extendFields = getFullFields( extend ); thisFields = clsMap.getFieldMapping(); - fieldList = new ArrayList(extendFields.length + thisFields.length); + fieldList = new ArrayList(extendFields.length + thisFields.length - identities.length); for (int i = 0; i < extendFields.length; i++) { fieldList.add(extendFields[i]); } - for ( int i=0; i --> - + + + + + @@ -133,7 +137,11 @@ - + + + + + @@ -202,7 +210,11 @@ - + + + + + @@ -269,7 +281,11 @@ - + + + + + @@ -337,7 +353,11 @@ - + + + + + @@ -404,7 +424,11 @@ - + + + + + @@ -471,7 +495,11 @@ - + + + + +