blob: cdcbfb32fda9013c8bf51f3c90df7d7a701ea94d [file] [log] [blame]
/*
* Copyright (c) 1998, 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/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.
*/
@Override
protected ColumnMetadata getColumn(String loggingCtx) {
return m_field == null ? (m_column == null ? super.getColumn(loggingCtx) : m_column) : m_field;
}
/**
* INTERNAL:
*/
@Override
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;
}
/**
* INTERNAL:
* Process a basic accessor.
*/
@Override
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);
}
// 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;
}
}