/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink * 05/16/2008-1.0M8 Guy Pelletier * - 218084: Implement metadata merging functionality between mapping files * 06/20/2008-1.0 Guy Pelletier * - 232975: Failure when attribute type is generic * 08/27/2008-1.1 Guy Pelletier * - 211329: Add sequencing on non-id attribute(s) support to the EclipseLink-ORM.XML Schema * 09/23/2008-1.1 Guy Pelletier * - 241651: JPA 2.0 Access Type support * 01/28/2009-2.0 Guy Pelletier * - 248293: JPA 2.0 Element Collections (part 1) * 02/06/2009-2.0 Guy Pelletier * - 248293: JPA 2.0 Element Collections (part 2) * 03/27/2009-2.0 Guy Pelletier * - 241413: JPA 2.0 Add EclipseLink support for Map type attributes * 04/24/2009-2.0 Guy Pelletier * - 270011: JPA 2.0 MappedById support * 10/21/2009-2.0 Guy Pelletier * - 290567: mappedbyid support incomplete * 12/2/2009-2.1 Guy Pelletier * - 296612: Add current annotation only metadata support of return insert/update to the EclipseLink-ORM.XML Schema * 03/08/2010-2.1 Guy Pelletier * - 303632: Add attribute-type for mapping attributes to EclipseLink-ORM * 04/27/2010-2.1 Guy Pelletier * - 309856: MappedSuperclasses from XML are not being initialized properly * 07/05/2010-2.1.1 Guy Pelletier * - 317708: Exception thrown when using LAZY fetch on VIRTUAL mapping * 03/24/2011-2.3 Guy Pelletier * - 337323: Multi-tenant with shared schema support (part 1) * 07/03/2011-2.3.1 Guy Pelletier * - 348756: m_cascadeOnDelete boolean should be changed to Boolean * 11/28/2012-2.5 Guy Pelletier * - 374688: JPA 2.1 Converter support * 07/16/2013-2.5.1 Guy Pelletier * - 412384: Applying Converter for parameterized basic-type for joda-time's DateTime does not work ******************************************************************************/ package org.eclipse.persistence.internal.jpa.metadata.accessors.mappings; import java.util.Collection; import java.util.Map; import org.eclipse.persistence.annotations.CacheIndex; import org.eclipse.persistence.annotations.Index; import org.eclipse.persistence.annotations.Mutable; import org.eclipse.persistence.annotations.ReturnInsert; import org.eclipse.persistence.annotations.ReturnUpdate; import org.eclipse.persistence.annotations.UuidGenerator; import org.eclipse.persistence.exceptions.ValidationException; import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor; import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor; 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.cache.CacheIndexMetadata; import org.eclipse.persistence.internal.jpa.metadata.columns.ColumnMetadata; import org.eclipse.persistence.internal.jpa.metadata.converters.EnumeratedMetadata; import org.eclipse.persistence.internal.jpa.metadata.converters.LobMetadata; import org.eclipse.persistence.internal.jpa.metadata.mappings.ReturnInsertMetadata; import org.eclipse.persistence.internal.jpa.metadata.sequencing.GeneratedValueMetadata; import org.eclipse.persistence.internal.jpa.metadata.sequencing.SequenceGeneratorMetadata; import org.eclipse.persistence.internal.jpa.metadata.sequencing.TableGeneratorMetadata; import org.eclipse.persistence.internal.jpa.metadata.sequencing.UuidGeneratorMetadata; import org.eclipse.persistence.internal.jpa.metadata.tables.IndexMetadata; import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_BASIC; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_COLUMN; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_FETCH_EAGER; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_GENERATED_VALUE; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_SEQUENCE_GENERATOR; import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_TABLE_GENERATOR; /** * INTERNAL: * A relational accessor. A Basic annotation may or may not be present on the * accessible object. * * Key notes: * - any metadata mapped from XML to this class must be compared in the * equals method. * - any metadata mapped from XML to this class must be handled in the merge * method. (merging is done at the accessor/mapping level) * - any metadata mapped from XML to this class must be initialized in the * initXMLObject method. * - methods should be preserved in alphabetical order. * * @author Guy Pelletier * @since TopLink EJB 3.0 Reference Implementation */ public class BasicAccessor extends DirectAccessor { private Boolean m_mutable; private Boolean m_returnUpdate; private ColumnMetadata m_column; private DatabaseField m_databaseField; private GeneratedValueMetadata m_generatedValue; private ReturnInsertMetadata m_returnInsert; private SequenceGeneratorMetadata m_sequenceGenerator; private TableGeneratorMetadata m_tableGenerator; private UuidGeneratorMetadata m_uuidGenerator; private IndexMetadata m_index; private CacheIndexMetadata m_cacheIndex; /** * INTERNAL: * Used for OX mapping. */ public BasicAccessor() { super("<basic>"); } /** * INTERNAL: */ public BasicAccessor(String xmlElement) { super(xmlElement); } /** * INTERNAL: */ public BasicAccessor(MetadataAnnotation annotation, MetadataAccessibleObject accessibleObject, ClassAccessor classAccessor) { super(annotation, accessibleObject, classAccessor); // Set the basic metadata if one is present. MetadataAnnotation basic = getAnnotation(JPA_BASIC); if (basic != null) { setFetch(basic.getAttributeString("fetch")); setOptional(basic.getAttributeBooleanDefaultTrue("optional")); } // Set the column metadata if one if present. m_column = new ColumnMetadata(getAnnotation(JPA_COLUMN), this); // Set the mutable value if one is present. if (isAnnotationPresent(Mutable.class)) { m_mutable = getAnnotation(Mutable.class).getAttributeBooleanDefaultTrue("value"); } // Set the generated value if one is present. if (isAnnotationPresent(JPA_GENERATED_VALUE)) { m_generatedValue = new GeneratedValueMetadata(getAnnotation(JPA_GENERATED_VALUE), this); } // Set the sequence generator if one is present. if (isAnnotationPresent(JPA_SEQUENCE_GENERATOR)) { m_sequenceGenerator = new SequenceGeneratorMetadata(getAnnotation(JPA_SEQUENCE_GENERATOR), this); } // Set the table generator if one is present. if (isAnnotationPresent(JPA_TABLE_GENERATOR)) { m_tableGenerator = new TableGeneratorMetadata(getAnnotation(JPA_TABLE_GENERATOR), this); } // Set the table generator if one is present. if (isAnnotationPresent(UuidGenerator.class)) { m_uuidGenerator = new UuidGeneratorMetadata(getAnnotation(UuidGenerator.class), this); } // Set the return insert if one is present. if (isAnnotationPresent(ReturnInsert.class)) { m_returnInsert = new ReturnInsertMetadata(getAnnotation(ReturnInsert.class), this); } // Set the return update if one is present. m_returnUpdate = isAnnotationPresent(ReturnUpdate.class); // Set the index annotation if one is present. if (isAnnotationPresent(Index.class)) { m_index = new IndexMetadata(getAnnotation(Index.class), this); } // Set the cache index annotation if one is present. if (isAnnotationPresent(CacheIndex.class)) { m_cacheIndex = new CacheIndexMetadata(getAnnotation(CacheIndex.class), this); } } /** * INTERNAL: */ @Override public boolean equals(Object objectToCompare) { if (super.equals(objectToCompare) && objectToCompare instanceof BasicAccessor) { BasicAccessor basicAccessor = (BasicAccessor) objectToCompare; if (! valuesMatch(m_mutable, basicAccessor.getMutable())) { return false; } if (! valuesMatch(m_returnUpdate, basicAccessor.getReturnUpdate())) { return false; } if (! valuesMatch(m_column, basicAccessor.getColumn())) { return false; } if (! valuesMatch(m_generatedValue, basicAccessor.getGeneratedValue())) { return false; } if (! valuesMatch(m_returnInsert, basicAccessor.getReturnInsert())) { return false; } if (! valuesMatch(m_sequenceGenerator, basicAccessor.getSequenceGenerator())) { return false; } if (! valuesMatch(m_uuidGenerator, basicAccessor.getUuidGenerator())) { return false; } return valuesMatch(m_tableGenerator, basicAccessor.getTableGenerator()); } return false; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (m_mutable != null ? m_mutable.hashCode() : 0); result = 31 * result + (m_returnUpdate != null ? m_returnUpdate.hashCode() : 0); result = 31 * result + (m_column != null ? m_column.hashCode() : 0); result = 31 * result + (m_generatedValue != null ? m_generatedValue.hashCode() : 0); result = 31 * result + (m_returnInsert != null ? m_returnInsert.hashCode() : 0); result = 31 * result + (m_sequenceGenerator != null ? m_sequenceGenerator.hashCode() : 0); result = 31 * result + (m_tableGenerator != null ? m_tableGenerator.hashCode() : 0); result = 31 * result + (m_uuidGenerator != null ? m_uuidGenerator.hashCode() : 0); return result; } /** * INTERNAL: * Used for OX mapping. */ public ColumnMetadata getColumn() { return m_column; } /** * INTERNAL: * Return the column from xml if there is one, otherwise look for an * annotation. */ protected ColumnMetadata getColumn(String loggingCtx) { return m_field == null ? (m_column == null ? super.getColumn(loggingCtx) : m_column) : m_field; } /** * INTERNAL: */ public String getDefaultFetchType() { return JPA_FETCH_EAGER; } /** * INTERNAL: */ protected DatabaseField getDatabaseField() { return m_databaseField; } /** * INTERNAL: * Used for OX mapping. */ public GeneratedValueMetadata getGeneratedValue() { return m_generatedValue; } /** * INTERNAL: * Used for OX mapping. */ public Boolean getMutable() { return m_mutable; } /** * INTERNAL: * Used for OX mapping. */ public ReturnInsertMetadata getReturnInsert() { return m_returnInsert; } /** * INTERNAL: * Used for OX mapping. */ public Boolean getReturnUpdate() { return m_returnUpdate; } /** * INTERNAL: * Used for OX mapping. */ public SequenceGeneratorMetadata getSequenceGenerator() { return m_sequenceGenerator; } /** * INTERNAL: * Used for OX mapping. */ public TableGeneratorMetadata getTableGenerator() { return m_tableGenerator; } /** * INTERNAL: * Used for OX mapping. */ public UuidGeneratorMetadata getUuidGenerator() { return m_uuidGenerator; } /** * INTERNAL: */ @Override public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) { super.initXMLObject(accessibleObject, entityMappings); // Default a column if necessary. if (m_column == null) { m_column = new ColumnMetadata(this); } else { // Initialize single objects. initXMLObject(m_column, accessibleObject); } // Initialize single objects. initXMLObject(m_generatedValue, accessibleObject); initXMLObject(m_returnInsert, accessibleObject); initXMLObject(m_sequenceGenerator, accessibleObject); initXMLObject(m_tableGenerator, accessibleObject); initXMLObject(m_uuidGenerator, accessibleObject); } /** * INTERNAL: * Return true if this accessor represents a basic mapping. */ @Override public boolean isBasic() { return true; } /** * INTERNAL: * Method to return whether a class is a collection or not. */ protected boolean isCollectionClass(MetadataClass cls) { return cls.extendsInterface(Collection.class); } /** * INTERNAL: * Method to return whether a class is a map or not. */ protected boolean isMapClass(MetadataClass cls) { return cls.extendsInterface(Map.class); } /** * INTERNAL: * USed for OX mapping */ public Boolean isReturnUpdate() { return m_returnUpdate != null && m_returnUpdate.booleanValue(); } /** * INTERNAL: * Process a basic accessor. */ public void process() { // Process a DirectToFieldMapping, that is a Basic that could // be used in conjunction with a Lob, Temporal, Enumerated // or inferred to be used with a serialized mapping. AbstractDirectMapping mapping = getOwningDescriptor().getClassDescriptor().newDirectMapping(); setMapping(mapping); // Process the @Column or column element if there is one. // A number of methods depend on this field so it must be // initialized before any further processing can take place. m_databaseField = getDatabaseField(getDescriptor().getPrimaryTable(), MetadataLogger.COLUMN); // To resolve any generic types (or respect an attribute type // specification) we need to set the attribute classification on the // mapping to ensure we do the right conversions. if (hasAttributeType() || getAccessibleObject().isGenericType()) { mapping.setAttributeClassificationName(getReferenceClassName()); } mapping.setField(m_databaseField); mapping.setIsReadOnly(m_databaseField.isReadOnly()); mapping.setAttributeName(getAttributeName()); mapping.setIsOptional(isOptional()); mapping.setIsLazy(usesIndirection()); // Will check for PROPERTY access. setAccessorMethods(mapping); // Process a converter for this mapping. We will look for an eclipselink // convert value first. If none is found then we'll look for a JPA // converter, that is, Convert, Enumerated, Lob and Temporal. With // everything falling into a serialized mapping if no converter // whatsoever is found. processMappingValueConverter(mapping, getConvert(), getConverts(), getReferenceClass(), getReferenceClassWithGenerics()); // Process a mutable setting. if (m_mutable != null) { mapping.setIsMutable(m_mutable.booleanValue()); } // Process the @ReturnInsert and @ReturnUpdate annotations. processReturnInsertAndUpdate(); // Process a generated value setting. processGeneratedValue(); // Add the table generator to the project if one is set. if (m_tableGenerator != null) { getProject().addTableGenerator(m_tableGenerator, getDescriptor().getDefaultCatalog(), getDescriptor().getDefaultSchema()); } // Add the sequence generator to the project if one is set. if (m_sequenceGenerator != null) { getProject().addSequenceGenerator(m_sequenceGenerator, getDescriptor().getDefaultCatalog(), getDescriptor().getDefaultSchema()); } // Add the uuid generator to the project if one is set. if (m_uuidGenerator != null) { getProject().addUuidGenerator(m_uuidGenerator); } // Process the index metadata. processIndex(); processCacheIndex(); } /** * INTERNAL: * Process an Enumerated annotation. The method may still be called if no * Enumerated annotation has been specified but the accessor's reference * class is a valid enumerated type. */ @Override protected void processEnumerated(EnumeratedMetadata enumerated, DatabaseMapping mapping, MetadataClass referenceClass, boolean isForMapKey) { // If the raw class is a collection or map (with generics or not), we // don't want to put a TypeConversionConverter on the mapping. Instead, // we will want a serialized converter. For example, we could have // an EnumSet<Enum> relation type. if (isCollectionClass(referenceClass) || isMapClass(referenceClass)) { processSerialized(mapping, referenceClass, isForMapKey); } else { super.processEnumerated(enumerated, mapping, referenceClass, isForMapKey); } } /** * INTERNAL: * Process the generated value metadata. */ protected void processGeneratedValue() { if (m_generatedValue != null) { // Set the sequence number field on all the owning descriptors. for (MetadataDescriptor owningDescriptor : getOwningDescriptors()) { DatabaseField existingSequenceNumberField = owningDescriptor.getSequenceNumberField(); if (existingSequenceNumberField == null) { owningDescriptor.setSequenceNumberField(m_databaseField); getProject().addGeneratedValue(m_generatedValue, owningDescriptor.getJavaClass()); } else { throw ValidationException.onlyOneGeneratedValueIsAllowed(owningDescriptor.getJavaClass(), existingSequenceNumberField.getQualifiedName(), m_databaseField.getQualifiedName()); } } } } /** * INTERNAL: * Process index information for the given mapping. */ protected void processIndex() { if (m_index != null) { m_index.process(getDescriptor(), m_databaseField.getName()); } } /** * INTERNAL: * Process cache index. */ protected void processCacheIndex() { if (m_cacheIndex != null) { m_cacheIndex.process(getDescriptor(), m_databaseField.getName()); } } /** * INTERNAL: * Process a Lob metadata. The lob must be specified to process and * create a lob type mapping. */ @Override protected void processLob(LobMetadata lob, DatabaseMapping mapping, MetadataClass referenceClass, boolean isForMapKey) { // If the raw class is a collection or map (with generics or not), we // don't want to put a TypeConversionConverter on the mapping. Instead, // we will want a serialized converter. if (isCollectionClass(referenceClass) || isMapClass(referenceClass)) { processSerialized(mapping, referenceClass, getMetadataClass(java.sql.Blob.class), isForMapKey); } else { super.processLob(lob, mapping, referenceClass, isForMapKey); } } /** * INTERNAL: * Process a ReturnInsert annotation. */ @Override protected void processReturnInsert() { if (m_returnInsert != null) { m_returnInsert.process(getDescriptor(), m_databaseField); } } /** * INTERNAL: * Process a return update setting. */ @Override protected void processReturnUpdate() { if (isReturnUpdate()) { getDescriptor().addFieldForUpdate(m_databaseField); } } /** * INTERNAL: * Used for OX mapping. */ public IndexMetadata getIndex() { return m_index; } /** * INTERNAL: * Used for OX mapping. */ public void setIndex(IndexMetadata index) { m_index = index; } /** * INTERNAL: * Used for OX mapping. */ public CacheIndexMetadata getCacheIndex() { return m_cacheIndex; } /** * INTERNAL: * Used for OX mapping. */ public void setCacheIndex(CacheIndexMetadata index) { m_cacheIndex = index; } /** * INTERNAL: * Used for OX mapping. */ public void setColumn(ColumnMetadata column) { m_column = column; } /** * INTERNAL: * Used for OX mapping. */ public void setGeneratedValue(GeneratedValueMetadata value) { m_generatedValue = value; } /** * INTERNAL: * Used for OX mapping. */ public void setMutable(Boolean mutable) { m_mutable = mutable; } /** * INTERNAL: * Used for OX mapping. */ public void setReturnInsert(ReturnInsertMetadata returnInsert) { m_returnInsert = returnInsert; } /** * INTERNAL: * Used for OX mapping. */ public void setReturnUpdate(Boolean returnUpdate) { m_returnUpdate = returnUpdate; } /** * INTERNAL: * Used for OX mapping. */ public void setSequenceGenerator(SequenceGeneratorMetadata sequenceGenerator) { m_sequenceGenerator = sequenceGenerator; } /** * INTERNAL: * Used for OX mapping. */ public void setTableGenerator(TableGeneratorMetadata tableGenerator) { m_tableGenerator = tableGenerator; } /** * INTERNAL: * Used for OX mapping. */ public void setUuidGenerator(UuidGeneratorMetadata uuidGenerator) { m_uuidGenerator = uuidGenerator; } }