Package org.exolab.castor.jdo.engine

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

/*
* 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: SQLStatementStore.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 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.persist.ProposedEntity;
import org.exolab.castor.jdo.ObjectDeletedException;
import org.exolab.castor.jdo.ObjectModifiedException;
import org.exolab.castor.jdo.PersistenceException;
import org.exolab.castor.jdo.engine.nature.ClassDescriptorJDONature;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.persist.spi.Identity;
import org.exolab.castor.persist.spi.PersistenceFactory;
import org.exolab.castor.persist.spi.QueryExpression;

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

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

    private final String _mapTo;

    /** Indicates whether there is a field to persist at all; in the case of
     *  EXTEND relationships where no additional attributes are defined in the
     *  extending class, this might NOT be the case; in general, a class has to have
     *  at least one field that is to be persisted. */
    private boolean _hasFieldsToPersist = false;

    private String _statementLazy;

    private String _statementDirty;
   
    private String _statementLoad;

    public SQLStatementStore(final SQLEngine engine, final PersistenceFactory factory,
                             final String load) {
        _engine = engine;
        _factory = factory;
        _type = engine.getDescriptor().getJavaClass().getName();
        _mapTo = new ClassDescriptorJDONature(engine.getDescriptor()).getTableName();
       
        // iterate through all fields to check whether there is a field
        // to persist at all; in the case of extend relationships where no
        // additional attributes are defined in the extending class, this
        // might NOT be the case
        SQLFieldInfo[] fields = _engine.getInfo();
        for (int i = 0; i < fields.length; ++i) {
            if (fields[i].isStore()) {
                _hasFieldsToPersist = true;
                break;
            }
        }

        if (LOG.isTraceEnabled()) {
            LOG.trace("hasFieldsToPersist = " + _hasFieldsToPersist);
        }

        buildStatement();
       
        _statementLoad = load;
    }
   
    private void buildStatement() {
        StringBuffer sql = new StringBuffer("UPDATE ");
        sql.append(_factory.quoteName(_mapTo));
       
        // append the SET clause only if there are any fields that need to be persisted.
        if (_hasFieldsToPersist) {
            sql.append(" SET ");
           
            int count = 0;
            SQLFieldInfo[] fields = _engine.getInfo();
            for (int i = 0; i < fields.length; ++i) {
                if (fields[i].isStore()) {
                    SQLColumnInfo[] columns = fields[i].getColumnInfo();
                    for (int j = 0; j < columns.length; j++) {
                        if (count > 0) { sql.append(','); }
                        sql.append(_factory.quoteName(columns[j].getName()));
                        sql.append("=?");
                        ++count;
                    }
                }
            }
           
            sql.append(JDBCSyntax.WHERE);

            SQLColumnInfo[] ids = _engine.getColumnInfoForIdentities();
            for (int i = 0; i < ids.length; i++) {
                if (i > 0) { sql.append(" AND "); }
                sql.append(_factory.quoteName(ids[i].getName()));
                sql.append(QueryExpression.OP_EQUALS);
                sql.append(JDBCSyntax.PARAMETER);
            }

            _statementLazy = sql.toString();
           
            if (LOG.isTraceEnabled()) {
                LOG.trace(Messages.format("jdo.updating", _type, _statementLazy));
            }

            for (int i = 0; i < fields.length; ++i) {
                if (fields[i].isStore() && fields[i].isDirtyCheck()) {
                    SQLColumnInfo[] columns = fields[i].getColumnInfo();
                    for (int j = 0; j < columns.length; j++) {
                        sql.append(" AND ");
                        sql.append(_factory.quoteName(columns[j].getName()));
                        sql.append("=?");
                    }
                }
            }
           
            _statementDirty = sql.toString();
           
            if (LOG.isTraceEnabled()) {
                LOG.trace(Messages.format("jdo.updating", _type, _statementDirty));
            }
        }
    }
   
    public Object executeStatement(final Connection conn, final Identity identity,
                                   final ProposedEntity newentity,
                                   final ProposedEntity oldentity)
    throws PersistenceException {
        PreparedStatement stmt = null;
        int count;
        String storeStatement = null;

        // Must store record in parent table first.
        // All other dependents are stored independently.
        SQLEngine extended = _engine.getExtends();
        if (extended != null) {
            // | quick and very dirty hack to try to make multiple class on the same table work
            ClassDescriptor extDesc = extended.getDescriptor();
            if (!new ClassDescriptorJDONature(extDesc).getTableName().equals(_mapTo)) {
                extended.store(conn, identity, newentity, oldentity);
            }
        }

        // Only build and execute an UPDATE statement if the class to be updated has
        // fields to persist.
        if (_hasFieldsToPersist) {
            try {
                storeStatement = getStoreStatement(oldentity);
                stmt = conn.prepareStatement(storeStatement);
               
                if (LOG.isTraceEnabled()) {
                    LOG.trace(Messages.format("jdo.storing", _type, stmt.toString()));
                }
               
                count = 1;
               
                // bind fields of the row to be stored into the preparedStatement
                SQLFieldInfo[] fields = _engine.getInfo();
                for (int i = 0; i < fields.length; ++i) {
                    if (fields[i].isStore()) {
                        SQLColumnInfo[] columns = fields[i].getColumnInfo();
                        Object value = newentity.getField(i);
                        if (value == null) {
                            for (int j = 0; j < columns.length; j++) {
                                stmt.setNull(count++, columns[j].getSqlType());
                            }
                        } else if (value instanceof Identity) {
                            Identity id = (Identity) value;
                            if (id.size() != columns.length) {
                                throw new PersistenceException("Size of identity field mismatch!");
                            }
                           
                            for (int j = 0; j < columns.length; j++) {
                                SQLTypeInfos.setValue(stmt, count++,
                                        columns[j].toSQL(id.get(j)), columns[j].getSqlType());
                            }
                        } else {
                            if (columns.length != 1) {
                                throw new PersistenceException("Complex field expected!");
                            }
                           
                            SQLTypeInfos.setValue(stmt, count++, columns[0].toSQL(value),
                                    columns[0].getSqlType());
                        }
                    }
                }
               
                // bind the identity of the row to be stored into the preparedStatement
                SQLColumnInfo[] ids = _engine.getColumnInfoForIdentities();
                if (identity.size() != ids.length) {
                    throw new PersistenceException("Size of identity field mismatched!");
                }
               
                for (int i = 0; i < ids.length; i++) {
                    stmt.setObject(count++, ids[i].toSQL(identity.get(i)));
                    if (LOG.isTraceEnabled()) {
                        LOG.trace(Messages.format("jdo.bindingIdentity", ids[i].getName(),
                                ids[i].toSQL(identity.get(i))));
                    }
                }                   
               
                // bind the old fields of the row to be stored into the preparedStatement
                if (oldentity.getFields() != null) {
                    boolean supportsSetNull = _factory.supportsSetNullInWhere();
                   
                    for (int i = 0; i < fields.length; ++i) {
                        if (fields[i].isStore() && fields[i].isDirtyCheck()) {
                            SQLColumnInfo[] columns = fields[i].getColumnInfo();
                            Object value = oldentity.getField(i);
                            if (value == null) {
                                if (supportsSetNull) {
                                    for (int j = 0; j < columns.length; j++) {
                                        stmt.setNull(count++, columns[j].getSqlType());
                                    }
                                }
                            } else if (value instanceof Identity) {
                                Identity id = (Identity) value;
                                if (id.size() != columns.length) {
                                    throw new PersistenceException(
                                            "Size of identity field mismatch!");
                                }
                               
                                for (int j = 0; j < columns.length; j++) {
                                    SQLTypeInfos.setValue(stmt, count++,
                                            columns[j].toSQL(id.get(j)), columns[j].getSqlType());
                                   
                                    if (LOG.isTraceEnabled()) {
                                        LOG.trace(Messages.format("jdo.bindingField",
                                                columns[j].getName(), columns[j].toSQL(id.get(j))));
                                    }
                                }
                            } else {
                                if (columns.length != 1) {
                                    throw new PersistenceException("Complex field expected!");
                                }
                               
                                SQLTypeInfos.setValue(stmt, count++, columns[0].toSQL(value),
                                        columns[0].getSqlType());
                           
                                if (LOG.isTraceEnabled()) {
                                    LOG.trace(Messages.format("jdo.bindingField",
                                            columns[0].getName(), columns[0].toSQL(value)));
                                }
                            }
                        }
                    }
                }
               
                if (LOG.isDebugEnabled()) {
                    LOG.debug(Messages.format("jdo.storing", _type, stmt.toString()));
                }

                if (stmt.executeUpdate() <= 0) { // SAP DB returns -1 here
                    // If no update was performed, the object has been previously
                    // removed from persistent storage or has been modified if
                    // dirty checking. Determine which is which.
                    stmt.close();

                    if (oldentity.getFields() != null) {
                        stmt = conn.prepareStatement(_statementLoad);
                       
                        if (LOG.isTraceEnabled()) {
                            LOG.trace(Messages.format("jdo.storing", _type, stmt.toString()));
                        }
                       
                        // bind the identity to the prepareStatement
                        count = 1;
                        for (int i = 0; i < ids.length; i++) {
                            stmt.setObject(count++, ids[i].toSQL(identity.get(i)));
                        }
                       
                        ResultSet res = stmt.executeQuery();
                        if (res.next()) {                    
                            StringBuffer enlistFieldsNotMatching = new StringBuffer();
                           
                            int numberOfFieldsNotMatching = 0;
                            for (int i = 0; i < fields.length; i++) {
                                SQLColumnInfo[] columns = fields[i].getColumnInfo();
                                Object value = oldentity.getField(i);
                                Object currentField = columns[0].toJava(res.getObject(
                                        columns[0].getName()));
                                if (fields[i].getTableName().compareTo(_mapTo) == 0) {
                                    if ((value == null) || ((value != null)
                                            && (currentField == null))) {
                                        enlistFieldsNotMatching.append("(" + _type + ")."
                                                + columns[0].getName() + ": ");
                                        enlistFieldsNotMatching.append("[" + value + "/"
                                                + currentField + "]");
                                    } else if (!value.equals(currentField)) {
                                        if (numberOfFieldsNotMatching >= 1) {
                                            enlistFieldsNotMatching.append(", ");
                                        }
                                        enlistFieldsNotMatching.append("(" + _type + ")."
                                                + columns[0].getName() + ": ");
                                        enlistFieldsNotMatching.append("[" + value + "/"
                                                + currentField + "]");
                                        numberOfFieldsNotMatching++;
                                    }
                                }
                            }
                            throw new ObjectModifiedException(Messages.format(
                                    "persist.objectModified", _type, identity,
                                    enlistFieldsNotMatching.toString()));
                        }
                    }
                    throw new ObjectDeletedException(Messages.format(
                            "persist.objectDeleted", _type, identity));
                }               
            } catch (SQLException except) {
                LOG.fatal(Messages.format("jdo.storeFatal", _type,  storeStatement), except);
                throw new PersistenceException(Messages.format("persist.nested", except), except);
            } finally {
                try {
                    // Close the insert/select statement
                    if (stmt != null) { stmt.close(); }
                } catch (SQLException except2) {
                    LOG.warn("Problem closing JDBC statement", except2);
                }
            }
        }

        return null;
    }

    /**
     * If the RDBMS doesn't support setNull for "WHERE fld=?" and requires
     * "WHERE fld IS NULL", we need to modify the statement.
     */
    private String getStoreStatement(final ProposedEntity oldentity)
    throws PersistenceException {
        if (oldentity.getFields() == null) {
            return _statementLazy;
        } else if (_factory.supportsSetNullInWhere()) {
            return _statementDirty;
        } else {
            int pos = _statementDirty.length() - 1;
           
            StringBuffer sql = new StringBuffer(pos * 4);
            sql.append(_statementDirty);
           
            SQLFieldInfo[] fields = _engine.getInfo();
            for (int i = fields.length - 1; i >= 0; i--) {
                if (fields[i].isStore() && fields[i].isDirtyCheck()) {
                    SQLColumnInfo[] columns = fields[i].getColumnInfo();
                    Object value = oldentity.getField(i);
                    if (value == null) {
                        for (int j = columns.length - 1; j >= 0; j--) {
                            pos = nextParameter(true, sql, pos);
                        }
                    } else if (value instanceof Identity) {
                        Identity identity = (Identity) value;
                        if (identity.size() != columns.length) {
                            throw new PersistenceException("Size of identity field mismatch!");
                        }

                        for (int j = columns.length - 1; j >= 0; j--) {
                            pos = nextParameter((identity.get(j) == null), sql, pos);
                        }
                    } else {
                        if (columns.length != 1) {
                            throw new PersistenceException("Complex field expected!");
                        }

                        pos = nextParameter(false, sql, pos);
                    }
                }
            }
            return sql.toString();
        }
    }
   
    /**
     * if isNull, replace next "=?" with " IS NULL", otherwise skip next "=?",
     * move "pos" to the left.
     *
     * @param isNull True if =? should be replaced with 'IS NULL'
     * @param sb StringBUffer holding the SQL statement to be modified
     * @param pos The current position (where to apply the replacement).
     * @return The next position.
     */
    private int nextParameter(final boolean isNull, final StringBuffer sb, final int pos) {
        int internalpos = pos;
        for ( ; internalpos > 0; internalpos--) {
            if ((sb.charAt(internalpos - 1) == '=') && (sb.charAt(internalpos) == '?')) {
                break;
            }
        }
        if (internalpos > 0) {
            internalpos--;
            if (isNull) {
                sb.delete(internalpos, internalpos + 2);
                sb.insert(internalpos, " IS NULL");
            }
        }
        return internalpos;
    }
}
TOP

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

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.