| /* |
| * 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 05/26/2009-2.0 mobrien - 266912: Add implementation of IdentifiableType |
| // as EntityType inherits here instead of ManagedType as of rev# 4265 |
| // 09/23/2009-2.0 mobrien - 266912: Implement hasSingleIdAttribute() and |
| // all other 6 remaining methods for Id and Version support. |
| // DI 71 - 77 and 56 |
| // http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_74:_20090909:_Implement_IdentifiableType.hasSingleIdAttribute.28.29 |
| // 10/21/2009-2.0 Guy Pelletier |
| // - 290567: mappedbyid support incomplete |
| // 06/14/2010-2.1 mobrien - 314906: getJavaType should return the |
| // collection javaType C in <X,C,V) of <X, List<V>, V> instead off the elementType V. |
| // Because of this we switch to using getBindableJavaType() in getIdType() |
| // 08/06/2010-2.2 mobrien 322018 - reduce protected instance variables to private to enforce encapsulation |
| package org.eclipse.persistence.internal.jpa.metamodel; |
| |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import jakarta.persistence.metamodel.Attribute; |
| import jakarta.persistence.metamodel.Bindable; |
| import jakarta.persistence.metamodel.IdentifiableType; |
| import jakarta.persistence.metamodel.SingularAttribute; |
| import jakarta.persistence.metamodel.Type; |
| |
| import org.eclipse.persistence.descriptors.CMPPolicy; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.localization.ExceptionLocalization; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| |
| /** |
| * <p> |
| * <b>Purpose</b>: Provides the implementation for the Entity 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 IdentifiableType represent entity or |
| * mapped superclass types. |
| * |
| * @see jakarta.persistence.metamodel.EntityType |
| * |
| * @since EclipseLink 1.2 - JPA 2.0 |
| * @param <X> The represented entity or mapped superclass type. |
| * |
| */ |
| public abstract class IdentifiableTypeImpl<X> extends ManagedTypeImpl<X> implements IdentifiableType<X> { |
| |
| /** |
| * The supertype may be an entity or mappedSuperclass.<p> |
| * For top-level inheritance root identifiable types with no superclass - return null (not Object) |
| */ |
| private IdentifiableType<? super X> superType; |
| |
| /** |
| * The collection of SingularAttributes that are Id attributes. |
| */ |
| private Set<SingularAttribute<? super X, ?>> idAttributes; |
| |
| /** |
| * The SingularAttribute if it exists that is a version attribute |
| */ |
| private SingularAttribute<? super X, ?> versionAttribute; |
| |
| protected IdentifiableTypeImpl(MetamodelImpl metamodel, ClassDescriptor descriptor) { |
| super(metamodel, descriptor); |
| /* The superType field cannot be set until all ManagedType instances |
| * have been instantiated for this metamodel. |
| * This is required so that any references between attributes can be resolved. |
| * This occurs later in MetamodelImpl.initialize() |
| * The idAttributes field is computed at the end of MetamodelImpl.initialize() |
| * The versionAttribute is lazy loaded. |
| */ |
| } |
| |
| /** |
| * INTERNAL: |
| * The idAttributes collection is computed at the end of MetamodelImpl.initialize() |
| */ |
| protected void initializeIdAttributes() { |
| // initialize the set of id attributes directly from the mapping |
| idAttributes = new HashSet<SingularAttribute<? super X, ?>>(); |
| for(Attribute attribute : this.getAttributes()) { |
| if(!((AttributeImpl)attribute).isPlural()) { |
| if(((SingularAttribute)attribute).isId()) { |
| idAttributes.add((SingularAttribute)attribute); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return the attribute that corresponds to the id attribute |
| * declared by the entity or mapped superclass. |
| * @param type the type of the represented declared id attribute |
| * @return declared id attribute |
| * @throws IllegalArgumentException if id attribute of the given |
| * type is not declared in the identifiable type or if |
| * the identifiable type has an id class |
| */ |
| @Override |
| public <Y> SingularAttribute<X, Y> getDeclaredId(Class<Y> type) { |
| /** |
| * We throw an IAE in 3 cases |
| * 1) If the type is different from the javaType of the attribute |
| * 2) If the id is not declared on (this) type |
| * 3) If the id is not part of an IdClass (it is an EmbeddedId or just an Id) |
| * 4) If the id does not exist on the hierarchy - never happens |
| */ |
| // No need to check if an id exists - on an IdentifiableType - there is always at least one |
| // This call will throw an IAE for 1) and 3) |
| SingularAttribute<? super X, Y> anId = this.getId(type); |
| // return the id only if it is declared on this IdentifableType |
| // We know that the attribute exists - so the an IAE will be thrown for 2) for us |
| return (SingularAttribute<X, Y>)getDeclaredAttribute(anId.getName(), true); |
| } |
| |
| /** |
| * Return the attribute that corresponds to the version |
| * attribute declared by the entity or mapped superclass. |
| * @param type the type of the represented declared version |
| * attribute |
| * @return declared version attribute |
| * @throws IllegalArgumentException if version attribute of the |
| * type is not declared in the identifiable type |
| */ |
| @Override |
| public <Y> SingularAttribute<X, Y> getDeclaredVersion(Class<Y> type) { |
| /** |
| * We throw an IAE in 3 cases |
| * 1) If the type is different from the javaType of the attribute |
| * 2) If the version is not declared on (this) type |
| * 3) If the version does not exist on the hierarchy |
| */ |
| // This call will throw an IAE for 1) and 3) |
| SingularAttribute<? super X, Y> aVersion = this.getVersion(type); |
| // return the version only if it is declared on this IdentifableType |
| // We know that the attribute exists - so the an IAE will be thrown for 2) for us |
| return (SingularAttribute<X, Y>)getDeclaredAttribute(aVersion.getName(), true); |
| } |
| |
| /** |
| * Return the attributes corresponding to the id class of the |
| * identifiable type. |
| * @return id attributes |
| * @throws IllegalArgumentException if the identifiable type |
| * does not have an id class |
| */ |
| @Override |
| public Set<SingularAttribute<? super X, ?>> getIdClassAttributes() { |
| // Get the list of IdClass attributes previously stored on the core project during metadata processing |
| List<String> idClassNamesList = getMetamodel().getProject().getMetamodelIdClassMap() |
| .get(getJavaType().getCanonicalName()); |
| // Check for IdClass existence |
| if(null != idClassNamesList) { |
| // All Id attributes are part of an IdClass |
| return this.idAttributes; |
| } else { |
| // No IdClass attributes found - the IdentifiableType may still have a single @Id or an @EmbeddedId in this case |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_identifiable_type_has_no_idclass_attribute", |
| new Object[] {this})); |
| } |
| } |
| |
| /** |
| * Return the attribute that corresponds to the id attribute of |
| * the entity or mapped superclass. |
| * @param type the type of the represented id attribute |
| * @return id attribute |
| * @throws IllegalArgumentException if id attribute of the given |
| * type is not present in the identifiable type or if |
| * the identifiable type has an id class |
| */ |
| @Override |
| public <Y> SingularAttribute<? super X, Y> getId(Class<Y> type) { |
| // We assume that there is at most a single EmbeddedId |
| SingularAttribute<? super X, Y> idAttribute = null; |
| if(!hasSingleIdAttribute()) { |
| // Id is part of an IdClass |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_identifiable_id_attribute_is_incorrect_idclass", |
| new Object[] { this })); |
| } else { |
| // verify single id attribute type |
| for(SingularAttribute<? super X, ?> anAttribute : idAttributes) { |
| // Verify type is correct - relax restriction on null and Object.class (from same classLoader) |
| if(null == type || Object.class == type || |
| type.getCanonicalName().equals(anAttribute.getJavaType().getCanonicalName())) { |
| idAttribute = (SingularAttribute<? super X, Y>) anAttribute; |
| } else { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_identifiable_id_attribute_type_incorrect", |
| new Object[] { anAttribute, this, type, anAttribute.getJavaType() })); |
| } |
| } |
| } |
| return idAttribute; |
| } |
| |
| /** |
| * Return the type that represents the type of the id. |
| * @return type of id |
| */ |
| @Override |
| public Type<?> getIdType() { |
| // NOTE: This code is another good reason to abstract out a PKPolicy on the descriptor |
| CMPPolicy cmpPolicy = getDescriptor().getCMPPolicy(); |
| if (null == cmpPolicy) { |
| // Composite key support (IE: @EmbeddedId) |
| List<DatabaseMapping> pkMappings = getDescriptor().getObjectBuilder().getPrimaryKeyMappings(); |
| // Check the primaryKeyFields on the descriptor - for MappedSuperclass pseudo-Descriptors |
| if(pkMappings.isEmpty()) { |
| // Search the mappings for Id mappings |
| for(DatabaseMapping aMapping : getDescriptor().getMappings()) { |
| if(aMapping.isJPAId()) { |
| // get the attribute Id (declared or not) |
| Attribute<X, ?> anAttribute = this.getAttribute(aMapping.getAttributeName()); |
| if(anAttribute != null) { |
| return this.getMetamodel().getType(((Bindable)anAttribute).getBindableJavaType()); // all Attributes are Bindable |
| } |
| } |
| } |
| } |
| |
| if (pkMappings.size() == 1) { |
| Class aClass = pkMappings.get(0).getAttributeClassification(); // null for OneToOneMapping |
| // lookup class in our types map |
| return this.getMetamodel().getType(aClass); |
| } |
| } |
| |
| // Single Key support using any Java class - built in or user defined |
| // There already is an instance of the PKclass on the policy |
| if (cmpPolicy != null && cmpPolicy.isCMP3Policy()) { |
| // BasicType, EntityType or IdentifiableType are handled here, lookup the class in the types map and create a wrapper if it does not exist yet |
| return this.getMetamodel().getType(cmpPolicy.getPKClass()); |
| } |
| // Non-specification mandated exception |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_incompatible_persistence_config_for_getIdType", |
| new Object[] { this })); |
| } |
| |
| /** |
| * Return the identifiable type that corresponds to the most |
| * specific mapped superclass or entity extended by the entity |
| * or mapped superclass. |
| * @return supertype of identifiable type or null if no such supertype |
| */ |
| @Override |
| public IdentifiableType<? super X> getSupertype() { |
| return this.superType; |
| } |
| |
| /** |
| * Return the attribute that corresponds to the version |
| * attribute of the entity or mapped superclass. |
| * @param type the type of the represented version attribute |
| * @return version attribute |
| * @throws IllegalArgumentException if version attribute of the |
| * given type is not present in the identifiable type |
| */ |
| @Override |
| public <Y> SingularAttribute<? super X, Y> getVersion(Class<Y> type) { |
| // Lazy load the version attribute if it exists |
| if(null == getVersion()) { |
| // No version exists - throw IAE |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_identifiable_no_version_attribute_present", |
| new Object[] { this })); |
| } else { |
| // Verify the type (Note: ClassLoaders do not have to be the same) |
| // Relax restriction on null and Object.class (from same classLoader) |
| if(null == type || Object.class == type || |
| type.getCanonicalName().equals(versionAttribute.getJavaType().getCanonicalName())) { |
| return (SingularAttribute<? super X, Y>)versionAttribute; |
| } else { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage( |
| "metamodel_identifiable_version_attribute_type_incorrect", |
| new Object[] { versionAttribute, this, type, versionAttribute.getJavaType()})); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the version attribute on this type. |
| * If no version attribute exists - return null. |
| */ |
| private <Y> SingularAttribute<? super X, ?> getVersion() { |
| if(hasVersionAttribute()) { |
| return versionAttribute; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Whether or not the identifiable type has an id attribute. |
| * Returns true for a simple id or embedded id; returns false |
| * for an idclass. |
| * @return boolean indicating whether or not the identifiable |
| * type has a single id attribute |
| */ |
| @Override |
| public boolean hasSingleIdAttribute() { |
| // The following section will return false for any multiple EmbeddableId as well as multiple Ids as part of an IdClass |
| // Note: there will always be at least 1 Id for an IdentifiableType |
| |
| /** |
| * Since we are in IdentifiableType which involves only Entities and MappedSuperclasses, |
| * we are safe to assume that there will always be an Id of some sort - we are not in |
| * Basic, Embeddable or transient types. |
| * Check the core API project for any IdClass matching this IdentifiableType |
| * after we check directly on the descriptor for an Id. |
| * References: SubQueryImpl.select() |
| */ |
| |
| // Note: this function will return false only if an IdClass is present |
| List<DatabaseField> pkFields = this.getDescriptor().getPrimaryKeyFields(); |
| // return false for no Id field types |
| if(pkFields.isEmpty()) { |
| return false; |
| } else { |
| // Optional: Verify the mapping on the each field and whether it is an IdClass |
| Class pkClass = null; |
| if(this.getDescriptor().hasCMPPolicy()) { |
| pkClass = this.getDescriptor().getCMPPolicy().getPKClass(); |
| if(null == pkClass) { |
| return false; |
| } |
| } else { |
| // MappedSuperclass descriptors do not have a CMP policy yet because the are not initialized |
| return pkFields.size() < 2; |
| } |
| |
| // 288792: Search the values of the list of IdClass attribute names stored on the project |
| for (List<String> idClassNamesList : getMetamodel().getProject().getMetamodelIdClassMap().values()) { |
| for(String idClassName : idClassNamesList) { |
| if(idClassName.equals(pkClass.getCanonicalName())) { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Whether or not the identifiable type has a version attribute. |
| * @return boolean indicating whether or not the identifiable |
| * type has a version attribute |
| */ |
| @Override |
| public boolean hasVersionAttribute() { |
| // The versionAttribute is lazy loaded |
| if(null != versionAttribute) { |
| return true; |
| } else { |
| for(Attribute attribute : this.getAttributes()) { |
| if(!((AttributeImpl)attribute).isPlural() && ((SingularAttribute)attribute).isVersion()) { |
| versionAttribute = (SingularAttribute)attribute; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return whether this type is identifiable. |
| * This would be EntityType and MappedSuperclassType |
| */ |
| @Override |
| protected boolean isIdentifiableType() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the superType for this IdentifiableType - only after all ManagedTypes |
| * have been instantiated for this Metamodel.<p> |
| * Top-level identifiable types have their supertype set to null. |
| * |
| * @param superType - the entity or mappedSuperclass superType |
| */ |
| protected void setSupertype(IdentifiableType<? super X> superType) { |
| // See design issue #42 - we return null for top-level types (with no superclass) as well as unset supertypes |
| // http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_42:_20090709:_IdentifiableType.supertype_-_what_do_top-level_types_set_it_to |
| this.superType = superType; |
| } |
| } |