blob: f57143a4d71b6cb229fda9af1be33811ce947d81 [file] [log] [blame]
/*
* Copyright (c) 1998, 2019 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
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 1)
// 02/08/2012-2.4 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// // 30/05/2012-2.4 Guy Pelletier
// - 354678: Temp classloader is still being used during metadata processing
// 09/27/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
package org.eclipse.persistence.internal.jpa.metadata.queries;
import static java.sql.Types.STRUCT;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PARAMETER_IN;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PARAMETER_INOUT;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PARAMETER_OUT;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PARAMETER_REF_CURSOR;
import org.eclipse.persistence.annotations.Direction;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.jpa.metadata.ORMetadata;
import org.eclipse.persistence.internal.jpa.metadata.accessors.MetadataAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.queries.StoredFunctionCall;
import org.eclipse.persistence.queries.StoredProcedureCall;
/**
* INTERNAL:
* Object to hold onto a stored procedure parameter metadata.
*
* Key notes:
* - any metadata mapped from XML to this class must be compared in the
* equals method.
* - all metadata mapped from XML should be initialized in the initXMLObject
* method.
* - when loading from annotations, the constructor accepts the metadata
* accessor this metadata was loaded from. Used it to look up any
* 'companion' annotation needed for processing.
* - methods should be preserved in alphabetical order.
*
* @author Guy Pelletier
* @since TopLink 11g
*/
public class StoredProcedureParameterMetadata extends ORMetadata {
private Boolean m_optional;
private Integer m_jdbcType;
private MetadataClass m_type;
private String m_direction;
private String m_mode;
private String m_jdbcTypeName;
private String m_name;
private String m_queryParameter;
private String m_typeName;
/**
* INTERNAL:
* Used for XML loading.
*/
public StoredProcedureParameterMetadata() {
super("<stored-procedure-parameter>");
}
/**
* INTERNAL:
* Used for annotation loading.
*/
public StoredProcedureParameterMetadata(MetadataAnnotation storedProcedureParameter, MetadataAccessor accessor) {
super(storedProcedureParameter, accessor);
m_direction = storedProcedureParameter.getAttributeString("direction");
m_mode = storedProcedureParameter.getAttributeString("mode");
m_name = storedProcedureParameter.getAttributeString("name");
m_queryParameter = storedProcedureParameter.getAttributeString("queryParameter");
m_type = getMetadataClass(storedProcedureParameter.getAttributeClass("type", Void.class));
m_jdbcType = storedProcedureParameter.getAttributeInteger("jdbcType");
m_jdbcTypeName = storedProcedureParameter.getAttributeString("jdbcTypeName");
m_optional = storedProcedureParameter.getAttributeBooleanDefaultFalse("optional");
}
/**
* Builds an ObjectRelationalDatabaseField based on a given OracleArrayTypeMetadata
* instance.
*
* @param aType OracleArrayTypeMetadata instance to be used to construct the field
* @return an ObjectRelationalDatabaseField instance
*/
protected ObjectRelationalDatabaseField buildNestedField(OracleArrayTypeMetadata aType) {
ObjectRelationalDatabaseField dbFld = new ObjectRelationalDatabaseField("");
String nestedType = aType.getNestedType();
dbFld.setSqlTypeName(nestedType);
for (OracleObjectTypeMetadata oType : getEntityMappings().getOracleObjectTypes()) {
if (oType.getName().equals(nestedType)) {
dbFld.setSqlType(STRUCT);
dbFld.setTypeName(oType.getJavaType());
}
}
return dbFld;
}
/**
* INTERNAL:
*/
@Override
public boolean equals(Object objectToCompare) {
if (objectToCompare instanceof StoredProcedureParameterMetadata) {
StoredProcedureParameterMetadata parameter = (StoredProcedureParameterMetadata) objectToCompare;
if (! valuesMatch(m_type, parameter.getType())) {
return false;
}
if (! valuesMatch(m_direction, parameter.getDirection())) {
return false;
}
if (! valuesMatch(m_mode, parameter.getMode())) {
return false;
}
if (! valuesMatch(m_jdbcType, parameter.getJdbcType())) {
return false;
}
if (! valuesMatch(m_jdbcTypeName, parameter.getJdbcTypeName())) {
return false;
}
if (! valuesMatch(m_name, parameter.getName())) {
return false;
}
if (! valuesMatch(m_optional, parameter.getOptional())) {
return false;
}
return valuesMatch(m_queryParameter, parameter.getQueryParameter());
}
return false;
}
@Override
public int hashCode() {
int result = m_optional != null ? m_optional.hashCode() : 0;
result = 31 * result + (m_jdbcType != null ? m_jdbcType.hashCode() : 0);
result = 31 * result + (m_type != null ? m_type.hashCode() : 0);
result = 31 * result + (m_direction != null ? m_direction.hashCode() : 0);
result = 31 * result + (m_mode != null ? m_mode.hashCode() : 0);
result = 31 * result + (m_jdbcTypeName != null ? m_jdbcTypeName.hashCode() : 0);
result = 31 * result + (m_name != null ? m_name.hashCode() : 0);
result = 31 * result + (m_queryParameter != null ? m_queryParameter.hashCode() : 0);
return result;
}
/**
* Returns the OracleArrayTypeMetadata instance for a given class name, or null
* if none exists.
*
* @param javaClassName class name used to look up the OracleArrayTypeMetadata instance
* @return the OracleArrayTypeMetadata instance with javaType matching javaClassName,
* or null if none exists.
*/
protected OracleArrayTypeMetadata getArrayTypeMetadata(String javaClassName) {
for (OracleArrayTypeMetadata aType : getEntityMappings().getOracleArrayTypes()) {
if (aType.getJavaType().equals(javaClassName)) {
return aType;
}
}
return null;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getDirection() {
return m_direction;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public Integer getJdbcType() {
return m_jdbcType;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getJdbcTypeName() {
return m_jdbcTypeName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getMode() {
return m_mode;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getName() {
return m_name;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public Boolean getOptional() {
return m_optional;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getQueryParameter() {
return m_queryParameter;
}
/**
* INTERNAL:
*/
public MetadataClass getType() {
return m_type;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getTypeName() {
return m_typeName;
}
/**
* INTERNAL:
*/
protected boolean hasJdbcType() {
return m_jdbcType != null && ! m_jdbcType.equals(-1);
}
/**
* INTERNAL:
*/
protected boolean hasJdbcTypeName() {
return m_jdbcTypeName != null && ! m_jdbcTypeName.equals("");
}
/**
* INTERNAL:
*/
protected boolean hasType() {
return !m_type.isVoid();
}
/**
* INTERNAL:
*/
protected boolean hasTypeName() {
return m_typeName != null && ! m_typeName.equals("");
}
/**
* INTERNAL:
*/
@Override
public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) {
super.initXMLObject(accessibleObject, entityMappings);
m_type = initXMLClassName(m_typeName);
}
/**
* INTERNAL:
*/
public boolean isOutParameter() {
String parameterMode = (m_direction == null) ? m_mode : m_direction;
return parameterMode != null && ( parameterMode.equals(JPA_PARAMETER_OUT)
|| parameterMode.equals(JPA_PARAMETER_INOUT)
|| parameterMode.equals(JPA_PARAMETER_REF_CURSOR)
|| parameterMode.equals(Direction.OUT_CURSOR.name()));
}
/**
* INTERNAL:
*/
private boolean process(StoredProcedureCall call, int index) {
boolean shouldCallByIndex = false;
// Process the procedure parameter name, defaults to the argument field name.
if (m_name == null || m_name.equals("")) {
if (m_queryParameter == null || m_queryParameter.equals("")) {
// JPA 2.1 make the query parameter positional
shouldCallByIndex = true;
m_queryParameter = String.valueOf(index);
} else {
// EclipseLink support, a query parameter is required to be specified.
// TODO: Log a message when defaulting.
m_name = m_queryParameter;
}
}
// There is no such thing as queryParameter in JPA's version.
if (m_queryParameter == null || m_queryParameter.equals("")) {
m_queryParameter = m_name;
}
if ((m_optional != null) && m_optional) {
call.addOptionalArgument(m_queryParameter);
}
if (m_mode == null) {
if (m_direction == null) {
// TODO: Log a defaulting message if parameterMode is null.
m_mode = JPA_PARAMETER_IN;
} else {
m_mode = m_direction;
}
}
return shouldCallByIndex;
}
/**
* INTERNAL:
* #Bug 533272 - JPA NamedStoredProcedure call getOutputParameterValue with parameter name cause exception
*/
public void processArgument(StoredProcedureCall call, boolean callByIndex, int index) {
boolean shouldCallByIndex = process(call, index);
if (! callByIndex) {
callByIndex = shouldCallByIndex;
}
if (m_mode.equals(JPA_PARAMETER_IN)) {
if (hasType()) {
if (callByIndex) {
call.addUnamedArgument(m_queryParameter, getJavaClass(m_type));
} else {
if (hasJdbcType() && hasJdbcTypeName()) {
OracleArrayTypeMetadata aType = null;
if (hasTypeName() && (aType = getArrayTypeMetadata(m_typeName)) != null) {
call.addNamedArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type), buildNestedField(aType));
} else {
call.addNamedArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type));
}
} else {
call.addNamedArgument(m_name, m_queryParameter, getJavaClass(m_type));
}
}
} else if (hasJdbcType() && hasJdbcTypeName()) {
if (callByIndex) {
call.addUnamedArgument(m_queryParameter, m_jdbcType, m_jdbcTypeName);
} else {
call.addNamedArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName);
}
} else if (hasJdbcType()) {
if (callByIndex) {
call.addUnamedArgument(m_queryParameter, m_jdbcType);
} else {
call.addNamedArgument(m_name, m_queryParameter, m_jdbcType);
}
} else {
if (callByIndex) {
call.addUnamedArgument(m_queryParameter);
} else {
call.addNamedArgument(m_name, m_queryParameter);
}
}
} else if (m_mode.equals(JPA_PARAMETER_OUT)) {
if (hasType()) {
if (callByIndex) {
call.addUnamedOutputArgument(m_queryParameter, getJavaClass(m_type));
} else {
if (hasJdbcType() && hasJdbcTypeName()) {
OracleArrayTypeMetadata aType = null;
if (hasTypeName() && (aType = getArrayTypeMetadata(m_typeName)) != null) {
call.addNamedOutputArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type), buildNestedField(aType));
} else {
call.addNamedOutputArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type));
}
} else {
call.addNamedOutputArgument(m_name, m_queryParameter, getJavaClass(m_type));
}
}
} else if (hasJdbcType() && hasJdbcTypeName()) {
if (callByIndex) {
call.addUnamedOutputArgument(m_queryParameter, m_jdbcType, m_jdbcTypeName);
} else {
call.addNamedOutputArgument(m_name, m_queryParameter, m_jdbcType, m_jdbcTypeName);
}
} else if (hasJdbcType()) {
if (callByIndex) {
call.addUnamedOutputArgument(m_queryParameter, m_jdbcType);
} else {
call.addNamedOutputArgument(m_name, m_queryParameter, m_jdbcType);
}
} else {
if (callByIndex) {
call.addUnamedOutputArgument(m_queryParameter);
} else {
call.addNamedOutputArgument(m_name, m_queryParameter);
}
}
//set the project level settings on the argument's database fields
setDatabaseFieldSettings((DatabaseField)call.getParameters().get(call.getParameters().size() - 1));
} else if (m_mode.equals(Direction.IN_OUT.name()) || m_mode.equals(JPA_PARAMETER_INOUT)) {
if (hasType()) {
if (callByIndex) {
call.addUnamedInOutputArgument(m_queryParameter, m_queryParameter, getJavaClass(m_type));
} else {
if (hasJdbcType() && hasJdbcTypeName()) {
OracleArrayTypeMetadata aType = null;
if (hasTypeName() && (aType = getArrayTypeMetadata(m_typeName)) != null) {
call.addNamedInOutputArgument(m_name, m_queryParameter, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type), buildNestedField(aType));
} else {
call.addNamedInOutputArgument(m_name, m_queryParameter, m_queryParameter, m_jdbcType, m_jdbcTypeName, getJavaClass(m_type));
}
} else {
call.addNamedInOutputArgument(m_name, m_queryParameter, m_queryParameter, getJavaClass(m_type));
}
}
} else if (hasJdbcType() && hasJdbcTypeName()) {
if (callByIndex) {
call.addUnamedInOutputArgument(m_queryParameter, m_queryParameter, m_jdbcType, m_jdbcTypeName);
} else {
call.addNamedInOutputArgument(m_name, m_queryParameter, m_queryParameter, m_jdbcType, m_jdbcTypeName);
}
} else if (hasJdbcType()) {
if (callByIndex) {
call.addUnamedInOutputArgument(m_queryParameter, m_queryParameter, m_jdbcType);
} else {
call.addNamedInOutputArgument(m_name, m_queryParameter, m_queryParameter, m_jdbcType);
}
} else {
if (callByIndex) {
call.addUnamedInOutputArgument(m_queryParameter);
} else {
call.addNamedInOutputArgument(m_name, m_queryParameter);
}
}
//set the project level settings on the argument's out database field
Object[] array = (Object[])call.getParameters().get(call.getParameters().size() - 1);
if (array[0] == array[1]){
array[1] = ((DatabaseField)array[1]).clone();
}
setDatabaseFieldSettings((DatabaseField) array[1]);
} else if (m_mode.equals(Direction.OUT_CURSOR.name()) || m_mode.equals(JPA_PARAMETER_REF_CURSOR)) {
if (callByIndex) {
call.useUnnamedCursorOutputAsResultSet(index);
} else {
call.useNamedCursorOutputAsResultSet(m_queryParameter);
}
}
}
/**
* INTERNAL:
*/
public void processResult(StoredFunctionCall call, int index) {
process(call, index);
// Process the function parameter
if (hasType()) {
if (hasJdbcType() && hasJdbcTypeName()) {
OracleArrayTypeMetadata aType = null;
if (hasTypeName() && (aType = getArrayTypeMetadata(m_typeName)) != null) {
call.setResult(m_jdbcType, m_jdbcTypeName, getJavaClass(m_type), buildNestedField(aType));
} else {
call.setResult(m_jdbcType, m_jdbcTypeName, getJavaClass(m_type));
}
} else {
call.setResult(m_name, getJavaClass(m_type));
}
} else if (hasJdbcType() && hasJdbcTypeName()) {
call.setResult(m_name, m_jdbcType, m_jdbcTypeName);
} else if (hasJdbcType()) {
call.setResult(m_name, m_jdbcType);
} else {
call.setResult(m_name);
}
}
/**
* INTERNAL:
* set the project level settings on the database fields
*/
protected void setDatabaseFieldSettings(DatabaseField field) {
if (getProject().useDelimitedIdentifier()) {
field.setUseDelimiters(true);
} else if (getProject().getShouldForceFieldNamesToUpperCase() && !field.shouldUseDelimiters()) {
field.useUpperCaseForComparisons(true);
}
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setDirection(String direction) {
m_direction = direction;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setJdbcType(Integer jdbcType) {
m_jdbcType = jdbcType;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setJdbcTypeName(String jdbcTypeName) {
m_jdbcTypeName = jdbcTypeName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setMode(String mode) {
m_mode = mode;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setName(String name) {
m_name = name;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setOptional(Boolean optional) {
m_optional = optional;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setQueryParameter(String queryParameter) {
m_queryParameter = queryParameter;
}
/**
* INTERNAL:
*/
public void setType(MetadataClass type) {
m_type = type;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setTypeName(String typeName) {
m_typeName = typeName;
}
}