org.eclipse.persistence.internal.helper.LOBValueWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.helper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.internal.databaseaccess.Accessor;
import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.expressions.ForUpdateClause;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.platform.database.OraclePlatform;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.WriteObjectQuery;
/**
* INTERNAL:
* Purpose:LOBValueWriter is used to write a large size of object into an Oracle
* CLOB/BLOB column through Oracle LOB Locator. It's a work-around object for the well-known 4k write
* limits on an Oracle thin driver.
*
*
Responsibilities:
* - Build the Oracle empty lob method call string for the insert call.
*
- Build the minimal SELECT call to retrieve the locator.
*
- Write the lob value through the locator.
*
- Resolve the multiple table INSERT/SELECT orders.
*
- Resolve the nested unit of work commit issue.
*
*
* @author King Wang
* @since TopLink/Java 5.0. July 2002.
*/
public class LOBValueWriter {
//DatabaseCalls still to be processed
private Collection calls = null;
private Accessor accessor;
private boolean isNativeConnectionRequired;
/**
* This is the default constructor for the class.
*
* Bug 2804663 - Each DatabaseAccessor will now hold on to its own instance
* of this class, hence a singleton pattern is not applicable.
*/
public LOBValueWriter(Accessor accessor) {
this.accessor = accessor;
DatabasePlatform platform = ((DatabaseAccessor)accessor).getPlatform();
this.isNativeConnectionRequired = platform.isOracle() && ((OraclePlatform)platform).isNativeConnectionRequiredForLobLocator();
}
protected void buildAndExecuteCall(DatabaseCall dbCall, AbstractSession session) {
DatabaseQuery query = dbCall.getQuery();
if (!query.isWriteObjectQuery()) {
//if not writequery, should not go through the locator writing..
return;
}
WriteObjectQuery writeQuery = (WriteObjectQuery)query;
writeQuery.setAccessor(accessor);
//build a select statement form the query
SQLSelectStatement selectStatement = buildSelectStatementForLocator(writeQuery, dbCall, session);
//then build a call from the statement
DatabaseCall call = buildCallFromSelectStatementForLocator(selectStatement, writeQuery, dbCall, session);
accessor.executeCall(call, call.getQuery().getTranslationRow(), session);
}
/**
* Fetch the locator(s) from the result set and write LOB value to the table
*/
public void fetchLocatorAndWriteValue(DatabaseCall dbCall, Object resultSet) throws SQLException {
Enumeration enumFields = dbCall.getContexts().getFields().elements();
Enumeration> enumValues = dbCall.getContexts().getValues().elements();
AbstractSession executionSession = dbCall.getQuery().getSession().getExecutionSession(dbCall.getQuery());
while (enumFields.hasMoreElements()) {
DatabaseField field = enumFields.nextElement();
Object value = enumValues.nextElement();
//write the value through the locator
executionSession.getPlatform().writeLOB(field, value, (ResultSet)resultSet, executionSession);
}
}
/**
* Build the select statement for selecting the locator
*/
private SQLSelectStatement buildSelectStatementForLocator(WriteObjectQuery writeQuery, DatabaseCall call, AbstractSession session) {
SQLSelectStatement selectStatement = new SQLSelectStatement();
Vector tables = writeQuery.getDescriptor().getTables();
selectStatement.setTables(tables);
//rather than get ALL fields from the descriptor, only use the LOB-related fields to build the minimal SELECT statement.
selectStatement.setFields(call.getContexts().getFields());
//the where clause setting here is sufficient if the object does not map to multiple tables.
selectStatement.setWhereClause(writeQuery.getDescriptor().getObjectBuilder().buildPrimaryKeyExpressionFromObject(writeQuery.getObject(), session));
//need pessimistic locking for the locator select
selectStatement.setLockingClause(ForUpdateClause.newInstance(ObjectBuildingQuery.LOCK));
if (tables.size() > 1) {
//the primary key expression from the primary table
Expression expression = selectStatement.getWhereClause();
//additional join from the non-primary tables
Expression additionalJoin = writeQuery.getDescriptor().getQueryManager().getAdditionalJoinExpression();
if (additionalJoin != null) {
expression = expression.and(additionalJoin);
}
//where clause now contains extra joins across all tables
selectStatement.setWhereClause(expression);
}
//normalize the statement at the end, such as assign alias to all tables, and build sorting statement
selectStatement.normalize(session, writeQuery.getDescriptor());
return selectStatement;
}
/**
* Build the sql call from the select statement for selecting the locator
*/
private DatabaseCall buildCallFromSelectStatementForLocator(SQLSelectStatement selectStatement, WriteObjectQuery writeQuery, DatabaseCall dbCall, AbstractSession session) {
DatabaseCall call = selectStatement.buildCall(session);
// Locator LOB must not be wrapped (WLS wraps LOBs).
call.setIsNativeConnectionRequired(this.isNativeConnectionRequired);
//the LOB context must be passed into the new call object
call.setContexts(dbCall.getContexts());
//need to explicitly define one row return, otherwise, EL assumes multiple rows return and confuses the accessor
call.returnOneRow();
//the query object has to be set in order to access to the platform and login objects
call.setQuery(writeQuery);
// prepare it
call.prepare(session);
//finally do the translation
call.translate(writeQuery.getTranslationRow(), writeQuery.getModifyRow(), session);
return call;
}
// Building of SELECT statements is no longer done in DatabaseAccessor.basicExecuteCall
// for updates because DatabaseCall.isUpdateCall() can't recognize update in case
// StoredProcedureCall is used. Therefore in all cases: insert(single or multiple tables)
// and update the original (insert and update) calls are saved
// and both building and executing of SELECT statements postponed until
// buildAndExecuteSelectCalls method is called.
/**
* Add original (insert or update) call to the collection
*/
public void addCall(Call call) {
if (calls == null) {
//use lazy initialization
calls = new ArrayList<>(2);
}
calls.add((DatabaseCall) call);
}
// Bug 3110860: RETURNINGPOLICY-OBTAINED PK CAUSES LOB TO BE INSERTED INCORRECTLY
// The deferred locator SELECT calls should be generated and executed after ReturningPolicy
// merges PK obtained from the db into the object held by the query.
// That's why original (insert or update) calls are saved,
// and both building and executing of SELECT statements postponed until
// this method is called.
/**
* Build and execute the deferred select calls.
*/
public void buildAndExecuteSelectCalls(AbstractSession session) {
if ((calls == null) || calls.isEmpty()) {
//no deferred select calls (it means no locator is required)
return;
}
//all INSERTs have been executed, time to execute the SELECTs
try {
for (Iterator callIt = calls.iterator(); callIt.hasNext();) {
DatabaseCall dbCall = callIt.next();
buildAndExecuteCall(dbCall, session);
}
} finally {
//after executing all select calls, need to empty the collection.
//this is necessary in the nested unit of work cases.
calls.clear();
}
}
}