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 7 Mar 2005 08:37:57 -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/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 7 Mar 2005 08:37:59 -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,138 @@ 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 = 1; + JDOClassDescriptor currentClassDescriptor = getDescriptor(); + while (currentClassDescriptor.getExtends() != null) { + currentClassDescriptor = (JDOClassDescriptor) currentClassDescriptor.getExtends(); + numberOfExtendLevels += 1; + } + + JDOClassDescriptor classDescriptor = this.getDescriptor(); + Collection extendingClassDescriptors = classDescriptor.getExtendedBy(); + int suggestedNumberOfFields = originalNumberOfFields; + if (extendingClassDescriptors.size() > 0) { + Collection potentialActualClassDescriptor = new LinkedList(); + int numberOfIdentitiesToAnalyze = 0; + JDOClassDescriptor potentialLeafDescriptor = null; + 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; + } + } + } + } + + _log.debug ("In total " + numberOfIdentitiesToAnalyze + " (extending) identity to analyze."); + _log.debug ("Most likely of type " + potentialLeafDescriptor.getJavaClass().getName()); + + Object[] expandedFields = new Object[suggestedNumberOfFields]; + _log.debug ("After analysis, " + suggestedNumberOfFields + " fields need to be loaded."); + + 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 +1255,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 +1270,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 +1315,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 ); @@ -1372,15 +1525,36 @@ 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]); } // 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()); + } + } + if ( _fields[i].load ) { if ( _fields[i].joined /*&& !joinTables.contains( _fields[i].tableName )*/ ) { int offset = 0; @@ -1404,10 +1578,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 ); @@ -1418,6 +1653,16 @@ } } + + 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 +1688,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 +1843,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 +1878,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 { @@ -1677,8 +1979,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 +1996,7 @@ } catch (SQLException e) { - throw new PersistenceException(e.getMessage()); + throw new PersistenceException(e.getMessage(), e); } return retval; } 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 7 Mar 2005 08:38:00 -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 7 Mar 2005 08:38:01 -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,21 @@ public ClassDescriptor getExtends() { return _extends; + } + + /** + * Returns a collection of class descriptors that extend this class descriptor. + * @return A collection of class descriptors. + */ + public Collection getExtendedBy() + { + return _extendedBy; + } + + 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.18 diff -u -r1.18 ClassMolder.java --- org/exolab/castor/persist/ClassMolder.java 7 Feb 2005 20:45:05 -0000 1.18 +++ org/exolab/castor/persist/ClassMolder.java 7 Mar 2005 08:38:04 -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(); + } +}