persistence@glassfish.java.net

Re: Fixing issue #383

From: Guy Pelletier <guy.pelletier_at_oracle.com>
Date: Wed, 15 Mar 2006 16:08:36 -0500

Approved!

Thanks,
Guy

----- Original Message -----
From: "Marina Vatkina" <Marina.Vatkina_at_Sun.COM>
To: <persistence_at_glassfish.dev.java.net>
Sent: Wednesday, March 15, 2006 4:05 PM
Subject: Re: Fixing issue #383


> OK. Please approve the fix:
>
> Index:
> src/java/oracle/toplink/essentials/internal/annotations/EJBAnnotationsProcessor.java
> ===================================================================
> RCS file:
> /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/annotations/EJBAnnotationsProcessor.java,v
> retrieving revision 1.22
> diff -r1.22 EJBAnnotationsProcessor.java
> 27a28
>> import java.util.Hashtable;
> 1347c1348,1351
> < m_session.getProject().getAliasDescriptors().remove("");
> ---
>> Hashtable aliasDescriptors =
>> m_session.getProject().getAliasDescriptors();
>> if (aliasDescriptors != null) {
>> aliasDescriptors.remove("");
>> }
>
>
> thanks,
> -marina
>
> Tom Ware wrote On 03/15/06 06:25,:
>> I took another look. Guy is correct here. Let's go with option A.
>>
>> Appologies,
>> Tom
>>
>> Guy Pelletier wrote:
>>
>>
>>>Actually I was already looking at this. Go ahead and use option A for
>>>now.
>>>We will plan to take a look at this again after 100% CTS compliance.
>>>
>>>Cheers,
>>>Guy
>>>
>>>----- Original Message -----
>>>From: "Tom Ware" <tom.ware_at_oracle.com>
>>>To: <persistence_at_glassfish.dev.java.net>
>>>Sent: Wednesday, March 15, 2006 8:43 AM
>>>Subject: Re: Fixing issue #383
>>>
>>>
>>>
>>>
>>>
>>>>That sounds fine to me.
>>>>
>>>>-Tom
>>>>
>>>>Craig L Russell wrote:
>>>>
>>>>
>>>>
>>>>
>>>>>Hi Marina,
>>>>>
>>>>>FWIW, I like to use pattern b if it's a one-shot issue (which this
>>>>>sounds
>>>>>like). There's no downside to creating a hashtable once. If you are
>>>>>constantly creating it and testing for an empty hashset, it's easier
>>>>>to
>>>>>have the hashset defined as null and avoid the construction.
>>>>>
>>>>>Craig
>>>>>
>>>>>On Mar 14, 2006, at 6:53 PM, Marina Vatkina wrote:
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>>Tom,
>>>>>>
>>>>>>The NPE is caused by the following code m_session.getProject
>>>>>>().getAliasDescriptors().remove("");
>>>>>>
>>>>>>When there are no entities in the PU, the alias descriptors
>>>>>>hashtable is null because it's never created.
>>>>>>
>>>>>>There are 2 options to fix it:
>>>>>>a) change the line above to
>>>>>>Hashtable d = m_session.getProject().getAliasDescriptors();
>>>>>>if (d != null)
>>>>>>d.remove("");
>>>>>>
>>>>>>b) Change the Project class to create aliasDescriptors in the
>>>>>>constructor.
>>>>>>
>>>>>>If you don't see a problem with ether fix, I can make the change.
>>>>>>
>>>>>>regards,
>>>>>>-marina
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>Craig Russell
>>>>>Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
>>>>>408 276-5638 mailto:Craig.Russell_at_sun.com
>>>>>P.S. A good JDO? O, Gasp!
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>--
>>>>Tom Ware
>>>>Principal Software Engineer
>>>>Oracle Canada Inc.
>>>>
>>>>Direct: (613) 783-4598
>>>>Email: tom.ware_at_oracle.com
>>>>
>>>>
>>>>
>>
>>
>


--------------------------------------------------------------------------------


> /*
> * 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<Class> m_classes;
>
> // Store the named queries when processing O/R annotations.
> private HashSet<DescriptorMetadata> m_entitiesWithQueries;
>
> // Responsible for all sequencing-related processing.
> private SequencingProcessor m_sequencingProcessor;
>
> /**
> * INTERNAL:
> */
> public EJBAnnotationsProcessor(AbstractSession session, ClassLoader
> loader, Collection<Class> 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<DescriptorMetadata>();
> m_entitiesWithQueries = new HashSet<DescriptorMetadata>();
> m_processingContext = EJB_ANNOTATIONS;
> }
>
> /**
> * INTERNAL:
> * Called for XML/Annotation merging.
> */
> public EJBAnnotationsProcessor(AbstractSession session, ClassLoader
> loader, Collection<Class> classes, boolean enableLazyForOneToOne,
> HashMap<Class, MetadataDescriptor> 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<Class> 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<MetadataDefaultListener>) 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<Class> 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<MetadataJoinColumn> sourceKeys = new
> Vector<MetadataJoinColumn>();
> Vector<MetadataJoinColumn> targetKeys = new
> Vector<MetadataJoinColumn>();
>
> // 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<Method> methods = new ArrayList<Method>();
> 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<MetadataJoinColumn>
> processJoinColumns(MetadataAccessor accessor) {
> Vector<MetadataJoinColumn> allJoinColumns = new
> Vector<MetadataJoinColumn>();
>
> 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<DescriptorMetadata>)
> 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<MetadataJoinColumn>
> processPrimaryKeyJoinColumns(Object[] primaryKeyJoinColumns, String
> sourceTableName, String targetTableName, MetadataDescriptor dmd) {
> Vector<MetadataJoinColumn> metadataJoinColumns = new
> Vector<MetadataJoinColumn>();
>
> 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<MetadataJoinColumn>
> processPrimaryKeyJoinColumns(String sourceTableName, String
> targetTableName, Object element, MetadataDescriptor md) {
> Vector<MetadataJoinColumn> allPrimaryKeyJoinColumns = new
> Vector<MetadataJoinColumn>();
> 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<String, String> processQueryHints(QueryHint[]
> queryHints) {
> HashMap<String, String> hm = new HashMap<String, String>();
>
> 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<MetadataAccessor>)
> 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<Class> classes) {
> m_classes = classes;
> }
> }
>