Package org.exolab.castor.jdo.engine

Source Code of org.exolab.castor.jdo.engine.SQLStatementLoad

/*
* 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 7951 2008-10-09 20:52:17Z wguttmn $
*/
package org.exolab.castor.jdo.engine;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
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.jdo.engine.SQLTypeInfos;
import org.castor.jdo.util.JDOUtils;
import org.castor.persist.ProposedEntity;
import org.exolab.castor.jdo.ObjectNotFoundException;
import org.exolab.castor.jdo.PersistenceException;
import org.exolab.castor.jdo.QueryException;
import org.exolab.castor.jdo.engine.nature.ClassDescriptorJDONature;
import org.exolab.castor.mapping.AccessMode;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.persist.spi.Identity;
import org.exolab.castor.persist.spi.Persistence;
import org.exolab.castor.persist.spi.PersistenceFactory;
import org.exolab.castor.persist.spi.QueryExpression;

public final class SQLStatementLoad {
    /** 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(SQLStatementLoad.class);

    private final SQLEngine _engine;
   
    private final PersistenceFactory _factory;
   
    private final String _type;

    private final String _mapTo;

    /** Number of ClassDescriptor that extend this one. */
    private final int _numberOfExtendLevels;

    /** Collection of all the ClassDescriptor that extend this one (closure). */
    private final Collection _extendingClassDescriptors;

    private String _statementNoLock;
   
    private String _statementLock;

    private QueryExpression _queryExpression;

    public SQLStatementLoad(final SQLEngine engine, final PersistenceFactory factory)
    throws MappingException {
        _engine = engine;
        _factory = factory;
        _type = engine.getDescriptor().getJavaClass().getName();
        _mapTo = new ClassDescriptorJDONature(engine.getDescriptor()).getTableName();

        // obtain the number of ClassDescriptor that extend this one.
        _numberOfExtendLevels = SQLHelper.numberOfExtendingClassDescriptors(engine.getDescriptor());
        _extendingClassDescriptors =
            new ClassDescriptorJDONature(engine.getDescriptor()).getExtended();

        buildStatement();
    }

    private void buildStatement() throws MappingException {
        try {
            QueryExpression expr = _factory.getQueryExpression();
           
            Map identitiesUsedForTable = new HashMap();
            Vector joinTables = new Vector();

            // 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(baseDesc).getTableName(), baseDescIdNames);
                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);
                        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 classDescriptorsToAdd = new LinkedList();
            ClassDescriptor classDescriptor = null;
            SQLHelper.addExtendingClassDescriptors(classDescriptorsToAdd,
                    new ClassDescriptorJDONature(_engine.getDescriptor()).getExtended());
           
            if (classDescriptorsToAdd.size() > 0) {
                for (Iterator iter = classDescriptorsToAdd.iterator(); iter.hasNext(); ) {
                    classDescriptor = (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);

                    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());
                        }
                    }
                }
            }
           
            QueryExpression find = (QueryExpression) expr.clone();
           
            // get id columns' names
            for (int i = 0; i < ids.length; i++) {
                expr.addParameter(_mapTo, ids[i].getName(), QueryExpression.OP_EQUALS);
            }

            _statementNoLock = expr.getStatement(false);
            _statementLock = expr.getStatement(true);

            // 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++) {
                    find.addColumn(_mapTo, ids[i].getName());
                }
            }

            _queryExpression = find;

            if (LOG.isTraceEnabled()) {
                LOG.trace(Messages.format("jdo.loading", _type, _statementNoLock));
                LOG.trace(Messages.format("jdo.loading.with.lock", _type, _statementLock));
                LOG.trace(Messages.format("jdo.finding", _type, _queryExpression));
            }
        } catch (QueryException ex) {
            LOG.warn("Problem building SQL", ex);
            throw new MappingException(ex);
        }
    }
   
    public String getLoadStatement() {
        return _statementNoLock;
    }

    public QueryExpression getQueryExpression() {
        return (QueryExpression) _queryExpression.clone();
    }

    public void executeStatement(final Connection conn, final Identity identity,
                                   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
            if (identity.size() != ids.length) {
                throw new PersistenceException(
                        "Size of identity field mismatched! expected: " + ids.length
                        + " found: " + identity.size());
            }

            for (int i = 0; i < ids.length; i++) {
                stmt.setObject(fieldIndex++, ids[i].toSQL(identity.get(i)));
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug(Messages.format("jdo.loading", _type, stmt.toString()));
            }

            // execute the SQL query
            rs = stmt.executeQuery();
            if (!rs.next()) {
                throw new ObjectNotFoundException(Messages.format(
                        "persist.objectNotFound", _type, identity));
            }

            // if this class is part of an extend relation (hierarchy), let's investigate
            // what the real class type is vs. the specified one as part of the load statement;
            // this is done by looking at (the id fields of all) the extending class
            // desriptors, and by trying to find a (if not the) potential leaf descriptor;
            // if there's no potential leaf descriptor, let's simply continue; if there's
            // one, set the actual values in the ProposedEntity instance and return
            // to indicate that the load shoul dbe re-tried with the correct ClassMolder
            if (_extendingClassDescriptors.size() > 0) {
                Object[] returnValues =
                    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);
                }

                // make sure that we only return early (as described above), if we actually
                // found a potential leaf descriptor
                if (potentialLeafDescriptor != null) {
                    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;
                }
                processedTables.add(tableName);
               
                if (!field.isJoined() && (field.getJoinFields() == null)) {
                    entity.setField(columns[0].toJava(SQLTypeInfos.getValue(
                            rs, columnIndex++, columns[0].getSqlType())), i);
                    fieldIndex++;
                } else if (!field.isMulti()) {
                    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++;
                        if (id[j] != null) { notNull = true; }
                    }
                    entity.setField(((notNull) ? new Identity(id) : null), i);
                } else {
                    ArrayList res = new ArrayList();
                    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()));
                        if (id[j] != null) { notNull = true; }
                        fieldIndex++;
                        columnIndex++;
                    }
                    if (notNull) { res.add(new Identity(id)); }
                    entity.setField(res, i);
                }
            }

            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;
                    }
                    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()));
                            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),
                    except);
            throw new PersistenceException(Messages.format("persist.nested", except), except);
        } finally {
            JDOUtils.closeResultSet(rs);
            JDOUtils.closeStatement(stmt);
        }
    }
}
TOP

Related Classes of org.exolab.castor.jdo.engine.SQLStatementLoad

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.