blob: a5f0b41afc6d1924e21718ca2701b1815fa53bf4 [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:
// 03/19/2009-2.0 dclarke - initial API start
// 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
// - 266912: JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
// 29/10/2009-2.0 mobrien - m:1 and 1:m relationships require special handling
// in their internal m:m and 1:1 database mapping representations.
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_96:_20091019:_Attribute.getPersistentAttributeType.28.29_treats_ManyToOne_the_same_as_OneToOne
// 16/06/2010-2.2 mobrien - 316991: Attribute.getJavaMember() requires reflective getMethod call
// when only getMethodName is available on accessor for attributes of Embeddable types.
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_95:_20091017:_Attribute.getJavaMember.28.29_returns_null_for_a_BasicType_on_a_MappedSuperclass_because_of_an_uninitialized_accessor
// 09/08/2010-2.2 mobrien - 322166: If attribute is defined on a lower level MappedSuperclass (and not on a superclass)
// - do not attempt a reflective call on a superclass
// - see design issue #25
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_25:_20090616:_Inherited_parameterized_generics_for_Element_Collections_.28Basic.29
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
package org.eclipse.persistence.internal.jpa.metamodel;
import java.io.Serializable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.ManagedType;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredField;
import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredMethod;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ManyToManyMapping;
/**
* <p>
* <b>Purpose</b>: Provides the implementation for the Attribute interface
* of the JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
* <p>
* <b>Description</b>:
* An attribute of a Java type
*
* @see jakarta.persistence.metamodel.Attribute
*
* @since EclipseLink 1.2 - JPA 2.0
* @param <X> The represented type that contains the attribute
* @param <T> The type of the represented attribute
*
*/
public abstract class AttributeImpl<X, T> implements Attribute<X, T>, Serializable {
/** The ManagedType associated with this attribute **/
private ManagedTypeImpl<X> managedType;
/** The databaseMapping associated with this attribute **/
private DatabaseMapping mapping;
/**
* INTERNAL:
*
*/
protected AttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping) {
this.mapping = mapping;
// Cache this Attribute on the mapping - Why??
//this.mapping.setProperty(getClass().getName(), this);
this.managedType = managedType;
}
/**
* Return the managed type representing the type in which
* the attribute was declared.
* @return declaring type
*/
@Override
public ManagedType<X> getDeclaringType() {
return getManagedTypeImpl();
}
/**
* INTERNAL:
* Return the Descriptor associated with this attribute
*/
protected ClassDescriptor getDescriptor() {
return getManagedTypeImpl().getDescriptor();
}
/**
* Return the java.lang.reflect.Member for the represented attribute.
* In the case of property access the get method will be returned
*
* @return corresponding java.lang.reflect.Member
*/
@Override
public Member getJavaMember() {
AttributeAccessor accessor = getMapping().getAttributeAccessor();
if (accessor.isMethodAttributeAccessor()) {
// Method level access here
Method aMethod = ((MethodAttributeAccessor) accessor).getGetMethod();
if(null == aMethod) {
// 316991: If the getMethod is not set - use a reflective call via the getMethodName
String getMethodName = null;
try {
getMethodName = ((MethodAttributeAccessor)mapping.getAttributeAccessor()).getGetMethodName();
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod(
this.getManagedTypeImpl().getJavaType(), getMethodName, null));
} else {
aMethod = PrivilegedAccessHelper.getDeclaredMethod(
this.getManagedTypeImpl().getJavaType(),getMethodName, null);
}
// Exceptions are to be ignored for reflective calls - if the methodName is also null - it will catch here
} catch (PrivilegedActionException pae) {
//pae.printStackTrace();
} catch (NoSuchMethodException nsfe) {
//nsfe.printStackTrace();
}
}
return aMethod;
}
// Field level access here
Member aMember = ((InstanceVariableAttributeAccessor) accessor).getAttributeField();
// For primitive and basic types - we should not return null - the attributeAccessor on the MappedSuperclass is not initialized - see
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_95:_20091017:_Attribute.getJavaMember.28.29_returns_null_for_a_BasicType_on_a_MappedSuperclass_because_of_an_uninitialized_accessor
// MappedSuperclasses need special handling to get their type from an inheriting subclass
// Note: This code does not handle attribute overrides on any entity subclass tree - use descriptor initialization instead
if(null == aMember) {
if(this.getManagedTypeImpl().isMappedSuperclass()) {
// get inheriting subtype member (without handling @override annotations)
AttributeImpl inheritingTypeMember = ((MappedSuperclassTypeImpl)this.getManagedTypeImpl())
.getMemberFromInheritingType(mapping.getAttributeName());
// 322166: If attribute is defined on this current ManagedType (and not on a superclass) - do not attempt a reflective call on a superclass
if(null != inheritingTypeMember) {
// Verify we have an attributeAccessor
aMember = ((InstanceVariableAttributeAccessor)inheritingTypeMember.getMapping()
.getAttributeAccessor()).getAttributeField();
}
}
if(null == aMember) {
// 316991: Handle Embeddable types
// If field level access - perform a getDeclaredField call
// Field level access
// Check declaredFields in the case where we have no getMethod or getMethodName
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
aMember = AccessController.doPrivileged(new PrivilegedGetDeclaredField(
this.getManagedTypeImpl().getJavaType(), mapping.getAttributeName(), false));
} else {
aMember = PrivilegedAccessHelper.getDeclaredField(
this.getManagedTypeImpl().getJavaType(), mapping.getAttributeName(), false);
}
// Exceptions are to be ignored for reflective calls - if the methodName is also null - it will catch here
} catch (PrivilegedActionException pae) {
//pae.printStackTrace();
} catch (NoSuchFieldException nsfe) {
//nsfe.printStackTrace();
}
}
}
// 303063: secondary check for attribute override case - this will show on code coverage
if(null == aMember) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, AbstractSessionLog.METAMODEL, "metamodel_attribute_getmember_is_null", this, this.getManagedTypeImpl(), this.getDescriptor());
}
return aMember;
}
/**
* Return the Java type of the represented attribute.
* @return Java type
*/
@Override
public abstract Class<T> getJavaType();
/**
* INTERNAL:
* Return the managed type representing the type in which the member was
* declared.
*/
public ManagedTypeImpl<X> getManagedTypeImpl() {
return this.managedType;
}
/**
* INTERNAL:
* Return the databaseMapping that represents the type
*/
public DatabaseMapping getMapping() {
return this.mapping;
}
/**
* INTERNAL:
* Return the concrete metamodel that this attribute is associated with.
* @return MetamodelImpl
*/
protected MetamodelImpl getMetamodel() {
return this.managedType.getMetamodel();
}
/**
* Return the name of the attribute.
* @return name
*/
@Override
public String getName() {
return this.getMapping().getAttributeName();
}
/**
* Return the persistent attribute type for the attribute.
* @return persistent attribute type
*/
@Override
public Attribute.PersistentAttributeType getPersistentAttributeType() {
/**
* process the following mappings by referencing the Core API Mapping.
* MANY_TO_ONE (ONE_TO_ONE internally)
* ONE_TO_ONE (May originally be a MANY_TO_ONE)
* BASIC
* EMBEDDED
* MANY_TO_MANY (May originally be a unidirectional ONE_TO_MANY on a mappedSuperclass)
* ONE_TO_MANY (MANY_TO_MANY internally for unidirectional mappings on MappedSuperclasses)
* ELEMENT_COLLECTION
*/
if (mapping.isAbstractDirectMapping()) {
return PersistentAttributeType.BASIC;
}
if (mapping.isAggregateObjectMapping()) {
return PersistentAttributeType.EMBEDDED;
}
if (mapping.isOneToManyMapping()) {
return PersistentAttributeType.ONE_TO_MANY;
}
/**
* EclipseLink internally processes a ONE_TO_MANY on a MappedSuperclass as a MANY_TO_MANY
* because the relationship is unidirectional.
*/
if (mapping.isManyToManyMapping()) {
// Check for a OneToMany on a MappedSuperclass being processed internally as a ManyToMany
if(((ManyToManyMapping)mapping).isDefinedAsOneToManyMapping()) {
return PersistentAttributeType.ONE_TO_MANY;
} else {
// Test coverage required
return PersistentAttributeType.MANY_TO_MANY;
}
}
if (mapping.isManyToOneMapping()) {
return PersistentAttributeType.MANY_TO_ONE;
}
if (mapping.isOneToOneMapping()) {
return PersistentAttributeType.ONE_TO_ONE;
}
// Test coverage required
return PersistentAttributeType.ELEMENT_COLLECTION;
}
/**
* Is the attribute an association.
* @return whether boolean indicating whether attribute
* corresponds to an association
*/
@Override
public boolean isAssociation() {
// If the mapping a relationship to another entity.
return getMapping().isForeignReferenceMapping() && !getMapping().isDirectCollectionMapping()
&& !getMapping().isAggregateCollectionMapping();
}
/**
* Is the attribute collection-valued.
* @return boolean indicating whether attribute is
* collection-valued.<p>
* This will be true for the mappings CollectionMapping, AbstractCompositeCollectionMapping,
* AbstractCompositeDirectCollectionMapping and their subclasses
*
*/
@Override
public boolean isCollection() {
return getMapping().isCollectionMapping();
}
/**
* INTERNAL:
* Return whether the attribute is plural or singular
*/
public abstract boolean isPlural();
}