blob: be6e9a7b7fc7673e5dc1e93d409d7d9d458e5810 [file] [log] [blame]
/*
* Copyright (c) 2011, 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:
// 05/26/2009-2.0 mobrien - API update
// - 266912: JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
// 06/30/2009-2.0 mobrien - finish JPA Metadata API modifications in support
// of the Metamodel implementation for EclipseLink 2.0 release involving
// Map, ElementCollection and Embeddable types on MappedSuperclass descriptors
// 08/06/2010-2.2 mobrien 322018 - reduce protected instance variables to private to enforce encapsulation
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
package org.eclipse.persistence.internal.jpa.metamodel;
import java.lang.reflect.Field;
import jakarta.persistence.metamodel.Bindable;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import org.eclipse.persistence.internal.descriptors.OptimisticLockingPolicy;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.AggregateMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.mappings.VariableOneToOneMapping;
import org.eclipse.persistence.mappings.structures.ReferenceMapping;
/**
* <p>
* <b>Purpose</b>: Provides the implementation for the SingularAttribute interface
* of the JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
* <p>
* <b>Description</b>:
* Instances of the type SingularAttribute represents persistent
* single-valued properties or fields.
*
* @author Michael O'Brien
* @see jakarta.persistence.metamodel.SingularAttribute
* @since EclipseLink 1.2 - JPA 2.0
*
* @param <X> The type containing the represented attribute
* @param <T> The type of the represented attribute
*
*/
public class SingularAttributeImpl<X, T> extends AttributeImpl<X, T> implements SingularAttribute<X, T> {
/** Item 54: DI 89: explicit UID will avoid performance hit runtime generation of one */
private static final long serialVersionUID = 3928292425281232234L;
/** The Type representing this Entity or Basic type **/
private Type<T> elementType;
/**
* Create an instance of the Attribute
*/
protected SingularAttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping) {
this(managedType, mapping, false);
}
/**
* INTERNAL:
* Create an Attribute instance with a passed in validation flag (usually set to true only during Metamodel initialization)
*/
protected SingularAttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping, boolean validationEnabled) {
super(managedType, mapping);
// Case: Handle primitive or java lang type (non-Entity) targets
Class attributeClass = mapping.getAttributeClassification();
/**
* Case: Handle Entity targets
* Process supported mappings by assigning their elementType.
* For unsupported mappings we default to MetamodelImpl.DEFAULT_ELEMENT_TYPE.
* If attribute is a primitive type (non-null) - we will wrap it in a BasicType automatically in getType below
* The attribute classification is null for non-collection mappings such as embeddable keys.
*/
if (null == attributeClass) {
// We support @OneToOne but not EIS, Reference or VariableOneToOne
// Note: OneToMany, ManyToMany are handled by PluralAttributeImpl
if(mapping instanceof ForeignReferenceMapping) {// handles @ManyToOne
attributeClass = ((ForeignReferenceMapping)mapping).getReferenceClass();
} else if (mapping.isAbstractDirectMapping()) { // Also handles the keys of an EmbeddedId
attributeClass = mapping.getField().getType();
if(null == attributeClass) {
// lookup the attribute on the containing class
attributeClass = managedType.getTypeClassFromAttributeOrMethodLevelAccessor(mapping);
}
} else if (mapping.isAggregateObjectMapping()) { // IE: EmbeddedId
attributeClass = ((AggregateMapping)mapping).getReferenceClass();
} else if (mapping.isVariableOneToOneMapping()) { // interfaces are unsupported in the JPA 2.0 spec for the Metamodel API
if(validationEnabled) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_mapping_type_is_unsupported", mapping, this);
}
// see JUnitCriteriaUnitTestSuite.testSelectPhoneNumberAreaCode() line: 246
// VariableOneToOne mappings are unsupported - default to referenceClass (Interface) anyway
// see interface org.eclipse.persistence.testing.models.jpa.relationships.Distributor
attributeClass = ((VariableOneToOneMapping)mapping).getReferenceClass();
} else if (mapping.isEISMapping() || mapping.isTransformationMapping()) { // unsupported in the JPA 2.0 spec for the Metamodel API
if(validationEnabled) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_mapping_type_is_unsupported", mapping, this);
}
} else if ( mapping.isReferenceMapping()) { // unsupported in the JPA 2.0 spec for the Metamodel API
if(validationEnabled) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_mapping_type_is_unsupported", mapping, this);
}
// Reference mappings are unsupported - default to referenceClass anyway
attributeClass = ((ReferenceMapping)mapping).getReferenceClass();
}
}
// All unsupported mappings such as TransformationMapping
if(null == attributeClass && validationEnabled) {
// TODO: refactor
attributeClass = MetamodelImpl.DEFAULT_ELEMENT_TYPE_FOR_UNSUPPORTED_MAPPINGS;
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_attribute_class_type_is_null", this);
}
elementType = getMetamodel().getType(attributeClass);
}
/**
* Return the Java type of the represented object.
* If the bindable type of the object is <code>PLURAL_ATTRIBUTE</code>,
* the Java element type is returned. If the bindable type is
* <code>SINGULAR_ATTRIBUTE</code> or <code>ENTITY_TYPE</code>,
* the Java type of the
* represented entity or attribute is returned.
* @return Java type
*/
@Override
public Class<T> getBindableJavaType() {
// In SingularAttribute our BindableType is SINGLE_ATTRIBUTE - return the java type of the represented entity
return this.elementType.getJavaType();
}
/**
* Is the attribute an id attribute.
* @return boolean indicating whether or not attribute is an id
*/
@Override
public boolean isId() {
if(this.getManagedTypeImpl().isMappedSuperclass()) {
// The field on the mapping is the same field in the pkFields list on the descriptor
// 288792: We can use the new isJPAId field here
return (this.getDescriptor().getPrimaryKeyFields().contains(this.getMapping().getField()));
} else {
// 288792: Some id mappings will return false for isPrimaryKeyMapping but true for isJPAId
return getMapping().isPrimaryKeyMapping() || getMapping().isJPAId();
}
}
/**
* Can the attribute be null.
* @return boolean indicating whether or not the attribute can
* be null
*/
@Override
public boolean isOptional() {
return getMapping().isOptional();
}
/**
* INTERNAL:
* Return whether the attribute is plural or singular
*/
@Override
public boolean isPlural() {
return false;
}
/**
* Is the attribute a version attribute.
* @return boolean indicating whether or not attribute is
* a version attribute
*/
@Override
public boolean isVersion() {
if (getDescriptor().usesOptimisticLocking() && getMapping().isDirectToFieldMapping()) {
OptimisticLockingPolicy policy = getDescriptor().getOptimisticLockingPolicy();
return policy.getWriteLockField().equals(getMapping().getField());
}
return false;
}
@Override
public Bindable.BindableType getBindableType() {
return Bindable.BindableType.SINGULAR_ATTRIBUTE;
}
/**
* Return the Java type of the represented attribute.
* @return Java type
*/
@Override
public Class<T> getJavaType() {
if(null == elementType) {
Class aJavaType = getMapping().getAttributeClassification();
if(null == aJavaType) {
aJavaType = getMapping().getField().getType();
if(null == aJavaType) {
// lookup the attribute on the containing class
Class containingClass = getMapping().getDescriptor().getJavaClass();
Field aField = null;
try {
aField = containingClass.getDeclaredField(getMapping().getAttributeName());
aJavaType = aField.getType();
return aJavaType;
} catch (NoSuchFieldException nsfe) {
// This exception will be warned about below
if(null == aJavaType) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_attribute_class_type_is_null", this);
return (Class<T>) MetamodelImpl.DEFAULT_ELEMENT_TYPE_FOR_UNSUPPORTED_MAPPINGS;
}
}
}
}
return aJavaType;
} else {
return this.elementType.getJavaType();
}
}
/**
* Return the type that represents the type of the attribute.
* @return type of attribute
*/
@Override
public Type<T> getType() {
return elementType;
}
/**
* Return the String representation of the receiver.
*/
@Override
public String toString() {
StringBuffer aBuffer = new StringBuffer("SingularAttributeImpl[");
aBuffer.append(getType());
aBuffer.append(",");
aBuffer.append(getMapping());
aBuffer.append("]");
return aBuffer.toString();
}
}