Index: 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 --- org/exolab/castor/jdo/drivers/MultiRSCallQuery.java 25 Mar 2004 12:31:06 -0000 1.4 +++ org/exolab/castor/jdo/drivers/MultiRSCallQuery.java 15 Mar 2005 13:35:51 -0000 @@ -59,6 +59,7 @@ import org.exolab.castor.jdo.QueryException; import org.exolab.castor.jdo.engine.SQLTypes; import org.exolab.castor.mapping.AccessMode; +import org.exolab.castor.persist.ProposedObject; import org.exolab.castor.persist.spi.Complex; import org.exolab.castor.persist.spi.PersistenceQuery; import org.exolab.castor.util.Messages; @@ -235,10 +236,12 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException + public Object fetch(ProposedObject proposedObject, Object identity) throws ObjectNotFoundException, PersistenceException { Object stamp = null; + Object[] fields = proposedObject.getFields(); + try { // Load all the fields of the object including one-one relations // index 0 belongs to the identity Index: 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 --- org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java 25 Mar 2004 12:32:01 -0000 1.4 +++ org/exolab/castor/jdo/drivers/PostgreSQLCallQuery.java 15 Mar 2005 13:35:51 -0000 @@ -60,6 +60,7 @@ import org.exolab.castor.jdo.engine.JDBCSyntax; import org.exolab.castor.jdo.engine.SQLTypes; import org.exolab.castor.mapping.AccessMode; +import org.exolab.castor.persist.ProposedObject; import org.exolab.castor.persist.spi.Complex; import org.exolab.castor.persist.spi.PersistenceQuery; import org.exolab.castor.util.Messages; @@ -248,10 +249,10 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException + public Object fetch(ProposedObject proposedObject, Object identity) throws ObjectNotFoundException, PersistenceException { Object stamp = null; - + Object[] fields = proposedObject.getFields(); try { // Load all the fields of the object including one-one relations Index: 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 --- org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java 25 Mar 2004 12:24:22 -0000 1.3 +++ org/exolab/castor/jdo/drivers/ReturnedRSCallQuery.java 15 Mar 2005 13:35:51 -0000 @@ -59,6 +59,7 @@ import org.exolab.castor.jdo.QueryException; import org.exolab.castor.jdo.engine.SQLTypes; import org.exolab.castor.mapping.AccessMode; +import org.exolab.castor.persist.ProposedObject; import org.exolab.castor.persist.spi.Complex; import org.exolab.castor.persist.spi.PersistenceQuery; import org.exolab.castor.util.Messages; @@ -228,10 +229,10 @@ } - public Object fetch(Object[] fields,Object identity) throws ObjectNotFoundException, PersistenceException + public Object fetch(ProposedObject proposedObject, Object identity) throws ObjectNotFoundException, PersistenceException { Object stamp = null; - + Object[] fields = proposedObject.getFields(); try { // Load all the fields of the object including one-one relations Index: 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.17 diff -u -r1.17 DatabaseImpl.java --- org/exolab/castor/jdo/engine/DatabaseImpl.java 10 Jan 2005 21:22:46 -0000 1.17 +++ org/exolab/castor/jdo/engine/DatabaseImpl.java 15 Mar 2005 13:35:52 -0000 @@ -313,7 +313,15 @@ if ( info == null ) throw new ClassNotPersistenceCapableException( Messages.format( "persist.classNotPersistenceCapable", type.getName() ) ); - return tx.load( info.engine, info.molder, identity, object, null ); + ProposedObject proposedObject = new ProposedObject(); + proposedObject.setObject(object); + if (object != null) { + proposedObject.setProposedClass(object.getClass()); + } + + Object loaded = tx.load( info.engine, info.molder, identity, proposedObject, null ); + object = proposedObject.getObject(); + return loaded; } public Object load( Class type, Complex identity ) throws TransactionNotInProgressException, ObjectNotFoundException, @@ -363,7 +371,11 @@ if ( info == null ) throw new ClassNotPersistenceCapableException( Messages.format("persist.classNotPersistenceCapable", type.getName()) ); - return tx.load( info.engine, info.molder, identity, null, mode ); + ProposedObject proposedObject = new ProposedObject(); + proposedObject.setObject(null); + proposedObject.setProposedClass(null); + + return tx.load( info.engine, info.molder, identity, proposedObject, mode ); } public void create( Object object ) Index: org/exolab/castor/jdo/engine/OQLQueryImpl.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/OQLQueryImpl.java,v retrieving revision 1.13 diff -u -r1.13 OQLQueryImpl.java --- org/exolab/castor/jdo/engine/OQLQueryImpl.java 13 Dec 2004 10:29:19 -0000 1.13 +++ org/exolab/castor/jdo/engine/OQLQueryImpl.java 15 Mar 2005 13:35:52 -0000 @@ -47,6 +47,8 @@ package org.exolab.castor.jdo.engine; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.exolab.castor.jdo.*; import org.exolab.castor.jdo.oql.*; import org.exolab.castor.mapping.AccessMode; @@ -75,6 +77,7 @@ implements Query, OQLQuery { + private final static Log log = LogFactory.getLog (OQLQueryImpl.class); private LockEngine _dbEngine; @@ -249,7 +252,9 @@ _objClass = walker.getObjClass(); _clsDesc = walker.getClassDescriptor(); + log.debug ("class descriptor = " + _clsDesc); _expr = walker.getQueryExpression(); + log.debug ("sql expression = " + _expr); _paramInfo = walker.getParamInfo(); _projectionType = walker.getProjectionType(); _pathInfo = walker.getPathInfo(); Index: 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.26 diff -u -r1.26 SQLEngine.java --- org/exolab/castor/jdo/engine/SQLEngine.java 29 Oct 2004 14:40:05 -0000 1.26 +++ org/exolab/castor/jdo/engine/SQLEngine.java 15 Mar 2005 13:35:54 -0000 @@ -52,6 +52,7 @@ import org.exolab.castor.jdo.*; import org.exolab.castor.mapping.*; import org.exolab.castor.mapping.loader.FieldHandlerImpl; +import org.exolab.castor.persist.ProposedObject; import org.exolab.castor.persist.spi.*; import org.exolab.castor.util.Messages; import org.exolab.castor.util.SqlBindParser; @@ -116,7 +117,7 @@ private ColumnInfo[] _ids; private SQLEngine _extends; - + private QueryExpression _sqlFinder; private PersistenceFactory _factory; @@ -319,6 +320,10 @@ } } + public Persistence.ColumnInfo[] getColumnInfoForIdentities() { + return _ids; + } + public Persistence.FieldInfo[] getInfo() { return _fields; } @@ -1058,23 +1063,46 @@ } - 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(); + int originalNumberOfFields = fields.length; + + _log.debug ("number of fields to load from persistence store: " + fields.length); try { - stmt = ( (Connection) conn ).prepareStatement( ( accessMode == AccessMode.DbLocked ) ? _sqlLoadLock : _sqlLoad ); + 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(), sqlString)); } - int count = 1; + int fieldIndex = 1; // bind the identity of the preparedStatement if ( identity instanceof Complex ) { Complex id = (Complex) identity; @@ -1082,33 +1110,72 @@ 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 ) ); } - // query the object + // execute the SQL query rs = stmt.executeQuery(); if ( ! rs.next() ) throw new ObjectNotFoundException( Messages.format("persist.objectNotFound", _clsDesc.getJavaClass().getName(), identity) ); + int numberOfFields = _fields.length; + int numberOfIdentityColumns = _ids.length; + + int numberOfExtendLevels = numberOfExtendingClassDescriptors (getDescriptor()); + + Collection extendingClassDescriptors = this.getDescriptor().getExtendedBy(); + + if (extendingClassDescriptors.size() > 0) { + Object[] returnValues = + calculateNumberOfFields(extendingClassDescriptors, + _ids.length, _fields.length, numberOfExtendLevels, rs); + JDOClassDescriptor potentialLeafDescriptor = (JDOClassDescriptor) returnValues[0]; + + + if (!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; + int columnIndex = 1; // index to use during ResultSet.getXXX() + fieldIndex = 1; // index in fields[] for storing result of SQLTypes.getObject() + 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 ) { + + tableName = _fields[i].tableName; + if (!tableName.equals (tableNameOld) && !_fields[i].joined) { + for (int j = 0; j < _ids.length; j++) { + columnIndex = columnIndex + 1; + } + } + 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 += 1; } 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 ) ); + temp[j] = toJava( i, j, SQLTypes.getObject( rs, columnIndex++, _fields[i].columns[j].sqlType ) ); + fieldIndex += 1; if ( temp[j] != null ) { notNull = true; } @@ -1122,11 +1189,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 += 1; } if ( notNull ) { if ( _fields[i].columns.length == 1 ) @@ -1136,23 +1204,39 @@ } fields[i] = res; } + + tableNameOld = tableName; } while ( rs.next() ) { - count = 1; + fieldIndex = 1; + columnIndex = 1; + + tableName = null; + tableNameOld = tableName; + for ( int i = 0; i < _fields.length ; ++i ) { - if ( !_fields[i].load ) + + tableName = _fields[i].tableName; + if (!tableName.equals (tableNameOld) && !_fields[i].joined) { + for (int j = 0; j < _ids.length; j++) { + columnIndex = columnIndex + 1; + } + } + + 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 ) ); + temp[j] = toJava( i, j, SQLTypes.getObject( rs, columnIndex, _fields[i].columns[j].sqlType ) ); if ( temp[j] != null ) { notNull = true; } - count++; + fieldIndex++; + columnIndex += 1; } if ( notNull ) { if ( _fields[i].columns.length == 1 ) { @@ -1165,9 +1249,12 @@ } } } else { - count += _fields[i].columns.length; + fieldIndex += _fields[i].columns.length; + columnIndex += _fields[i].columns.length; } } + + proposedObject.setFields (fields); } } catch ( SQLException except ) { _log.fatal( Messages.format( "jdo.loadFatal", _type, (( accessMode == AccessMode.DbLocked ) ? _sqlLoadLock : _sqlLoad ) ), except ); @@ -1187,7 +1274,105 @@ } return stamp; } - + + private int numberOfExtendingClassDescriptors (JDOClassDescriptor classDescriptor) { + int numberOfExtendLevels = 1; + JDOClassDescriptor currentClassDescriptor = getDescriptor(); + while (currentClassDescriptor.getExtends() != null) { + currentClassDescriptor = (JDOClassDescriptor) currentClassDescriptor.getExtends(); + numberOfExtendLevels += 1; + } + 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(); + for (int j = 0; j < columnNames.length; j++) { + columnIndex = columnIndex + 1; + } + } + + // 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; + } + } + } + + _log.debug ("In total " + numberOfIdentitiesToAnalyze + " (extending) identities analyzed."); + + if (potentialLeafDescriptor != null) { + _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; @@ -1345,7 +1530,8 @@ } - private void buildFinder( JDOClassDescriptor clsDesc ) throws QueryException { + // private void buildFinder(JDOClassDescriptor clsDesc, ColumnInfo[] ids, FieldInfo[] fields, String mapTo) throws QueryException { + private void buildFinder(JDOClassDescriptor clsDesc) throws QueryException { QueryExpression expr; QueryExpression find; @@ -1372,15 +1558,38 @@ find.addInnerJoin(curDesc.getTableName(), curDesc.getIdentityColumnNames(), baseDesc.getTableName(), baseDesc.getIdentityColumnNames()); curDesc = baseDesc; + + // add identity column names to SELECT statement +// String[] identityColumnNames = baseDesc.getIdentityColumnNames(); +// for (int j = 0; j < identityColumnNames.length; j++) { +// expr.addColumn (baseDesc.getTableName(), identityColumnNames[j]); +// } } + for ( int i=0; i<_ids.length; i++ ) { - find.addColumn( _mapTo, idnames[i] ); + // find.addColumn(_mapTo, idnames[i]); + // expr.addColumn(_mapTo, idnames[i]); } // join all the related/depended table Vector joinTables = new Vector(); + String aliasOld = null; + String alias = null; + for ( int i=0; i<_fields.length; i++ ) { - String alias = _fields[i].tableName; + if (i > 0) { + aliasOld = alias; + } + alias = _fields[i].tableName; + + // add id columns to select statement + if (!alias.equals (aliasOld) && !_fields[i].joined) { + for (int j = 0; j < _ids.length; j++) { + expr.addColumn (alias, _ids[j].getName()); + find.addColumn (alias, _ids[j].getName()); + } + } + if ( _fields[i].load ) { if ( _fields[i].joined /*&& !joinTables.contains( _fields[i].tableName )*/ ) { int offset = 0; @@ -1404,10 +1613,71 @@ 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(); + _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++) { + _log.debug ("Adding column " + idInfos[i].name + " for table " + classDescriptor.getTableName()); + 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++ ) { + _log.debug ("Adding column " + fieldInfos[i].columns[j].name + " for table " + classDescriptor.getTableName()); + 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 ); @@ -1415,9 +1685,21 @@ if(_log.isDebugEnabled()){ _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() { @@ -1443,10 +1725,17 @@ 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; @@ -1591,16 +1880,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, @@ -1611,6 +1915,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 { @@ -1619,7 +1958,8 @@ private ResultSet _rs; - private final SQLEngine _engine; + private /*final*/ SQLEngine _engine; + private SQLEngine _requestedEngine; private final Class[] _types; @@ -1638,6 +1978,7 @@ SQLQuery( SQLEngine engine, String sql, Class[] types ) { _engine = engine; + _requestedEngine = engine; _types = types; _values = new Object[ _types.length ]; _sql = sql; @@ -1677,8 +2018,11 @@ /** * use the jdbc 2.0 method to move to an absolute position in the * resultset. + * @param row Row number to move to. + * @return True if the pointer has been moved to the indicated row number. + * @exception PersistenceException If an underlying SQLException is is reported. */ - public boolean absolute(int row) + public boolean absolute(int row) throws PersistenceException { boolean retval = false; @@ -1691,7 +2035,7 @@ } catch (SQLException e) { - throw new PersistenceException(e.getMessage()); + throw new PersistenceException(e.getMessage(), e); } return retval; } @@ -1896,19 +2240,43 @@ } - 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); + _log.debug("Current column: " + columnTableName + "." + metaData.getColumnName(count) + "/" + metaData.getColumnLabel(count)); + + while (!columnTableName.equals(fieldTableName)) { + _log.debug("Skipping " + metaData.getTableName(count) + "." + metaData.getColumnName(count) + " due to wrong table name."); + count++; + columnTableName = metaData.getTableName(count); + } + String firstColumnOfField = _engine._fields[i].columns[0].name; + while (!metaData.getColumnName(count).equals(firstColumnOfField)) { + _log.debug("Skipping " + metaData.getTableName(count) + "." + metaData.getColumnName(count)); + count++; + } + if ( _engine._fields[i].columns.length == 1 ) { - field = _engine.toJava( i, 0, SQLTypes.getObject( _rs, count++, + field = _engine.toJava( i, 0, SQLTypes.getObject( _rs, count, _engine._fields[i].columns[0].sqlType ) ); + _log.debug ("Got value for " + metaData.getTableName(count) + "." + metaData.getColumnName(count) + ": " + field); + count++; } else { for ( int j=0; j<_engine._fields[i].columns.length; j++ ) { - temp[j] = _engine.toJava( i, j, SQLTypes.getObject( _rs, count++, + temp[j] = _engine.toJava( i, j, SQLTypes.getObject( _rs, count, _engine._fields[i].columns[j].sqlType ) ); + _log.debug ("Got value for " + metaData.getTableName(count) + "." + metaData.getColumnName(count) + ": " + temp[j]); + count++; if ( temp[j] != null ) { notNull = true; } @@ -1918,12 +2286,16 @@ else field = null; } + _log.debug("Loaded field " + _engine._fields[i].getFieldName() + ": " + field); + 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; @@ -1951,27 +2323,43 @@ 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; + String tableName = null; + + // TODO: wrong, as it could be that the first field is not part of the root class. + tableName = _engine._fields[0].tableName; + // Load all the fields. - for ( int i = 0 ; i < _engine._fields.length ; ++i ) { + 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 ) { - fields[i] = loadMultiField( i, count, fields[i] ); + 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. - fields[i] = loadSingleField( i, count ); + counterReference.setCounter (count); + fields[i] = loadSingleField( i, counterReference); + count = counterReference.getCounter(); } - count += _engine._fields[i].columns.length; + // count += _engine._fields[i].columns.length; } return count; } @@ -2004,12 +2392,19 @@ } - // 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; } @@ -2019,7 +2414,48 @@ // 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) { + _log.debug("potential leaf descriptor = " + leafDescriptor.getJavaClass().getName()); + + + if (!leafDescriptor.getJavaClass().getName().equals(_requestedEngine.getDescriptor().getJavaClass().getName())) { + _log.debug ("Increasing number of fields to load from " + originalFieldNumber + " to " + + ((Integer) returnValues[1]).intValue()); + 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); + } + _log.debug ("About to start using persistence engine for " + ((SQLEngine) newEngine).getDescriptor()); + _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 ) @@ -2042,7 +2478,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() ) { @@ -2054,7 +2490,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. @@ -2069,7 +2505,7 @@ // 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: org/exolab/castor/jdo/engine/SQLTypes.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/jdo/engine/SQLTypes.java,v retrieving revision 1.10 diff -u -r1.10 SQLTypes.java --- org/exolab/castor/jdo/engine/SQLTypes.java 29 Oct 2004 14:06:17 -0000 1.10 +++ org/exolab/castor/jdo/engine/SQLTypes.java 15 Mar 2005 13:35:56 -0000 @@ -361,7 +361,6 @@ return java.toString(); } - public static Object getObject( ResultSet rs, int index, int sqlType ) throws SQLException { Index: 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 --- org/exolab/castor/mapping/loader/ClassDescriptorImpl.java 5 Mar 2005 13:41:52 -0000 1.2 +++ org/exolab/castor/mapping/loader/ClassDescriptorImpl.java 15 Mar 2005 13:35:56 -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,7 +72,7 @@ public class ClassDescriptorImpl implements ClassDescriptor { - + private static final Log _log = LogFactory.getLog (ClassDescriptorImpl.class); private ClassMapping _map; /** @@ -85,6 +91,12 @@ * 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; @@ -166,6 +178,13 @@ throw new MappingException( "mapping.classDoesNotExtend", _javaClass.getName(), extend.getJavaClass().getName() ); _extends = extend; + + _log.debug (_extends.getClass().getName()); + 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 +251,31 @@ 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) + { + _log.debug ("Adding ClassDescriptor of type " + classDesc.getClass().getName()); + _extendedBy.add (classDesc); } public ClassDescriptor getDepends() { Index: org/exolab/castor/persist/ClassMolder.java =================================================================== RCS file: /scm/castor/castor/src/main/org/exolab/castor/persist/ClassMolder.java,v retrieving revision 1.19 diff -u -r1.19 ClassMolder.java --- org/exolab/castor/persist/ClassMolder.java 8 Mar 2005 20:46:01 -0000 1.19 +++ org/exolab/castor/persist/ClassMolder.java 15 Mar 2005 13:35:59 -0000 @@ -65,6 +65,7 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -463,17 +464,31 @@ 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"); + return buffer.toString(); + } +}