| /* |
| * 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; |
| } |
| } |