/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.internal.queries; import java.util.*; import org.eclipse.persistence.internal.expressions.*; import org.eclipse.persistence.internal.databaseaccess.*; import org.eclipse.persistence.expressions.*; import org.eclipse.persistence.internal.helper.*; import org.eclipse.persistence.exceptions.*; import org.eclipse.persistence.queries.*; import org.eclipse.persistence.sessions.SessionProfiler; /** * <p><b>Purpose</b>: * Mechanism used for all statement objects. * <p> * <p><b>Responsibilities</b>: * Executes the appropriate statement. * * @author Yvon Lavoie * @since TOPLink/Java 1.0 */ public class StatementQueryMechanism extends CallQueryMechanism { protected SQLStatement sqlStatement; /** Normally only a single statement is used, however multiple table may require multiple statements on write. */ protected Vector sqlStatements; public StatementQueryMechanism() { } /** * INTERNAL: * Return a new mechanism for the query * @param query - owner of mechanism */ public StatementQueryMechanism(DatabaseQuery query) { super(query); } /** * Return a new mechanism for the query * @param query - owner of mechanism * @param statement - sql statement */ public StatementQueryMechanism(DatabaseQuery query, SQLStatement statement) { super(query); this.sqlStatement = statement; } /** * The statement is no longer require after prepare so can be released. */ public void clearStatement() { // Only clear the statement if it is an expression query, otherwise the statement may still be needed. } /** * Clone the mechanism for the specified query clone. */ public DatabaseQueryMechanism clone(DatabaseQuery queryClone) { StatementQueryMechanism clone = (StatementQueryMechanism)super.clone(queryClone); if ((!hasMultipleStatements()) && (getSQLStatement() != null)) { clone.setSQLStatement((SQLStatement)sqlStatement.clone()); } else { Vector currentStatements = getSQLStatements(); if (currentStatements != null) { Vector statementClone = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(currentStatements.size()); Enumeration enumtr = currentStatements.elements(); while (enumtr.hasMoreElements()) { statementClone.addElement(((SQLStatement)enumtr.nextElement()).clone()); } clone.setSQLStatements(statementClone); } } return clone; } /** * INTERNAL: * delete the object * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer deleteObject() throws DatabaseException { // Prepare the calls if not already set (prepare may not have had the modify row). if ((this.call == null) && (!hasMultipleCalls())) { prepareDeleteObject(); if ((this.call == null) && (!hasMultipleCalls())) { return Integer.valueOf(1);// Must be 1 otherwise locking error will occur. } } return super.deleteObject(); } /** * Update the object * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer executeNoSelect() throws DatabaseException { // Prepare the calls if not already set (prepare may not have had the modify row). if ((this.call == null) && (!hasMultipleCalls())) { prepareExecuteNoSelect(); } return super.executeNoSelect(); } /** * Return the selection criteria for the statement. */ public Expression getSelectionCriteria() { return getSQLStatement().getWhereClause(); } /** * INTERNAL: * Return the sqlStatement */ public SQLStatement getSQLStatement() { return sqlStatement; } /** * Normally only a single statement is used, however multiple table may require multiple statements on write. * This is lazy initialied to conserv space. */ public Vector getSQLStatements() { if (sqlStatements == null) { sqlStatements = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(3); } return sqlStatements; } /** * Normally only a single statement is used, however multiple table may require multiple statements on write. * This is lazy initialized to conserve space. */ public boolean hasMultipleStatements() { return (sqlStatements != null) && (!sqlStatements.isEmpty()); } /** * Insert the object * @exception DatabaseException - an error has occurred on the database. */ public void insertObject() throws DatabaseException { // Prepare the calls if not already set (prepare may not have had the modify row). if ((this.call == null) && (!hasMultipleCalls())) { prepareInsertObject(); } super.insertObject(); } /** * Insert the object if the reprepare flag is set, first reprepare the query. * Added for CR#3237 * @param boolean reprepare - whether to reprepare the query. */ public void insertObject(boolean reprepare) { if (reprepare) { // Clear old calls, and reprepare. setCalls(null); trimFieldsForInsert(); prepareInsertObject(); } insertObject(); } /** * INTERNAL * Remove a potential sequence number field and invoke the ReturningPolicy trimModifyRowsForInsert method */ public void trimFieldsForInsert() { getDescriptor().getObjectBuilder().trimFieldsForInsert(getSession(), getModifyRow()); } /** * Return true if this is a call query mechanism */ public boolean isCallQueryMechanism() { return false; } /** * Return true if this is a statement query mechanism */ public boolean isStatementQueryMechanism() { return true; } /** * INTERNAL: * This is different from 'prepareForExecution' in that this is called on the original query, * and the other is called on the copy of the query. * This query is copied for concurrency so this prepare can only setup things that * will apply to any future execution of this query. */ public void prepare() { if ((!hasMultipleStatements()) && (getSQLStatement() == null)) { throw QueryException.sqlStatementNotSetProperly(getQuery()); } // Cannot call super yet as the call is not built. } /** * Pre-build the SQL call from the statement. */ public void prepareCursorSelectAllRows() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareCursorSelectAllRows(); } /** * Pre-build the SQL call from the statement. */ public void prepareDeleteAll() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareDeleteAll(); } /** * Pre-build the SQL call from the statement. */ public void prepareDeleteObject() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareDeleteObject(); } /** * Pre-build the SQL call from the statement. */ public void prepareDoesExist(DatabaseField field) { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); getCall().returnOneRow(); prepareCall(); } /** * Pre-build the SQL call from the statement. */ public void prepareExecuteNoSelect() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareExecuteNoSelect(); } /** * Pre-build the SQL call from the statement. */ public void prepareExecuteSelect() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareExecuteSelect(); } /** * Pre-build the SQL call from the statement. */ public void prepareInsertObject() { // Require modify row to prepare. if (getModifyRow() == null) { return; } if (hasMultipleStatements()) { for (Enumeration statementEnum = getSQLStatements().elements(); statementEnum.hasMoreElements();) { ((SQLModifyStatement)statementEnum.nextElement()).setModifyRow(getModifyRow()); } } else if (getSQLStatement() != null) { ((SQLModifyStatement)getSQLStatement()).setModifyRow(getModifyRow()); } setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareInsertObject(); } /** * Pre-build the SQL call from the statement. */ public void prepareSelectAllRows() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareSelectAllRows(); } /** * Pre-build the SQL call from the statement. */ public void prepareSelectOneRow() { setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareSelectOneRow(); } /** * Pre-build the SQL call from the statement. */ public void prepareUpdateObject() { // Require modify row to prepare. if (getModifyRow() == null) { return; } if (hasMultipleStatements()) { for (Enumeration statementEnum = getSQLStatements().elements(); statementEnum.hasMoreElements();) { ((SQLModifyStatement)statementEnum.nextElement()).setModifyRow(getModifyRow()); } } else if (getSQLStatement() != null) { ((SQLModifyStatement)getSQLStatement()).setModifyRow(getModifyRow()); } setCallFromStatement(); // The statement is no longer require so can be released. clearStatement(); super.prepareUpdateObject(); } /** * Pre-build the SQL call from the statement. */ public void prepareUpdateAll() { setCallFromStatement();// Will build an SQLUpdateAllStatement clearStatement();// The statement is no longer require so can be released. super.prepareUpdateAll(); } /** * Pre-build the SQL call from the statement. */ protected void setCallFromStatement() { // Profile SQL generation. getSession().startOperationProfile(SessionProfiler.SqlGeneration, getQuery(), SessionProfiler.ALL); try { if (hasMultipleStatements()) { for (Enumeration statementEnum = getSQLStatements().elements(); statementEnum.hasMoreElements();) { DatasourceCall call = null; if (getDescriptor() != null) { call = getDescriptor().buildCallFromStatement((SQLStatement)statementEnum.nextElement(), getQuery(), getExecutionSession()); } else { call = ((SQLStatement)statementEnum.nextElement()).buildCall(getExecutionSession()); } // In case of update call may be null if no update required. if (call != null) { addCall(call); } } } else { DatasourceCall call = null; if (getDescriptor() != null) { call = getDescriptor().buildCallFromStatement(getSQLStatement(), getQuery(), getExecutionSession()); } else { call = getSQLStatement().buildCall(getExecutionSession()); } // In case of update call may be null if no update required. if (call != null) { setCall(call); } } } finally { // Profile SQL generation. getSession().endOperationProfile(SessionProfiler.SqlGeneration, getQuery(), SessionProfiler.ALL); } } /** * Set the sqlStatement */ public void setSQLStatement(SQLStatement statement) { this.sqlStatement = statement; } /** * Normally only a single statement is used, however multiple table may require multiple statements on write. * This is lazy initialized to conserve space. */ protected void setSQLStatements(Vector sqlStatements) { this.sqlStatements = sqlStatements; } /** * Update the object * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer updateObject() throws DatabaseException { // Prepare the calls if not already set (prepare may not have had the modify row). if ((this.call == null) && (!hasMultipleCalls())) { prepareUpdateObject(); if ((this.call == null) && (!hasMultipleCalls())) { return Integer.valueOf(1);// Must be 1 otherwise locking error will occur. } } return super.updateObject(); } }