/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * glassfish/bootstrap/legal/CDDLv1.0.txt or * https://glassfish.dev.java.net/public/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, * add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your * own identifying information: Portions Copyright [yyyy] * [name of copyright owner] */ // Copyright (c) 1998, 2006, Oracle. All rights reserved. package oracle.toplink.essentials.internal.annotations; import java.util.List; import java.util.Vector; import java.util.HashSet; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.ArrayList; import java.util.Collection; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.AnnotatedElement; import java.lang.annotation.Annotation; import javax.persistence.EnumType; import javax.persistence.FetchType; import javax.persistence.CascadeType; import javax.persistence.FlushModeType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Basic; import javax.persistence.MapKey; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.OrderBy; import javax.persistence.IdClass; import javax.persistence.Temporal; import javax.persistence.PostLoad; import javax.persistence.OneToOne; import javax.persistence.QueryHint; import javax.persistence.Transient; import javax.persistence.ManyToOne; import javax.persistence.PreUpdate; import javax.persistence.OneToMany; import javax.persistence.JoinTable; import javax.persistence.PreRemove; import javax.persistence.PostRemove; import javax.persistence.PrePersist; import javax.persistence.JoinColumn; import javax.persistence.PostUpdate; import javax.persistence.NamedQuery; import javax.persistence.ManyToMany; import javax.persistence.Enumerated; import javax.persistence.PostPersist; import javax.persistence.Inheritance; import javax.persistence.JoinColumns; import javax.persistence.FieldResult; import javax.persistence.NamedQueries; import javax.persistence.ColumnResult; import javax.persistence.EntityResult; import javax.persistence.GeneratedValue; import javax.persistence.TableGenerator; import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTables; import javax.persistence.EntityListeners; import javax.persistence.UniqueConstraint; import javax.persistence.NamedNativeQuery; import javax.persistence.AttributeOverride; import javax.persistence.SequenceGenerator; import javax.persistence.AttributeOverrides; import javax.persistence.NamedNativeQueries; import javax.persistence.DiscriminatorValue; import javax.persistence.DiscriminatorColumn; import javax.persistence.AssociationOverride; import javax.persistence.AssociationOverrides; import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMappings; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumns; import javax.persistence.ExcludeDefaultListeners; import javax.persistence.ExcludeSuperclassListeners; import oracle.toplink.essentials.descriptors.ClassDescriptor; import oracle.toplink.essentials.internal.sessions.AbstractSession; import oracle.toplink.essentials.internal.helper.DatabaseField; import oracle.toplink.essentials.internal.helper.DatabaseTable; import oracle.toplink.essentials.exceptions.ValidationException; import oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataHelper; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataAccessor; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataProcessor; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataJoinColumn; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDefaultListener; import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataEntityListener; import oracle.toplink.essentials.mappings.DatabaseMapping; import oracle.toplink.essentials.mappings.OneToOneMapping; import oracle.toplink.essentials.mappings.ManyToManyMapping; import oracle.toplink.essentials.mappings.CollectionMapping; import oracle.toplink.essentials.mappings.DirectToFieldMapping; import oracle.toplink.essentials.mappings.AggregateObjectMapping; import oracle.toplink.essentials.mappings.ForeignReferenceMapping; import oracle.toplink.essentials.queryframework.EJBQLPlaceHolderQuery; /** * Process the CMP annotations on a set of classes into a given TopLink * session's project. * * @author Guy Pelletier * @since TopLink 10.1.3/EJB 3.0 Preview */ public class EJBAnnotationsProcessor extends MetadataProcessor { private Collection m_classes; // Store the named queries when processing O/R annotations. private HashSet m_entitiesWithQueries; // Responsible for all sequencing-related processing. private SequencingProcessor m_sequencingProcessor; /** * INTERNAL: */ public EJBAnnotationsProcessor(AbstractSession session, ClassLoader loader, Collection classes) { m_loader = loader; m_classes = classes; m_session = session; m_enableLazyForOneToOne = false; m_metadataDescriptors = new HashMap(); m_logger = new EJBAnnotationsLogger(m_session); m_sequencingProcessor = new SequencingProcessor(); m_relatedEntities = new HashSet(); m_entitiesWithQueries = new HashSet(); m_processingContext = EJB_ANNOTATIONS; } /** * INTERNAL: * Called for XML/Annotation merging. */ public EJBAnnotationsProcessor(AbstractSession session, ClassLoader loader, Collection classes, boolean enableLazyForOneToOne, HashMap metadataDescriptors) { this(session, loader, classes); m_enableLazyForOneToOne = enableLazyForOneToOne; // Convert the metadata descriptors (if there are any) to annotations // meta data from xml meta data. if (metadataDescriptors != null && !metadataDescriptors.isEmpty()) { for (MetadataDescriptor mdd : metadataDescriptors.values()) { DescriptorMetadata dmd = new DescriptorMetadata(mdd); m_metadataDescriptors.put(dmd.getJavaClass(), dmd); } } } /** * INTERNAL: * Method to place EntityListener's on the descriptors from the given * session. This call is made from the EntityManagerSetup deploy call. */ public void addEntityListeners(AbstractSession session, Collection entityClasses) { updateClassesInMetadata(); for (Class entityClass: entityClasses) { DescriptorMetadata dmd = (DescriptorMetadata) m_metadataDescriptors.get(entityClass); // We likely have a mapped superclass to which there is no dmd, // so ignore it. if (dmd != null) { // By default use the XML exclude-superclass-listeners flag. dmd.setExcludeSuperclassListeners(dmd.isXmlExcludeSuperclassListenersSet()); // By default use the XML exclude-superclass-listeners flag. dmd.setExcludeDefaultListeners(dmd.isXmlExcludeSuperclassListenersSet()); // Check for default listeners and add them. We add them // regardless if the exclude-default-listeners is set. This // allows the user to change the exlcude flag at runtime and // have the default listeners available to them. for (MetadataDefaultListener defaultListener : (List) dmd.getDefaultListeners()) { // Init the default listener with what we found in XML. defaultListener.initializeCallbackMethods(m_loader); // Process the default listener for annotated call back // methods and add when necessary. processCallbackMethods(MetadataHelper.getMethods(defaultListener.getListenerClass()), defaultListener, dmd); // Add the default listener to the descriptor event manager. dmd.addDefaultEventListener(defaultListener); } // Set the @ExcludeSuperclassListeners flag. Don't overwrite a // 'true' value XML default though. if (AnnotationsHelper.isAnnotationPresent(ExcludeSuperclassListeners.class, dmd)) { dmd.setExcludeSuperclassListeners(true); } // Set the @ExcludeDefaultListeners flag. Don't overwrite a // 'true' value XML default though. if (AnnotationsHelper.isAnnotationPresent(ExcludeDefaultListeners.class, dmd)) { dmd.setExcludeDefaultListeners(true); } // Check for @EntityListeners. if (AnnotationsHelper.isAnnotationPresent(EntityListeners.class, dmd)) { EntityListeners entityListeners = AnnotationsHelper.getAnnotation(EntityListeners.class, dmd); for (Class entityListener : entityListeners.value()) { MetadataEntityListener listener = new MetadataEntityListener(entityListener, entityClass); processCallbackMethods(MetadataHelper.getMethods(entityListener), listener, dmd); dmd.addEntityListenerEventListener(listener); } } // Check for lifecycle callback methods on the entity itself // and its mapped superclasses. AnnotationsEntityClassListener listener = new AnnotationsEntityClassListener(entityClass); // Check the mapped superclasses ... for (Class mappedSuperclass : dmd.getMappedSuperclasses()) { processCallbackMethodsFromMappedSuperclass(mappedSuperclass, listener, dmd); } // Check the entity class ... processCallbackMethods(MetadataHelper.getDeclaredMethods(entityClass), listener, dmd); if (listener.hasCallbackMethods()) { dmd.addEntityEventListener(listener); } } } } /** * INTERNAL: * Method to place NamedQueries and NamedNativeQueries on the given session. * This call is made from the EntityManagerSetup deploy call. */ public void addNamedQueriesToSession(AbstractSession session) { for (DescriptorMetadata dmd : m_entitiesWithQueries) { for (NamedQuery namedQuery : dmd.getNamedQueries()) { processNamedQuery(namedQuery, dmd, session); } for (NamedNativeQuery namedNativeQuery : dmd.getNamedNativeQueries()) { processNamedNativeQuery(namedNativeQuery, dmd, session); } } } /** * INTERNAL: * In the case of the XML processor using a defaulted name during * processing, check for the actual table name in annotations - if one * exists, set it as the primary database table for the related descriptor, * and update all applicable fields accordingly. */ protected void adjustTableOnExistingFields(MetadataDescriptor md) { // create the primary table based on annotations Table table = AnnotationsHelper.getAnnotation(Table.class, md); if (table == null) { // do nothing - use the default table created by the XML Processor return; } // Process the table and add set it as primary. processTable(table.name(), table.catalog(), table.schema(), table.uniqueConstraints(), md); // now we need to update any references to the default table for (Iterator fieldIt = md.getFieldsWithDefaultPrimaryTableSet().iterator(); fieldIt.hasNext(); ) { DatabaseField dbField = (DatabaseField) fieldIt.next(); dbField.setTable((DatabaseTable)md.getDescriptor().getTables().elementAt(0)); } } /** * INTERNAL: * Method to return the name of the join table. */ protected DatabaseTable buildJoinTable(JoinTable joinTable, MetadataAccessor accessor) { DatabaseTable dbJoinTable; if (joinTable != null) { dbJoinTable = buildJoinTable(joinTable.name(), joinTable.catalog(), joinTable.schema(), accessor); // Process the @UniqueConstraints for this table. processUniqueConstraints(joinTable.uniqueConstraints(), dbJoinTable); } else { dbJoinTable = buildJoinTable(accessor); } return dbJoinTable; } /** * INTERNAL: * Return the classes set for processing */ public Collection getClasses() { return m_classes; } /** * INTERNAL: * Return the logger used by this processor. */ public EJBAnnotationsLogger getLogger() { return (EJBAnnotationsLogger) m_logger; } /** * INTERNAL: * Lazily create a metadata/descriptor for any given class. */ public DescriptorMetadata getMetadataDescriptor(Class cls) { DescriptorMetadata dmd = (DescriptorMetadata) m_metadataDescriptors.get(cls); ClassDescriptor descriptorOnProject = MetadataHelper.findDescriptor(m_session.getProject(),cls); if (dmd == null) { // We don't have a dmd, check if there is a descriptor for this // class on the project already. if (descriptorOnProject != null) { // Wrap the existing descriptor into a DescriptorMetadata. dmd = new DescriptorMetadata(descriptorOnProject, cls); } else { // Create a new descriptor metadata and add it to the project. dmd = new DescriptorMetadata(cls); m_session.getProject().addDescriptor(dmd.getDescriptor()); } m_metadataDescriptors.put(cls, dmd); } else if (descriptorOnProject == null) { // WIP - We have a descriptor metadata but the descriptor itself is // not on the project. This should never be true, it can be now but // should be fixed. m_session.getProject().addDescriptor(dmd.getDescriptor()); } return dmd; } /** * INTERNAL: */ protected boolean isTransient(AnnotatedElement annotatedElement, int modifier, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(Transient.class, annotatedElement, dmd)) { if (AnnotationsHelper.getDeclaredAnnotationsCount(annotatedElement, dmd) > 1) { throw ValidationException.mappingAnnotationsAppliedToTransientAttribute(annotatedElement); } return true; } else if (Modifier.isTransient(modifier)) { if (AnnotationsHelper.getDeclaredAnnotationsCount(annotatedElement, dmd) > 0) { throw ValidationException.mappingAnnotationsAppliedToTransientAttribute(annotatedElement); } return true; } return false; } /** * INTERNAL: * Return true is this annotated element is not marked transient, static or * abstract. */ protected boolean isValidPersistenceElement(AnnotatedElement annotatedElement, int modifiers, DescriptorMetadata dmd) { return ! (isTransient(annotatedElement, modifiers, dmd) || Modifier.isStatic(modifiers) || Modifier.isAbstract(modifiers)); } /** * INTERNAL: * Check to see if this is a valid field to process for persistence. It is * valid if it is not static, transient or has a @Transient specified. */ protected boolean isValidPersistenceField(Field field, DescriptorMetadata dmd) { return (isValidPersistenceElement(field, field.getModifiers(), dmd)); } /** * INTERNAL: * Check to see if this is a valid method to process for persistence. It is * valid if it is not static, transient or has a @Transient specified. */ protected boolean isValidPersistenceMethod(Method method, DescriptorMetadata dmd) { // Ignore methods marked transient, static or abstract. if (isValidPersistenceElement(method, method.getModifiers(), dmd)) { // Look for methods that begin with "get" or "is", ignore all others. String methodName = method.getName(); if (MetadataHelper.isValidPersistenceMethodName(methodName)) { // Ignore get methods with parameters. if (method.getParameterTypes().length > 0) { return false; } String setMethodName = MetadataHelper.getSetMethodName(method, dmd.getJavaClass()); if (setMethodName == null) { if (AnnotationsHelper.getDeclaredAnnotationsCount(method, dmd) > 0) { // We decorated the property with annotations, but have // no corresponding setter property. throw ValidationException.noCorrespondingSetterMethodDefined(dmd.getJavaClass(), method); } getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_GET_METHOD, dmd, method); } else { // Store the setMethodName for later retrieval to avoid // figuring it out twice. dmd.addSetMethodName(methodName, setMethodName); return true; } } } return false; } /** * INTERNAL: * Method to init a collection mapping from a @OneToMany. */ protected void populateCollectionMapping(CollectionMapping mapping, Object oneToManyAnnotation, MetadataAccessor accessor) { OneToMany oneToMany = (OneToMany) oneToManyAnnotation; // Process the annotation specifics if there is one (may have been // defaulted). These are our defaults otherwise. boolean usesIndirection = false; Class targetEntity = void.class; CascadeType[] cascade = {}; if (oneToMany != null) { // OneToMany.fetch() usesIndirection = oneToMany.fetch() == FetchType.LAZY; // OneToMany.targetEntity() targetEntity = oneToMany.targetEntity(); // OneToMany.cascade() cascade = oneToMany.cascade(); } populateCollectionMapping(mapping, accessor, usesIndirection, targetEntity, cascade); } /** * INTERNAL: */ protected void preProcessGeneratedValue(Object generatedValue, MetadataAccessor accessor, DatabaseField field, MetadataDescriptor dmd) { m_sequencingProcessor.preProcessGeneratedValue((GeneratedValue) generatedValue, (Accessor) accessor, field, (DescriptorMetadata) dmd); } /** * INTERNAL: */ protected void preProcessSequencing(MetadataAccessor ma) { Accessor accessor = (Accessor) ma; preProcessSequencing(accessor.getAnnotatedElement(), accessor.getMetadataDescriptor()); } /** * INTERNAL: * Preprocess the sequencing annotations. Gather the necessary info. The * real sequencing setup is performed after all the sequencing-related * annotations on all classes are preprocessed. */ protected void preProcessSequencing(AnnotatedElement annotatedElement, DescriptorMetadata dmd) { TableGenerator tableGenerator = AnnotationsHelper.getAnnotation(TableGenerator.class, annotatedElement, dmd); m_sequencingProcessor.preProcessTableGenerator(tableGenerator, annotatedElement); SequenceGenerator sequenceGenerator = AnnotationsHelper.getAnnotation(SequenceGenerator.class, annotatedElement, dmd); m_sequencingProcessor.preProcessSequenceGenerator(sequenceGenerator, annotatedElement); } /** * INTERNAL: * Create mappings from the fields directly. */ protected void processAccessorFields(Class cls, DescriptorMetadata dmd) { for (Field field : MetadataHelper.getFields(cls)) { if (isValidPersistenceField(field, dmd)) { processAccessor(new Accessor(field, this, dmd)); } } } /** * INTERNAL: * Create mappings via the class properties. */ protected void processAccessorMethods(Class cls, DescriptorMetadata dmd) { for (Method method : MetadataHelper.getDeclaredMethods(cls)) { if (isValidPersistenceMethod(method, dmd)) { processAccessor(new Accessor(method, this, dmd)); } } } /** * INTERNAL: * Process the accessors for the given descriptor metadata class. */ protected void processAccessors(MetadataDescriptor dmd) { processAccessors(dmd.getJavaClass(), dmd.usesPropertyAccess(), dmd); } /** * INTERNAL: * Process the accessors for the given descriptor metadata class. */ protected void processAccessors(Class cls, boolean usePropertyAccess, MetadataDescriptor md) { DescriptorMetadata dmd = (DescriptorMetadata) md; // Process the fields or methods on the class. if (usePropertyAccess) { processAccessorMethods(cls, dmd); } else { processAccessorFields(cls, dmd); } } /** * INTERNAL: * Process a @JoinTable. */ protected void processJoinTable(MetadataAccessor accessor, ManyToManyMapping mapping) { JoinTable joinTable = AnnotationsHelper.getAnnotation(JoinTable.class, accessor); // Figure out the join table name and set it on the mapping. DatabaseTable relationTable = buildJoinTable(joinTable, accessor); mapping.setRelationTable(relationTable); String relationTableName = relationTable.getQualifiedName(); // Process the JoinColumns and InverseJoinColumns. Vector sourceKeys = new Vector(); Vector targetKeys = new Vector(); // If the join table is not null, look for join columns. if (joinTable != null) { // Process the JoinColumns - source keys. for (JoinColumn joinColumn : joinTable.joinColumns()) { sourceKeys.add(processJoinColumn(joinColumn, accessor, relationTableName, accessor.getMetadataDescriptor())); } // Process the InverseJoinColumns - target keys. for (JoinColumn inverseJoinColumn : joinTable.inverseJoinColumns()) { targetKeys.add(processJoinColumn(inverseJoinColumn, accessor, relationTableName, accessor.getReferenceMetadataDescriptor())); } } processJoinTable(accessor, mapping, sourceKeys, targetKeys); } /** * INTERNAL: * Process an @AssociationOverride for an Entity (or MappedSuperclass) * that inherits from a MappedSuperclass. * * It will also look for an @AssociationOverride. */ protected void processAssociationOverrides(Class cls, DescriptorMetadata dmd) { // Look for an @AssociationOverrides. AssociationOverrides associationOverrides = AnnotationsHelper.getAnnotation(AssociationOverrides.class, cls, dmd); if (associationOverrides != null) { for (AssociationOverride associationOverride : associationOverrides.value()) { dmd.addAssociationOverride(associationOverride.name(), associationOverride.joinColumns()); } } // Look for an @AssociationOverride. AssociationOverride associationOverride = AnnotationsHelper.getAnnotation(AssociationOverride.class, cls, dmd); if (associationOverride != null) { dmd.addAssociationOverride(associationOverride.name(), associationOverride.joinColumns()); } } /** * INTERNAL: * Process an @AssociationOverrides for an embedded object, that is, an * aggregate object mapping in TopLink. * * It will also look for an @AssociationOverride. */ protected void processAssociationOverrides(MetadataAccessor accessor, AggregateObjectMapping mapping) { // Look for an @AssociationOverrides. AssociationOverrides associationOverrides = AnnotationsHelper.getAnnotation(AssociationOverrides.class, accessor); if (associationOverrides != null) { for (AssociationOverride associationOverride : associationOverrides.value()) { processAssociationOverride(associationOverride, accessor, mapping); } } // Look for an @AssociationOverride. AssociationOverride associationOverride = AnnotationsHelper.getAnnotation(AssociationOverride.class, accessor); if (associationOverride != null) { processAssociationOverride(associationOverride, accessor, mapping); } } /** * INTERNAL: * Process an @AssociationOverride for an embedded object, that is, an * aggregate object mapping in TopLink. * * This functionality is not supported in XML, hence why this method is * defined here instead of on MetadataProcessor. * * Also this functionality is currently optional in the EJB 3.0 spec, but * since TopLink can handle it, it is implemented and assumes the user has * properly configured its use since it will fail silently. */ protected void processAssociationOverride(AssociationOverride associationOverride, MetadataAccessor accessor, AggregateObjectMapping aggregateMapping) { MetadataDescriptor dmd = accessor.getMetadataDescriptor(); MetadataDescriptor aggregateDmd = accessor.getReferenceMetadataDescriptor(); // AssociationOverride.name(), the name of the attribute we want to // override. String name = associationOverride.name(); DatabaseMapping mapping = aggregateDmd.getMappingForAttributeName(name); if (mapping == null) { // WIP - For now fail silently. //throw ValidationException.invalidEmbeddableAttribute(aggregateDmd.getJavaClass(), name, dmd.getJavaClass(), aggregateMapping.getAttributeName()); } if (mapping.isOneToOneMapping()) { int index = 0; for (JoinColumn joinColumn : associationOverride.joinColumns()) { // We can't change the mapping from the aggregate descriptor // so we have to add field name translations. This needs to be // tested since I am not entirely sure if this will acutally // work. // In composite primary key case, how do we association the // foreign keys? Right now we assume the association overrides // are specified in the same order as the original joinColumns, // therefore in the same order the foreign keys were added to // the mapping. DatabaseField fkField = (DatabaseField) ((OneToOneMapping) mapping).getForeignKeyFields().elementAt(index++); aggregateMapping.addFieldNameTranslation(joinColumn.name(), fkField.getName()); } } else { // WIP - For now fail silently. } } /** * INTERNAL: * Process an @AttributeOverride for an Entity (or MappedSuperclass) * that inherits from a MappedSuperclass. * * It will also look for an @AttributeOverride. */ protected void processAttributeOverrides(Class cls, DescriptorMetadata dmd) { // Look for an @AttributeOverrides. AttributeOverrides attributeOverrides = AnnotationsHelper.getAnnotation(AttributeOverrides.class, cls, dmd); if (attributeOverrides != null) { for (AttributeOverride attributeOverride : attributeOverrides.value()) { processAttributeOverride(attributeOverride.name(), attributeOverride.column(), dmd); } } // Look for an @AttributeOverride. AttributeOverride attributeOverride = AnnotationsHelper.getAnnotation(AttributeOverride.class, cls, dmd); if (attributeOverride != null) { processAttributeOverride(attributeOverride.name(), attributeOverride.column(), dmd); } } /** * INTERNAL: * Process an @AttributeOverrides for an embedded object, that is, an * aggregate object mapping in TopLink. * * It will also look for an @AttributeOverride. */ protected void processAttributeOverrides(MetadataAccessor accessor, AggregateObjectMapping mapping) { // Look for an @AttributeOverrides. AttributeOverrides attributeOverrides = AnnotationsHelper.getAnnotation(AttributeOverrides.class, accessor); if (attributeOverrides != null) { for (AttributeOverride attributeOverride : attributeOverrides.value()) { processAttributeOverride(attributeOverride.name(), attributeOverride.column(), accessor, mapping); } } // Look for an @AttributeOverride. AttributeOverride attributeOverride = AnnotationsHelper.getAnnotation(AttributeOverride.class, accessor); if (attributeOverride != null) { processAttributeOverride(attributeOverride.name(), attributeOverride.column(), accessor, mapping); } } /** * INTERNAL: * Process a @Basic. */ protected void processBasic(MetadataAccessor accessor, DirectToFieldMapping mapping) { Basic basic = AnnotationsHelper.getAnnotation(Basic.class, accessor); if (basic != null) { // Basic.fetch() if (basic.fetch() == FetchType.LAZY) { getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_BASIC_FETCH_LAZY, accessor); } // Basic.optional() mapping.setIsOptional(basic.optional()); } } /** * INTERNAL: * Process the cascade type on a mapping. */ protected void processCascadeType(MetadataAccessor accessor, Object[] types, ForeignReferenceMapping mapping) { CascadeType[] cTypes = (CascadeType[]) types; for (CascadeType type : cTypes) { setCascadeType(type.name(), mapping); } // apply the persistence unit default cascade-persist if necessary if (accessor.getMetadataDescriptor().isCascadePersistSet() && !mapping.isCascadePersist()) { setCascadeType(PERSIST, mapping); } } /** * INTERNAL: * Process a @Column into a DatabaseField and return it. Assumes the column * is not null at this point. */ protected DatabaseField processColumn(Column column, AnnotatedElement annotatedElement, String attributeName, DescriptorMetadata dmd) { return processColumn(attributeName, dmd, column.name(), column.columnDefinition(), column.table(), column.unique(), column.nullable(), column.insertable(), column.updatable(), column.length(), column.precision(), column.scale(), annotatedElement); } /** * INTERNAL: * Process a @Column for the given accessor into a DatabaseField. If there * is no @Column, we will default one. */ protected DatabaseField processColumn(MetadataAccessor accessor) { MetadataDescriptor dmd = accessor.getMetadataDescriptor(); String attributeName = accessor.getAttributeName(); AnnotatedElement annotatedElement = (AnnotatedElement) accessor.getAnnotatedElement(); // Process the @Column if there is one othersie return a defaulted one. Column column = AnnotationsHelper.getAnnotation(Column.class, accessor); if (column == null) { return processColumnDefaults(attributeName, dmd, annotatedElement); } else { return processColumn(column, annotatedElement, attributeName, dmd); } } /** * INTERNAL: * This method is called when processing an @AttributeOverride. The column * can not be null at this point. */ protected DatabaseField processColumn(Object column, Object annotatedElement, String attributeName, MetadataDescriptor dmd) { return processColumn((Column) column, (AnnotatedElement) annotatedElement, attributeName, (DescriptorMetadata) dmd); } /** * INTERNAL: * Process a @DiscriminatorColumn (if there is one) to set this classes * indication field name for inheritance. */ protected void processDiscriminatorColumn(DescriptorMetadata dmd) { // Process the disciminator column is there is one, otherwise, default. String name = ""; String discriminatorType = STRING; String columnDefinition = ""; int length = 31; DiscriminatorColumn discriminatorColumn = AnnotationsHelper.getAnnotation(DiscriminatorColumn.class, dmd); if (discriminatorColumn != null) { // DiscriminatorColumn.name(), might default to "" name = discriminatorColumn.name(); discriminatorType = discriminatorColumn.discriminatorType().toString(); // DiscriminatorColumn.columnDefinition() columnDefinition = discriminatorColumn.columnDefinition(); // DiscriminatorColumn.length() length = discriminatorColumn.length(); } processDiscriminatorColumn(dmd, name, columnDefinition, length, discriminatorType); } /** * INTERNAL: * Process a @DiscriminatorValue to set the class indicator on the root * descriptor of the inheritance hierarchy. * If there is no @DiscriminatorValue, the class indicator defaults to * the class name. */ protected void processDiscriminatorValue(MetadataDescriptor md) { DiscriminatorValue discriminatorValue = AnnotationsHelper.getAnnotation(DiscriminatorValue.class, md); String value = (discriminatorValue == null) ? null : discriminatorValue.value(); processDiscriminatorValue(md, value); } /** * INTERNAL: * Process the @MappedSuperclass(es) if there are any. There may be * several MappedSuperclasses for any given Entity. */ protected void processMappedSuperclasses(DescriptorMetadata dmd) { for (Class cls : dmd.getMappedSuperclasses()) { // Process the accessors from the mapped superclass. processAccessors(cls, dmd.usesPropertyAccess(), dmd); // Process the attribute overrides on this MappedSuperclass, if // there are any before processing its parent MappedSuperclass. processAttributeOverrides(cls, dmd); } } /** * INTERNAL: * Process the items of interest on an entity class. The order of * processing is important, care must be taken if changes must be made. * * Classes without an @Entity are ignored if no metadata complete flag * is set. */ protected void processEntityClass(Class cls) { String entityName = ""; DescriptorMetadata dmd = null; // Check for metadata complete flag. if (AnnotationsHelper.shouldIgnoreAnnotations(cls, m_metadataDescriptors)) { dmd = getMetadataDescriptor(cls); } else { // Othwerwise look for an @Entity . Entity entity = AnnotationsHelper.getAnnotation(Entity.class, cls); if (entity != null) { entityName = entity.name(); dmd = getMetadataDescriptor(cls); } } // If we have no descriptor metadata or if it is an aggregate // descriptor, then ignore the processing for this class. if (dmd != null && ! dmd.isAggregate()) { // Set the ignore flags for the annotations that should not be // processed since data is already defined on the descriptor. Our // XML/Annotation merge strategy is XML wins! We assume that any // data in the XML is properly and fully defined. dmd.setIgnoreFlags(); // Process the @Entity. processEntity(entityName, dmd); // Process the @NamedQueries and @NamedQuery. processNamedQueries(dmd); // Process the @NamedNativeQueries and @NamedNativeQuery. processNamedNativeQueries(dmd); // Process the @SqlRessultSetMapping. processSqlResultSetMappings(dmd); // Pre-process the sequencing annotations. preProcessSequencing(cls, dmd); // Process the @Table if there is one. Must be processed before the // calls below. processTable(dmd); // Process an @Inheritance. (only specified on the root) processInheritance(dmd); // Process the @IdClass (pkClass). processIdClass(dmd); // Set our attribute overrides from the entity class before // processing the accessors from the mapped superclass. processAttributeOverrides(cls, dmd); // Set our association overrides from the entity class before // processing the accessors from the mapped superclass. processAssociationOverrides(cls, dmd); // Process the @MappedSuperclass(es). processMappedSuperclasses(dmd); // Process the accessors on this entity now. processAccessors(dmd); // If this descriptor has a composite primary key, check that all // our composite primary key attributes were validated. if (dmd.hasCompositePrimaryKey()) { if (dmd.pkClassWasNotValidated()) { throw ValidationException.invalidCompositePKSpecification(cls, dmd.getPKClassName()); } } else { // Descriptor has a single primary key. Validate an id // attribute was found, unless we are an inheritance subclass. if (! dmd.hasPrimaryKeyFields() && !dmd.isInheritanceSubclass()) { throw ValidationException.noPrimaryKeyAnnotationsFound(cls); } } // Process the @SecondaryTable(s). processSecondaryTables(dmd); } } /** * INTERNAL: * Process an @Enumerated. The method may still be called if no @Enumerated * has been specified but the accessor's reference class is a valid * enumerated type. */ protected void processEnumerated(MetadataAccessor accessor, DirectToFieldMapping mapping) { boolean isOrdinal = true; // default // If we have an @Enumerated annotation get the enumerated type. if (accessor.hasEnumerated()) { Enumerated enumerated = AnnotationsHelper.getAnnotation(Enumerated.class, accessor); isOrdinal = (enumerated.value() == EnumType.ORDINAL); } processEnumerated(accessor, isOrdinal, mapping); } /** * INTERNAL: * Process the array of methods for lifecyle callback events and set them * on the given event listener. */ protected void processCallbackMethods(Method[] methods, MetadataEntityListener listener, DescriptorMetadata dmd) { for (Method method : methods) { processPostLoad(method, listener, dmd); processPostPersist(method, listener, dmd); processPostRemove(method, listener, dmd); processPostUpdate(method, listener, dmd); processPrePersist(method, listener, dmd); processPreRemove(method, listener, dmd); processPreUpdate(method, listener, dmd); } } /** * INTERNAL: * Process lifecyle callback event methods for a mapped superclass. We must * 'convert' the method to the entity class context before adding it to the * listener. */ protected void processCallbackMethodsFromMappedSuperclass(Class mappedSuperclass, MetadataEntityListener listener, DescriptorMetadata dmd) { ArrayList methods = new ArrayList(); Method[] allMethods = MetadataHelper.getMethods(dmd.getJavaClass()); for (Method method : MetadataHelper.getDeclaredMethods(mappedSuperclass)) { methods.add(MetadataHelper.getMethodForName(allMethods, method.getName())); } processCallbackMethods(methods.toArray(new Method[methods.size()]), listener, dmd); } /** * INTERNAL: * Process an @Id if there is one. */ protected void processId(MetadataAccessor accessor, DatabaseField field) { Id id = AnnotationsHelper.getAnnotation(Id.class, accessor); if (id != null) { processId(accessor, field, AnnotationsHelper.getAnnotation(GeneratedValue.class, accessor)); } } /** * INTERNAL: * Process an @IdClass. It is used to specify composite primary keys. * The primary keys will be processed and stored from the PK class so that * they may be validated against the fields or properties of the entity * bean. The access type of a primary key class is determined by the access * type of the entity for which it is the primary key. */ protected void processIdClass(DescriptorMetadata dmd) { IdClass idClass = AnnotationsHelper.getAnnotation(IdClass.class, dmd); if (idClass != null) { // IdClass.value(), our pk class. processIdClass(dmd, idClass.value()); } } /** * INTERNAL: * Process an @Inheritance if there is one. An @Inheritance must be * specified on the entity class that is the root of then entity class * hierarchy. */ protected void processInheritance(DescriptorMetadata dmd) { Inheritance inheritance = AnnotationsHelper.getAnnotation(Inheritance.class, dmd); if (inheritance != null) { if (dmd.ignoreInheritanceAnnotations() || dmd.isInheritanceSubclass()) { // XML/Annotation merging. XML wins, ignore annotations. // Also, ignore an @Inheritance on an entity class that is // already a sub-class within an inheritance hierarchy. getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_INHERITANCE, dmd); } else { // Inheritance.strategy(), store on the descriptor metadata. dmd.setInheritanceStrategy(inheritance.strategy()); // Process the @DiscriminatorColumn. processDiscriminatorColumn(dmd); // Process the @DiscriminatorValue. processDiscriminatorValue(dmd); } } } /** * INTERNAL: * Process a @JoinColumn. Returns a MetadataJoinColumn object. */ protected MetadataJoinColumn processJoinColumn(JoinColumn joinColumn, MetadataAccessor accessor, String targetTable, MetadataDescriptor sourceDmd) { return processJoinColumn( accessor, targetTable, sourceDmd, joinColumn.name(), joinColumn.referencedColumnName(), joinColumn.columnDefinition(), joinColumn.table(), joinColumn.unique(), joinColumn.nullable(), joinColumn.insertable(), joinColumn.updatable()); } /** * INTERNAL: * Process a @JoinColumns or @JoinColumn based on if the descriptor metadata * from the accessor uses a composite primary key or not. * Returns a Vector of MetadataJoinColumn. */ protected Vector processJoinColumns(MetadataAccessor accessor) { Vector allJoinColumns = new Vector(); MetadataDescriptor dmd = accessor.getMetadataDescriptor(); String targetTableName = dmd.getPrimaryTableName(); MetadataDescriptor referenceDmd = accessor.getReferenceMetadataDescriptor(); String sourceTableName = referenceDmd.getPrimaryTableName(); if (referenceDmd.hasCompositePrimaryKey()) { // Composite primary key, look for a @JoinColumns. JoinColumn[] joinColumns; if (dmd.hasAssociationOverrideFor(accessor)) { joinColumns = (JoinColumn[]) dmd.getAssociationOverrideFor(accessor); } else { JoinColumns jcs = AnnotationsHelper.getAnnotation(JoinColumns.class, accessor); if (jcs == null) { throw ValidationException.incompleteJoinColumnsSpecified(accessor.getAnnotatedElement(), accessor.getJavaClass()); } else { joinColumns = jcs.value(); } } // The number of JoinColumn specified should equal the number of primary key fields. if (joinColumns.length != referenceDmd.getPrimaryKeyFields().size()) { throw ValidationException.incompleteJoinColumnsSpecified(accessor.getAnnotatedElement(), accessor.getJavaClass()); } for (JoinColumn joinColumn : joinColumns) { allJoinColumns.add(processJoinColumn(joinColumn, accessor, targetTableName, referenceDmd)); } } else { // Single primary key, look for a @JoinColumn. JoinColumn joinColumn; if (dmd.hasAssociationOverrideFor(accessor)) { joinColumn = ((JoinColumn[]) dmd.getAssociationOverrideFor(accessor))[0]; } else { joinColumn = AnnotationsHelper.getAnnotation(JoinColumn.class, accessor); } if (joinColumn == null) { if (accessor.hasJoinColumns()) { throw ValidationException.excessiveJoinColumnsSpecified(accessor.getAnnotatedElement(), accessor.getJavaClass()); } addJoinColumnDefault(allJoinColumns, sourceTableName, targetTableName, dmd); } else { allJoinColumns.add(processJoinColumn(joinColumn, accessor, targetTableName, referenceDmd)); } } return allJoinColumns; } /** * INTERNAL: * Process a @ManyToMany into a TopLink ManyToMany mapping. The method * assumes there is actually a ManyToMany annotation on the annotated * element. */ protected void processManyToMany(MetadataAccessor accessor) { ManyToMany manyToMany = AnnotationsHelper.getAnnotation(ManyToMany.class, accessor); processManyToMany(accessor, manyToMany.mappedBy(), manyToMany.cascade(), manyToMany.fetch() == FetchType.LAZY, manyToMany.targetEntity()); } /** * INTERNAL: * Process a @ManyToOne into a TopLink OneToOne mapping. The method assumes * there is actually a ManyToOne annotation on the annotated element. * * Note: Target foreign keys are not valid for m-1 mapping */ protected void processManyToOne(MetadataAccessor accessor) { ManyToOne manyToOne = AnnotationsHelper.getAnnotation(ManyToOne.class, accessor); processManyToOne(accessor, manyToOne.cascade(), manyToOne.fetch() == FetchType.LAZY, manyToOne.optional(), manyToOne.targetEntity()); } /** * INTERNAL: * Process a @MapKey for a 1-M or M-M mapping. Will return the map key * method name that should be use, null otherwise. */ protected String processMapKey(MetadataAccessor accessor, CollectionMapping mapping) { MapKey mapKey = AnnotationsHelper.getAnnotation(MapKey.class, accessor); if (mapKey == null) { return null; } return processMapKey(accessor, mapping, mapKey.name()); } /** * INTERNAL: * Process a @NamedNativeQueries. The method will also look for * a @NamedNativeQuery. This method currently only stores the queries if * there are some. The actually query processing isn't done till * addNamedQueriesToSession is called. */ protected void processNamedNativeQueries(DescriptorMetadata dmd) { // Look for a NamedNativeQueries annotation. NamedNativeQueries namedNativeQueries = AnnotationsHelper.getAnnotation(NamedNativeQueries.class, dmd); if (namedNativeQueries != null) { for (NamedNativeQuery namedNativeQuery : namedNativeQueries.value()) { dmd.addNamedNativeQuery(namedNativeQuery); } m_entitiesWithQueries.add(dmd); } // Look for a NamedNativeQuery annotation. NamedNativeQuery namedNativeQuery = AnnotationsHelper.getAnnotation(NamedNativeQuery.class, dmd); if (namedNativeQuery != null) { dmd.addNamedNativeQuery(namedNativeQuery); m_entitiesWithQueries.add(dmd); } } /** * INTERNAL: * Does the actual NamedNativeQuery validation and adds the query to the * session. */ protected void processNamedNativeQuery(NamedNativeQuery namedNativeQuery, DescriptorMetadata dmd, AbstractSession session) { // NamedNativeQuery.name() String name = namedNativeQuery.name(); if (session.getQuery(name) != null) { // XML/Annotation merging. XML wins, ignore annotations. getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_QUERY, dmd, name); } else { // NamedNativeQuery.query(), contains the sql string. String queryString = namedNativeQuery.query(); // NamedNativeQuery.hints() HashMap hints = processQueryHints(namedNativeQuery.hints()); // NamedNativeQuery.resultClass() Class resultClass = namedNativeQuery.resultClass(); if (resultClass != void.class) { resultClass = MetadataHelper.getClassForName(resultClass.getName(), m_loader); session.addQuery(name, EJBQueryImpl.buildSQLDatabaseQuery(resultClass, queryString, hints)); return; } // NamedNativeQuery.resultSetMapping(), name of SQLResultSetMapping String resultSetMapping = namedNativeQuery.resultSetMapping(); if (! resultSetMapping.equals("")) { session.addQuery(name, EJBQueryImpl.buildSQLDatabaseQuery(resultSetMapping, queryString, hints)); return; } // Neither a resultClass or resultSetMapping is specified so place in a temp query on the session session.addQuery(name, EJBQueryImpl.buildSQLDatabaseQuery( queryString, hints)); } } /** * INTERNAL: * Process a @NamedQueries. The method will also look for a @NamedQuery. * This method currently only stores the queries if there are some. The * actually query processing isn't done till addNamedQueriesToSession is * called. */ protected void processNamedQueries(DescriptorMetadata dmd) { // Look for a NamedQueries annotation. NamedQueries namedQueries = AnnotationsHelper.getAnnotation(NamedQueries.class, dmd); if (namedQueries != null) { for (NamedQuery namedQuery : namedQueries.value()) { dmd.addNamedQuery(namedQuery); } m_entitiesWithQueries.add(dmd); } // Look for a NamedQuery annotation. NamedQuery namedQuery = AnnotationsHelper.getAnnotation(NamedQuery.class, dmd); if (namedQuery != null) { dmd.addNamedQuery(namedQuery); m_entitiesWithQueries.add(dmd); } } /** * INTERNAL: * Does the actual NamedQuery validation and adds the query to the session. */ protected void processNamedQuery(NamedQuery namedQuery, DescriptorMetadata dmd, AbstractSession session) { // NamedQuery.name(), is the name of the query. String name = namedQuery.name(); if (session.getQuery(name) != null) { getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_QUERY, dmd, name); } else { try { // NamedQuery.query(), contains the ejbql string. String queryString = namedQuery.query(); // NamedQuery.hints() HashMap hints = processQueryHints(namedQuery.hints()); // Add the query. session.addEjbqlPlaceHolderQuery(new EJBQLPlaceHolderQuery(name, queryString, hints)); } catch (Exception exception) { throw ValidationException.errorProcessingNamedQueryAnnotation(dmd.getJavaClass(), name, exception); } } } /** * INTERNAL: * Process a @OneToMany into a TopLink OneToMany mapping. If an @JoinTable * is found however, we must create a ManyToMany mapping. */ protected void processOneToMany(MetadataAccessor accessor) { // Extract the annotation, if there is one. We must null check. OneToMany oneToMany = AnnotationsHelper.getAnnotation(OneToMany.class, accessor); String mappedBy = ""; if (oneToMany != null) { mappedBy = oneToMany.mappedBy(); } processOneToMany(accessor, oneToMany, mappedBy); } /** * INTERNAL: * Process a @OneToOne into a TopLink OneToOne mapping. */ protected void processOneToOne(MetadataAccessor accessor) { // Extract the annotation, if there is one. We must null check. OneToOne oneToOne = AnnotationsHelper.getAnnotation(OneToOne.class, accessor); // Process the annotation specifics if there is one (may have been // defaulted). These are our defaults otherwise. String mappedBy = ""; Class targetEntity = void.class; CascadeType[] cascade = {}; boolean optional = true; boolean usesIndirection = false; if (oneToOne != null) { // OneToOne.targetEntity() targetEntity = oneToOne.targetEntity(); // OneToOne.cascade() cascade = oneToOne.cascade(); // OneToOne.fetch() usesIndirection = oneToOne.fetch() == FetchType.LAZY; // OneToOne.optional() optional = oneToOne.optional(); // OneToOne.mappedBy() mappedBy = oneToOne.mappedBy(); } processOneToOne(accessor, mappedBy, targetEntity, cascade, optional, usesIndirection); } /** * INTERNAL: * Process the annotations and fill in the project. Process in 2 steps, * first step to process most metadata except relationship mappings, second * step to go through the classes that have relationships and fill in the * rest. * * Assumes only Entity classes have been set for processing. */ public AbstractSession processORAnnotations() { // Step 1, fill in most of the metadata. for (Class cls: m_classes) { // Process the entity class. processEntityClass(cls); } // Step 1.5, sequencing setup. m_sequencingProcessor.process(m_session.getProject().getLogin()); // Step 2, fill in the relationships. for (DescriptorMetadata dmd: (HashSet) m_relatedEntities) { processRelatedEntity(dmd); } // remove from aliased descriptors the one named "" - it's there under another name Hashtable aliasDescriptors = m_session.getProject().getAliasDescriptors(); if (aliasDescriptors != null) { aliasDescriptors.remove(""); } // Return the modified session and we are done! return m_session; } /** * INTERNAL: * Process the @OrderBy annotation for collection mappings. It specifies * the ordering of the elements of a collection valued association at the * point when the association is retrieved. * * The syntax of the value ordering element is an orderby_list, as follows: * * orderby_list ::= orderby_item [, orderby_item]* * orderby_item ::= property_or_field_name [ASC | DESC] * * When ASC or DESC is not specified, ASC is assumed. * * If the ordering element is not specified, ordering by the primary key * of the associated entity is assumed. * * The property or field name must correspond to that of a persistent * property or field of the associated class. The properties or fields * used in the ordering must correspond to columns for which comparison * operators are supported. */ protected void processOrderBy(MetadataAccessor accessor, CollectionMapping mapping) { OrderBy orderBy = AnnotationsHelper.getAnnotation(OrderBy.class, accessor); if (orderBy != null) { processOrderBy(accessor, mapping, orderBy.value()); } } /** * INTERNAL: * Process a @PostLoad. * * The PostLoad method for an entity is invoked after the entity has been * loaded into the current persistence context from the database or after * the refresh operation has been applied to it. The PostLoad method is * invoked before a query result is returned or accessed or before an * association is traversed. */ protected void processPostLoad(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PostLoad.class, method, dmd)) { listener.setPostCloneMethod(method); listener.setPostRefreshMethod(method); } } /** * INTERNAL: * Process a @PostPersist. * * The PostPersist callback method is invoked for an entity after the * EntityManager persist operations for that entity is executed. The * callback will also be invoked on all entities to which these operations * are cascaded. The PostPersist method will be invoked after the database * insert operation. This may be directly after the persist operation has * been invoked or it may be directly after a flush operation has occurred * or it may be at the end of the transaction. Exceptions thrown by this * callback cause the current transaction to be rolled back. */ protected void processPostPersist(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PostPersist.class, method, dmd)) { listener.setPostInsertMethod(method); } } /** * INTERNAL: * Process a @PostRemove. * * The PostRemove callback method is invoked for an entity after the * EntityManager remove operations for that entity is executed. The * callback will also be invoked on all entities to which these operations * are cascaded. The PostRemove method will be invoked after the database * delete operation. This may be directly after the remove operation has * been invoked or it may be directly after a flush operation has occurred * or it may be at the end of the transaction. Exceptions thrown by this * callback cause the current transaction to be rolled back. */ protected void processPostRemove(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PostRemove.class, method, dmd)) { listener.setPostDeleteMethod(method); } } /** * INTERNAL: * Process a @PostUpdate. * * The PostUpdate callback occurs after the database update operations to * entity data. This may be at the time the entity state is updated or it * may be at the time state is flushed to the database or at the end of the * transaction. */ protected void processPostUpdate(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PostUpdate.class, method, dmd)) { listener.setPostUpdateMethod(method); } } /** * INTERNAL: * Process a @PrePersist. * * The PrePersist callback method is invoked for a given entity before the * EntityManager persist operations for that entity are executed. This * callback will also be invoked on all entities to which these operations * are cascaded. The PrePersist method will always be invoked as part of * the synchronous persist operation. Exceptions thrown by this callback * cause the current transaction to be rolled back. */ protected void processPrePersist(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PrePersist.class, method, dmd)) { listener.setPrePersistMethod(method); } } /** * INTERNAL: * Process a @PreRemove. * * The PreRemove callback method is invoked for a given entity before the * EntityManager remove operations for that entity are executed. This * callback will also be invoked on all entities to which these operations * are cascaded. The PreRemove methods will always be invoked as part of * the synchronous persist and remove operations. Exceptions thrown by this * callback cause the current transaction to be rolled back. */ protected void processPreRemove(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PreRemove.class, method, dmd)) { listener.setPreRemoveMethod(method); } } /** * INTERNAL: * Process a @PreUpdate. * * The PreUpdate callback occurs before database update operations to * entity data. This may be at the time the entity state is updated or it * may be at the time state is flushed to the database or at the end of the * transaction. */ protected void processPreUpdate(Method method, MetadataEntityListener listener, DescriptorMetadata dmd) { if (AnnotationsHelper.isAnnotationPresent(PreUpdate.class, method, dmd)) { listener.setPreUpdateWithChangesMethod(method); } } /** * INTERNAL: */ protected Vector processPrimaryKeyJoinColumns(Object[] primaryKeyJoinColumns, String sourceTableName, String targetTableName, MetadataDescriptor dmd) { Vector metadataJoinColumns = new Vector(); for (PrimaryKeyJoinColumn primaryKeyJoinColumn : (PrimaryKeyJoinColumn[]) primaryKeyJoinColumns) { metadataJoinColumns.add(processPrimaryKeyJoinColumn(primaryKeyJoinColumn.name(), primaryKeyJoinColumn.referencedColumnName(), primaryKeyJoinColumn.columnDefinition(), sourceTableName, targetTableName, dmd)); } return metadataJoinColumns; } /** * INTERNAL: * Process a @PrimaryKeyJoinColumns or @PrimaryKeyJoinColumn based on if * the descriptor metadata uses a composite primary key or not. * Returns a Vector of MetadataJoinColumn. */ protected Vector processPrimaryKeyJoinColumns(String sourceTableName, String targetTableName, Object element, MetadataDescriptor md) { Vector allPrimaryKeyJoinColumns = new Vector(); AnnotatedElement annotatedElement = (AnnotatedElement) element; DescriptorMetadata dmd = (DescriptorMetadata) md; if (dmd.hasCompositePrimaryKey()) { // Look for a PrimaryKeyJoinColumns. PrimaryKeyJoinColumns primaryKeyJoinColumns = AnnotationsHelper.getAnnotation(PrimaryKeyJoinColumns.class, annotatedElement, dmd); // The number of PrimaryKeyJoinColumns should equal the number of primary key fields. if (primaryKeyJoinColumns == null || primaryKeyJoinColumns.value().length != dmd.getPrimaryKeyFields().size()) { throw ValidationException.incompletePrimaryKeyJoinColumnsSpecified(annotatedElement); } for (PrimaryKeyJoinColumn primaryKeyJoinColumn : primaryKeyJoinColumns.value()) { allPrimaryKeyJoinColumns.add(processPrimaryKeyJoinColumn(primaryKeyJoinColumn.name(), primaryKeyJoinColumn.referencedColumnName(), primaryKeyJoinColumn.columnDefinition(), sourceTableName, targetTableName, dmd)); } } else { // Look for a @PrimaryKeyJoinColumn. PrimaryKeyJoinColumn primaryKeyJoinColumn = AnnotationsHelper.getAnnotation(PrimaryKeyJoinColumn.class, annotatedElement, dmd); if (primaryKeyJoinColumn == null) { if (dmd.hasPrimaryKeyJoinColumns()) { throw ValidationException.excessivePrimaryKeyJoinColumnsSpecified(annotatedElement); } addJoinColumnDefault(allPrimaryKeyJoinColumns, sourceTableName, targetTableName, dmd); } else { allPrimaryKeyJoinColumns.add(processPrimaryKeyJoinColumn(primaryKeyJoinColumn.name(), primaryKeyJoinColumn.referencedColumnName(), primaryKeyJoinColumn.columnDefinition(), sourceTableName, targetTableName, dmd)); } } return allPrimaryKeyJoinColumns; } /** * INTERNAL: * Process an array of @QueryHint. */ protected HashMap processQueryHints(QueryHint[] queryHints) { HashMap hm = new HashMap(); for (QueryHint queryHint : queryHints) { hm.put(queryHint.name(), queryHint.value()); } return hm; } /** * INTERNAL: * Process the related entities. That is, mappings and inheritance etc. */ protected void processRelatedEntity(DescriptorMetadata dmd) { // Process an inheritance subclass specifics. if (dmd.isInheritanceSubclass()) { processInheritanceSubclass(dmd); } // Process the relationship accessors. for (MetadataAccessor accessor : (Collection) dmd.getRelationshipAccessors()) { processRelationshipAccessor(accessor); } } /** * INTERNAL: * Process a @SecondaryTable and add it to descriptor. Method assumes that * the class has been processed for a primary table and primary key. * WIP - If the @SecondaryTable does not define the pkJoinColumns(), we * could look for PrimaryKeyJoinColumns on the class itself. This is not * mandatory through. */ protected void processSecondaryTable(SecondaryTable secondaryTable, DescriptorMetadata dmd) { if (dmd.ignoreTableAnnotations()) { // XML/Annotation merging. XML wins, ignore annotations. getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_SECONDARY_TABLE, dmd, secondaryTable.name()); } else { processSecondaryTable( secondaryTable.name(), secondaryTable.catalog(), secondaryTable.schema(), secondaryTable.uniqueConstraints(), secondaryTable.pkJoinColumns(), dmd); } } /** * INTERNAL: * Process a @SecondaryTables. If one isn't found, try a @SecondaryTable. */ protected void processSecondaryTables(DescriptorMetadata dmd) { // Look for a SecondaryTables annotation. SecondaryTables secondaryTables = AnnotationsHelper.getAnnotation(SecondaryTables.class, dmd); if (secondaryTables != null) { for (SecondaryTable secondaryTable : secondaryTables.value()) { processSecondaryTable(secondaryTable, dmd); } } else { // Look for a SecondaryTable annotation SecondaryTable secondaryTable = AnnotationsHelper.getAnnotation(SecondaryTable.class, dmd); if (secondaryTable != null) { processSecondaryTable(secondaryTable, dmd); } } } /** * INTERNAL: * Process a @SqlResultSetMapping. SqlResultSetMappings are stored on the * session. */ protected void processSqlResultSetMapping(SqlResultSetMapping sqlResultSetMapping) { // SqlResultSetMapping.name(), initialize a new SqlResultSetMapping. oracle.toplink.essentials.queryframework.SQLResultSetMapping mapping = new oracle.toplink.essentials.queryframework.SQLResultSetMapping(sqlResultSetMapping.name()); // SqlResultSetMapping.entities() for (EntityResult entityResult : sqlResultSetMapping.entities()) { // EntityResult.entityClass() Class entityClass = entityResult.entityClass(); oracle.toplink.essentials.queryframework.EntityResult eResult = new oracle.toplink.essentials.queryframework.EntityResult(entityClass.getName()); // EntityResult.fields() for (FieldResult fieldResult : entityResult.fields()) { // Use the FieldResult.name() and FieldResult.column() values to // create a new FieldResult and add it to the EntityResult. eResult.addFieldResult(new oracle.toplink.essentials.queryframework.FieldResult(fieldResult.name(), fieldResult.column())); } // EntityResult.discriminatorColumn() eResult.setDiscriminatorColumn(entityResult.discriminatorColumn()); // Add the result to the SqlResultSetMapping. mapping.addResult(eResult); } // SqlResultSetMapping.columns() for (ColumnResult columnResult : sqlResultSetMapping.columns()) { // Use the ColumnResult.name() value to create a new ColumnResult // and add it to the SqlResultSetMapping. mapping.addResult(new oracle.toplink.essentials.queryframework.ColumnResult(columnResult.name())); } m_session.getProject().addSQLResultSetMapping(mapping); } /** * INTERNAL: * Process a @SqlResultSetMappings. */ protected void processSqlResultSetMappings(DescriptorMetadata dmd) { // Look for a SqlResultSetMappings annotation. SqlResultSetMappings sqlResultSetMappings = AnnotationsHelper.getAnnotation(SqlResultSetMappings.class, dmd); if (sqlResultSetMappings != null) { for (SqlResultSetMapping sqlResultSetMapping : sqlResultSetMappings.value()) { processSqlResultSetMapping(sqlResultSetMapping); } } else { // Look for a SqlResultSetMapping annotation. SqlResultSetMapping sqlResultSetMapping = AnnotationsHelper.getAnnotation(SqlResultSetMapping.class, dmd); if (sqlResultSetMapping != null) { processSqlResultSetMapping(sqlResultSetMapping); } } } /** * INTERNAL: * Process a @Table. */ protected void processTable(MetadataDescriptor md) { // check to see if the XML processor used a default primary table // and update any existing database fields accordingly if (md.isDefaultPrimaryTableSet()) { adjustTableOnExistingFields(md); } else { if (md.ignoreTableAnnotations()) { // XML/Annotation merging. XML wins, ignore annotations. getLogger().logWarningMessage(EJBAnnotationsLogger.IGNORE_TABLE, md); } else { Table table = AnnotationsHelper.getAnnotation(Table.class, md); if (table != null) { // Process the table and add set it as primary. processTable(table.name(), table.catalog(), table.schema(), table.uniqueConstraints(), md); } else { processDefaultTable(md); } } } } /** * INTERNAL: * Process a @Temporal. The method may still be called if no @Temporal * has been specified but the accessor's reference class is a valid * temporal type. */ protected void processTemporal(MetadataAccessor accessor, DirectToFieldMapping mapping) { Temporal temporal = AnnotationsHelper.getAnnotation(Temporal.class, accessor); if (temporal == null) { throw ValidationException.noTemporalTypeSpecified(accessor.getAttributeName(), accessor.getJavaClass()); } else { processTemporal(accessor, temporal.value().name(), mapping); } } /** * INTERNAL: * Process a @UniqueConstraint(s) for the given table. */ protected void processUniqueConstraints(Object[] constraints, DatabaseTable table) { for (UniqueConstraint uniqueConstraint : (UniqueConstraint[]) constraints) { // UniqueConstraint.columnNames() String[] columnNames = uniqueConstraint.columnNames(); for (String columnName : columnNames) { table.addUniqueConstraint(new DatabaseField(columnName)); } } } /** * INTERNAL: * Set the classes for processing. */ public void setClasses(Collection classes) { m_classes = classes; } }