Index: cpa/src/main/java/org/castor/cpa/persistence/sql/query/visitor/UncoupleVisitor.java =================================================================== --- cpa/src/main/java/org/castor/cpa/persistence/sql/query/visitor/UncoupleVisitor.java (revision 0) +++ cpa/src/main/java/org/castor/cpa/persistence/sql/query/visitor/UncoupleVisitor.java (revision 0) @@ -0,0 +1,139 @@ +/* + * Copyright 2010 Dennis Butterstein, Ralf Joachim + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Id: SQLStatementDelete.java 8469 2009-12-28 16:47:54Z rjoachim $ + */ + +package org.castor.cpa.persistence.sql.query.visitor; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.castor.cpa.persistence.sql.query.Assignment; +import org.castor.cpa.persistence.sql.query.Delete; +import org.castor.cpa.persistence.sql.query.Insert; +import org.castor.cpa.persistence.sql.query.Select; +import org.castor.cpa.persistence.sql.query.Table; +import org.castor.cpa.persistence.sql.query.Update; +import org.castor.cpa.persistence.sql.query.Visitor; +import org.castor.cpa.persistence.sql.query.condition.AndCondition; +import org.castor.cpa.persistence.sql.query.condition.Compare; +import org.castor.cpa.persistence.sql.query.condition.IsNullPredicate; +import org.castor.cpa.persistence.sql.query.condition.OrCondition; +import org.castor.cpa.persistence.sql.query.expression.Column; +import org.castor.cpa.persistence.sql.query.expression.Expression; +import org.castor.cpa.persistence.sql.query.expression.NextVal; +import org.castor.cpa.persistence.sql.query.expression.Parameter; + +/** + * Visitor constructing result column map for queries to uncouple the order of + * select-columns from the order of resultset-columns. + * + * @author Dennis Butterstein + * @version $Revision: 8469 $ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ + */ +public final class UncoupleVisitor implements Visitor { + //----------------------------------------------------------------------------------- + + /** Map of parameter names to indices filled during build of SQL query string. */ + private final Map _resultColumnMap = new HashMap(); + + //----------------------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + public void visit(final Assignment assignment) { } + + /** + * {@inheritDoc} + */ + public void visit(final Delete delete) { } + + /** + * {@inheritDoc} + */ + public void visit(final Insert insert) { } + + /** + * {@inheritDoc} + */ + public void visit(final Select select) { + for (Iterator iter = select.getSelect().iterator(); iter.hasNext(); ) { + iter.next().accept(this); + } + } + + /** + * {@inheritDoc} + */ + public void visit(final Table table) { } + + /** + * {@inheritDoc} + */ + public void visit(final Update update) { } + + /** + * {@inheritDoc} + */ + public void visit(final AndCondition andCondition) { } + + /** + * {@inheritDoc} + */ + public void visit(final Compare compare) { } + + /** + * {@inheritDoc} + */ + public void visit(final IsNullPredicate isNullPredicate) { } + + /** + * {@inheritDoc} + */ + public void visit(final OrCondition orCondition) { } + + /** + * {@inheritDoc} + */ + public void visit(final Column column) { + _resultColumnMap.put(column.toString(), _resultColumnMap.size() + 1); + } + + /** + * {@inheritDoc} + */ + public void visit(final NextVal nextVal) { } + + /** + * {@inheritDoc} + */ + public void visit(final Parameter parameter) { } + + //----------------------------------------------------------------------------------- + + /** + * Method returning constructed result columns map. + * + * @return Constructed result columns map. + */ + public Map getResultColumnMap() { + return _resultColumnMap; + } + + //----------------------------------------------------------------------------------- +} Index: cpa/src/main/java/org/exolab/castor/jdo/engine/SQLEngine.java =================================================================== --- cpa/src/main/java/org/exolab/castor/jdo/engine/SQLEngine.java (revision 8639) +++ cpa/src/main/java/org/exolab/castor/jdo/engine/SQLEngine.java (working copy) @@ -184,6 +184,16 @@ } } + for (int i = 0; i < fieldsInfo.size(); i++) { + SQLFieldInfo info = fieldsInfo.get(i); + if (info.isJoined()) { + String alias = info.getTableName() + "_f" + i; + info.setTableAlias(alias); + } else { + info.setTableAlias(info.getTableName()); + } + } + _ids = new SQLColumnInfo[idsInfo.size()]; idsInfo.copyInto(_ids); Index: cpa/src/main/java/org/exolab/castor/jdo/engine/SQLFieldInfo.java =================================================================== --- cpa/src/main/java/org/exolab/castor/jdo/engine/SQLFieldInfo.java (revision 8563) +++ cpa/src/main/java/org/exolab/castor/jdo/engine/SQLFieldInfo.java (working copy) @@ -37,6 +37,9 @@ public class SQLFieldInfo { private final String _tableName; + /** Variable holding the alias for this table. */ + private String _tableAlias; + private final boolean _store; private final boolean _multi; @@ -169,6 +172,20 @@ public String getTableName() { return _tableName; } + /** + * Method returning table-alias currently set. + * + * @return Table-alias currently set. + */ + public String getTableAlias() { return _tableAlias; } + + /** + * Method setting current table-alias. + * + * @param tableAlias Table-alias to be set. + */ + public void setTableAlias(final String tableAlias) { _tableAlias = tableAlias; } + public boolean isStore() { return _store; } public boolean isMulti() { return _multi; } Index: cpa/src/main/java/org/exolab/castor/jdo/engine/SQLStatementLoad.java =================================================================== --- cpa/src/main/java/org/exolab/castor/jdo/engine/SQLStatementLoad.java (revision 8563) +++ cpa/src/main/java/org/exolab/castor/jdo/engine/SQLStatementLoad.java (working copy) @@ -24,17 +24,19 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.core.util.Messages; +import org.castor.cpa.persistence.sql.query.Select; +import org.castor.cpa.persistence.sql.query.Table; +import org.castor.cpa.persistence.sql.query.expression.Column; +import org.castor.cpa.persistence.sql.query.visitor.UncoupleVisitor; import org.castor.jdo.engine.SQLTypeInfos; import org.castor.jdo.util.JDOUtils; import org.castor.persist.ProposedEntity; @@ -63,6 +65,9 @@ private final String _mapTo; + /** Map storing mapping between select-column and resultset-column. */ + private Map _resultColumnMap; + /** Number of ClassDescriptor that extend this one. */ private final int _numberOfExtendLevels; @@ -89,12 +94,15 @@ } private void buildStatement() throws MappingException { + // Provisional select to construct map holding mapping between select- and + // resultset-columns. + Select select = new Select("provisional Select"); try { QueryExpression expr = _factory.getQueryExpression(); Map identitiesUsedForTable = new HashMap(); Vector joinTables = new Vector(); - + // join all the extended table ClassDescriptor curDesc = _engine.getDescriptor(); ClassDescriptor baseDesc; @@ -114,23 +122,18 @@ SQLColumnInfo[] ids = _engine.getColumnInfoForIdentities(); SQLFieldInfo[] fields = _engine.getInfo(); - // join all the related/depended table - String aliasOld = null; - String alias = null; for (int i = 0; i < fields.length; i++) { SQLFieldInfo field = fields[i]; - - if (i > 0) { aliasOld = alias; } - alias = field.getTableName(); + String tableName = field.getTableName(); // add id fields for root table if first field points to a separate table if ((i == 0) && field.isJoined()) { String[] identities = SQLHelper.getIdentitySQLNames(_engine.getDescriptor()); for (int j = 0; j < identities.length; j++) { - expr.addColumn( - new ClassDescriptorJDONature(curDesc).getTableName(), - identities[j]); + String name = new ClassDescriptorJDONature(curDesc).getTableName(); + select.addSelect(new Column(new Table(name), identities[j])); + expr.addColumn(name, identities[j]); } identitiesUsedForTable.put( new ClassDescriptorJDONature(curDesc).getTableName(), @@ -138,7 +141,7 @@ } // add id columns to select statement - if (!alias.equals(aliasOld) && !field.isJoined()) { + if (!field.isJoined()) { ClassDescriptor classDescriptor = field.getFieldDescriptor().getContainingClassDescriptor(); boolean isTableNameAlreadyAdded = identitiesUsedForTable.containsKey( @@ -146,7 +149,8 @@ if (!isTableNameAlreadyAdded) { String[] identities = SQLHelper.getIdentitySQLNames(classDescriptor); for (int j = 0; j < identities.length; j++) { - expr.addColumn(alias, identities[j]); + select.addSelect(new Column(new Table(tableName), identities[j])); + expr.addColumn(tableName, identities[j]); } identitiesUsedForTable.put( new ClassDescriptorJDONature(classDescriptor).getTableName(), @@ -166,21 +170,22 @@ if (joinTables.contains(field.getTableName()) || nature.getTableName().equals(field.getTableName())) { - // should not mix with aliases in ParseTreeWalker - alias = alias.replace('.', '_') + "_f" + i; - expr.addOuterJoin(_mapTo, leftCol, field.getTableName(), rightCol, alias); + tableName = field.getTableAlias(); + expr.addOuterJoin(_mapTo, leftCol, field.getTableName(), rightCol, + tableName); } else { - expr.addOuterJoin(_mapTo, leftCol, - field.getTableName(), rightCol, field.getTableName()); - joinTables.add(field.getTableName()); + expr.addOuterJoin(_mapTo, leftCol, tableName, rightCol, tableName); + joinTables.add(tableName); } } for (int j = 0; j < field.getColumnInfo().length; j++) { - expr.addColumn(alias, field.getColumnInfo()[j].getName()); + select.addSelect(new Column(new Table(tableName), + field.getColumnInfo()[j].getName())); + expr.addColumn(tableName, field.getColumnInfo()[j].getName()); } - expr.addTable(field.getTableName(), alias); + expr.addTable(field.getTableName(), tableName); } // 'join' all the extending tables @@ -220,6 +225,8 @@ SQLEngine engine = (SQLEngine) persistenceEngine; SQLColumnInfo[] idInfos = engine.getColumnInfoForIdentities(); for (int i = 0; i < idInfos.length; i++) { + select.addSelect(new Column(new Table(clsDescNature.getTableName()), + idInfos[i].getName())); expr.addColumn(clsDescNature.getTableName(), idInfos[i].getName()); } @@ -229,6 +236,8 @@ SQLColumnInfo[] columnInfos = fieldInfos[i].getColumnInfo(); if (clsDescNature.getTableName().equals(fieldInfos[i].getTableName())) { for (int j = 0; j < columnInfos.length; j++) { + select.addSelect(new Column(new Table(clsDescNature.getTableName()), + fieldInfos[i].getColumnInfo()[j].getName())); expr.addColumn(clsDescNature.getTableName(), fieldInfos[i].getColumnInfo()[j].getName()); } @@ -237,7 +246,7 @@ if (hasFieldToAdd) { expr.addTable(clsDescNature.getTableName(), - clsDescNature.getTableName()); + clsDescNature.getTableName()); } } } @@ -250,6 +259,8 @@ _statementNoLock = expr.getStatement(false); _statementLock = expr.getStatement(true); + + System.out.println(_statementNoLock); if (LOG.isTraceEnabled()) { LOG.trace(Messages.format("jdo.loading", _type, _statementNoLock)); @@ -259,27 +270,29 @@ LOG.warn("Problem building SQL", ex); throw new MappingException(ex); } + + UncoupleVisitor uncle = new UncoupleVisitor(); + uncle.visit(select); + _resultColumnMap = uncle.getResultColumnMap(); } public void executeStatement(final Connection conn, final Identity identity, - final ProposedEntity entity, - final AccessMode accessMode) - throws PersistenceException { + final ProposedEntity entity,final AccessMode accessMode) throws PersistenceException { PreparedStatement stmt = null; ResultSet rs = null; SQLColumnInfo[] ids = _engine.getColumnInfoForIdentities(); SQLFieldInfo[] fields = _engine.getInfo(); - + try { boolean locked = accessMode == AccessMode.DbLocked; String sqlString = locked ? _statementLock : _statementNoLock; stmt = conn.prepareStatement(sqlString); - + if (LOG.isTraceEnabled()) { LOG.trace(Messages.format("jdo.loading", _type, stmt.toString())); } - + int fieldIndex = 1; // bind the identity of the preparedStatement for (int i = 0; i < ids.length; i++) { @@ -309,10 +322,10 @@ SQLHelper.calculateNumberOfFields(_extendingClassDescriptors, ids.length, fields.length, _numberOfExtendLevels, rs); ClassDescriptor potentialLeafDescriptor = (ClassDescriptor) returnValues[0]; - + if ((potentialLeafDescriptor != null) && !potentialLeafDescriptor.getJavaClass().getName().equals(_type)) { - + entity.initializeFields(potentialLeafDescriptor.getFields().length); entity.setActualEntityClass(potentialLeafDescriptor.getJavaClass()); entity.setExpanded(true); @@ -324,41 +337,32 @@ return; } } - - // Load all the fields of the object including one-one relations - // index to use during ResultSet.getXXX(); don't forget to ignore - // the identity columns - int columnIndex = ids.length + 1; - - Set processedTables = new HashSet(); - if (fields.length > 0 && fields[0].isJoined()) { - ClassDescriptor clsDesc = _engine.getDescriptor(); - processedTables.add(new ClassDescriptorJDONature(clsDesc).getTableName()); - } + boolean notNull; // index in fields[] for storing result of SQLTypes.getObject() - fieldIndex = 1; - String tableName = null; for (int i = 0; i < fields.length; ++i) { SQLFieldInfo field = fields[i]; SQLColumnInfo[] columns = field.getColumnInfo(); - tableName = field.getTableName(); - if (i > 0 && !field.isJoined() && !processedTables.contains(tableName)) { - columnIndex = columnIndex + ids.length; + + String tableName = field.getTableAlias(); + + // If alias of the field is not used, we have to use the tablename + if (!_resultColumnMap.containsKey( + tableName + "." + field.getColumnInfo()[0].getName())) { + tableName = field.getTableName(); } - processedTables.add(tableName); - + if (!field.isJoined() && (field.getJoinFields() == null)) { entity.setField(columns[0].toJava(SQLTypeInfos.getValue( - rs, columnIndex++, columns[0].getSqlType())), i); - fieldIndex++; + rs, _resultColumnMap.get(tableName + "." + columns[0].getName()), + columns[0].getSqlType())), i); } else if (!field.isMulti()) { - notNull = false; + notNull = false; Object[] id = new Object[columns.length]; for (int j = 0; j < columns.length; j++) { - id[j] = columns[j].toJava(SQLTypeInfos.getValue( - rs, columnIndex++, columns[j].getSqlType())); - fieldIndex++; + id[j] = columns[j].toJava(SQLTypeInfos.getValue(rs, + _resultColumnMap.get(tableName + "." + columns[j].getName()), + columns[j].getSqlType())); if (id[j] != null) { notNull = true; } } entity.setField(((notNull) ? new Identity(id) : null), i); @@ -367,11 +371,10 @@ notNull = false; Object[] id = new Object[columns.length]; for (int j = 0; j < columns.length; j++) { - id[j] = columns[j].toJava(SQLTypeInfos.getValue( - rs, columnIndex, columns[j].getSqlType())); + id[j] = columns[j].toJava(SQLTypeInfos.getValue(rs, + _resultColumnMap.get(tableName + "." + columns[j].getName()), + columns[j].getSqlType())); if (id[j] != null) { notNull = true; } - fieldIndex++; - columnIndex++; } if (notNull) { res.add(new Identity(id)); } entity.setField(res, i); @@ -379,44 +382,35 @@ } while (rs.next()) { - fieldIndex = 1; - columnIndex = ids.length + 1; - processedTables.clear(); - if (fields[0].isJoined()) { - ClassDescriptor clsDesc = _engine.getDescriptor(); - processedTables.add(new ClassDescriptorJDONature(clsDesc).getTableName()); - } - for (int i = 0; i < fields.length; ++i) { SQLFieldInfo field = fields[i]; SQLColumnInfo[] columns = field.getColumnInfo(); - tableName = field.getTableName(); - if (i > 0 && !field.isJoined() && !processedTables.contains(tableName)) { - columnIndex = columnIndex + ids.length; + + String tableName = field.getTableAlias(); + // If alias of the field is not used, we have to use the tablename + if (!_resultColumnMap.containsKey( + tableName + "." + field.getColumnInfo()[0].getName())) { + tableName = field.getTableName(); } - processedTables.add(tableName); - + if (field.isMulti()) { ArrayList res = (ArrayList) entity.getField(i); notNull = false; Object[] id = new Object[columns.length]; for (int j = 0; j < columns.length; j++) { - id[j] = columns[j].toJava(SQLTypeInfos.getValue( - rs, columnIndex, columns[j].getSqlType())); + id[j] = columns[j].toJava(SQLTypeInfos.getValue(rs, + _resultColumnMap.get(tableName + "." + columns[j].getName()), + columns[j].getSqlType())); if (id[j] != null) { notNull = true; } - columnIndex++; } - fieldIndex++; if (notNull) { Identity com = new Identity(id); if (!res.contains(com)) { res.add(com); } } - } else { - fieldIndex++; - columnIndex += columns.length; } } } + } catch (SQLException except) { LOG.fatal(Messages.format("jdo.loadFatal", _type, (accessMode == AccessMode.DbLocked) ? _statementLock : _statementNoLock),