/* * Copyright 2006 Assaf Arkin, Thomas Yip, Bruce Snyder, Werner Guttmann, 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: SQLStatementLoad.java 8435 2009-09-10 21:56:23Z rjoachim $ */ package org.exolab.castor.jdo.engine; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.core.util.Messages; import org.exolab.castor.jdo.QueryException; import org.exolab.castor.jdo.engine.nature.ClassDescriptorJDONature; import org.exolab.castor.mapping.ClassDescriptor; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.persist.spi.Persistence; import org.exolab.castor.persist.spi.PersistenceFactory; import org.exolab.castor.persist.spi.QueryExpression; public final class SQLStatementQuery { /** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta * Commons Logging</a> instance used for all logging. */ private static final Log LOG = LogFactory.getLog(SQLStatementQuery.class); private final SQLEngine _engine; private final PersistenceFactory _factory; private final String _type; private final String _mapTo; private QueryExpression _queryExpression; public SQLStatementQuery(final SQLEngine engine, final PersistenceFactory factory) throws MappingException { _engine = engine; _factory = factory; _type = engine.getDescriptor().getJavaClass().getName(); _mapTo = new ClassDescriptorJDONature(engine.getDescriptor()).getTableName(); buildStatement(); } private void buildStatement() throws MappingException { try { QueryExpression expr = _factory.getQueryExpression(); Map<String, Boolean> identitiesUsedForTable = new HashMap<String, Boolean>(); Vector<String> joinTables = new Vector<String>(); // join all the extended table ClassDescriptor curDesc = _engine.getDescriptor(); ClassDescriptor baseDesc; while (curDesc.getExtends() != null) { baseDesc = curDesc.getExtends(); String[] curDescIdNames = SQLHelper.getIdentitySQLNames(curDesc); String[] baseDescIdNames = SQLHelper.getIdentitySQLNames(baseDesc); expr.addInnerJoin( new ClassDescriptorJDONature(curDesc).getTableName(), curDescIdNames, new ClassDescriptorJDONature(curDesc).getTableName(), new ClassDescriptorJDONature(baseDesc).getTableName(), baseDescIdNames, new ClassDescriptorJDONature(baseDesc).getTableName()); joinTables.add(new ClassDescriptorJDONature(baseDesc).getTableName()); curDesc = baseDesc; } 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(); // 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]); } identitiesUsedForTable.put( new ClassDescriptorJDONature(curDesc).getTableName(), Boolean.TRUE); } // add id columns to select statement if (!alias.equals(aliasOld) && !field.isJoined()) { ClassDescriptor classDescriptor = field.getFieldDescriptor().getContainingClassDescriptor(); boolean isTableNameAlreadyAdded = identitiesUsedForTable.containsKey( new ClassDescriptorJDONature(classDescriptor).getTableName()); if (!isTableNameAlreadyAdded) { String[] identities = SQLHelper.getIdentitySQLNames(classDescriptor); for (int j = 0; j < identities.length; j++) { expr.addColumn(alias, identities[j]); } identitiesUsedForTable.put( new ClassDescriptorJDONature(classDescriptor).getTableName(), Boolean.TRUE); } } if (field.isJoined()) { int offset = 0; String[] rightCol = field.getJoinFields(); String[] leftCol = new String[ids.length - offset]; for (int j = 0; j < leftCol.length; j++) { leftCol[j] = ids[j + offset].getName(); } ClassDescriptor clsDescriptor = _engine.getDescriptor(); ClassDescriptorJDONature nature = new ClassDescriptorJDONature(clsDescriptor); 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); } else { expr.addOuterJoin(_mapTo, leftCol, field.getTableName(), rightCol, field.getTableName()); joinTables.add(field.getTableName()); } } for (int j = 0; j < field.getColumnInfo().length; j++) { expr.addColumn(alias, field.getColumnInfo()[j].getName()); } expr.addTable(field.getTableName(), alias); } // 'join' all the extending tables List<ClassDescriptor> classDescriptorsToAdd = new LinkedList<ClassDescriptor>(); ClassDescriptor classDescriptor = null; SQLHelper.addExtendingClassDescriptors(classDescriptorsToAdd, new ClassDescriptorJDONature(_engine.getDescriptor()).getExtended()); if (classDescriptorsToAdd.size() > 0) { Iterator<ClassDescriptor> iter = classDescriptorsToAdd.iterator(); while (iter.hasNext()) { classDescriptor = iter.next(); ClassDescriptorJDONature clsDescNature = new ClassDescriptorJDONature(classDescriptor); if (LOG.isTraceEnabled()) { LOG.trace("Adding outer left join for " + classDescriptor.getJavaClass().getName() + " on table " + clsDescNature.getTableName()); } String[] engDescIdNames = SQLHelper.getIdentitySQLNames( _engine.getDescriptor()); String[] clsDescIdNames = SQLHelper.getIdentitySQLNames(classDescriptor); expr.addOuterJoin(_mapTo, engDescIdNames, clsDescNature.getTableName(), clsDescIdNames, clsDescNature.getTableName()); Persistence persistenceEngine; try { persistenceEngine = _factory.getPersistence(classDescriptor); } catch (MappingException e) { throw new QueryException( "Problem obtaining persistence engine for ClassDescriptor " + classDescriptor.getJavaClass().getName(), e); } SQLEngine engine = (SQLEngine) persistenceEngine; SQLColumnInfo[] idInfos = engine.getColumnInfoForIdentities(); for (int i = 0; i < idInfos.length; i++) { expr.addColumn(clsDescNature.getTableName(), idInfos[i].getName()); } SQLFieldInfo[] fieldInfos = ((SQLEngine) persistenceEngine).getInfo(); for (int i = 0; i < fieldInfos.length; i++) { boolean hasFieldToAdd = false; SQLColumnInfo[] columnInfos = fieldInfos[i].getColumnInfo(); if (clsDescNature.getTableName().equals(fieldInfos[i].getTableName())) { for (int j = 0; j < columnInfos.length; j++) { expr.addColumn(clsDescNature.getTableName(), fieldInfos[i].getColumnInfo()[j].getName()); } hasFieldToAdd = true; } if (hasFieldToAdd) { expr.addTable(clsDescNature.getTableName(), clsDescNature.getTableName()); } } } } // add table information if the class in question does not have any non-identity fields if (fields.length == 0) { for (int i = 0; i < ids.length; i++) { expr.addColumn(_mapTo, ids[i].getName()); } } _queryExpression = expr; if (LOG.isTraceEnabled()) { LOG.trace(Messages.format("jdo.finding", _type, _queryExpression)); } } catch (QueryException ex) { LOG.warn("Problem building SQL", ex); throw new MappingException(ex); } } public QueryExpression getQueryExpression() { return (QueryExpression) _queryExpression.clone(); } }