Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Http://wiki.eclipse.org/EclipseLink/DesignDocs/JPA XML metadata extensions

Revision as of 11:08, 18 September 2007 by Guy.pelletier.oracle.com (Talk | contribs) (h2. Converters)

WARNING ... WORK IN PROGRESS ...

Design Specification: JPA XML metadata extensions

Document History

Date Author Version Description
2007-09-17 Guy Pelletier Initial Draft

h2. Goals

The goal of this design spec is to expose some features from TopLink that are currently not available through the use of JPA metadata. The features will be exposed through the use of XML configurations and will provide those that have already been done via annotations.

The new xml configurations will be added to the eclipse.orm_1_0.xml file. This file will be a copy of the latest JPA orm.xml file plus the newly created xml element.

The new features exposed by this design spec are by no means the end all of features available through TopLink. It is merely a set that collectively has been agreed upon as most important to expose in this release. The ultimate goal remains to completely replace the existing TopLink deployment project with the configuration of annotations and/or XML.

h2. Package

  • New classes will be added to their respectful metadata directory under the xml package. E.G. XML converter metadata classes should be added under the org.eclipse.persistence.internal.jpa.metadata.converters.xml. This will be outlined with each new xml element discussed in this document.

h2. Converters

Three new converters, @Converter, @TypeConverter and @ObjectTypeConverter were added in addition to the ones already defined by JPA (@Enumerated, @Lob, @Temporal and serialized (serialization is done automatically based on the type of the mapped attribute)). Usage of these converters was is defined by specifying a @Convert. The user will now have the option to add these converters via XML (convert, converter, type-converter, object-type-converter).

The following new xml types for the converter definitions:

<xsd:element name="converter" type="orm:converter" minOccurs="0" maxOccurs="unbounded"/>

<xsd:element name="type-converter" type="orm:type-converter" minOccurs="0" maxOccurs="unbounded"/>

<xsd:element name="object-type-converter" type="orm:object-type-converter" minOccurs="0" maxOccurs="unbounded"/>

Can be added to the following levels:

  • entity-mappings
  • entity
  • mapped-superclass

Whereas, the new convert xml element will be added to the supported mapping types:

  • basic
<xsd:complexType name="basic">
  <xsd:annotation>
    <xsd:documentation>

      @Target({METHOD, FIELD}) @Retention(RUNTIME)
      public @interface Basic {
        FetchType fetch() default EAGER;
	boolean optional() default true;
      }

    </xsd:documentation>
  </xsd:annotation>
  <xsd:sequence>
    <xsd:element name="column" type="orm:column" minOccurs="0"/>
      <xsd:choice>
        <xsd:element name="lob" type="orm:lob" minOccurs="0"/>
        <xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
        <xsd:element name="enumerated" type="orm:enumerated" minOccurs="0"/>
	<xsd:element name="convert" type="orm:convert" minOccurs="0"/>
      </xsd:choice>
  </xsd:sequence>
  <xsd:attribute name="name" type="xsd:string" use="required"/>
  <xsd:attribute name="fetch" type="orm:fetch-type"/>
  <xsd:attribute name="optional" type="xsd:boolean"/>
</xsd:complexType>
  • basic-map (see sub-section below)
  • basic-collection (see sub-section below)

Warning cases:

  • Converter names are global across the persistence unit, therefore, must be unique. The first converter processed will be used, whereas, consecutive converters with the same name will be ignored and a warning message logged.

Exception cases:

  • The name "none" and "serialized" are reserved for converter names. Any converter (converter, type-converter, object-converter) specified with either name will cause an

exception to be thrown.

converter

The converter complex type will be added and is used to specify a custom converter for modification of the data value(s) during the reading and writing of a mapped attribute.

<xsd:complexType name="converter">
  <xsd:annotation>
    <xsd:documentation>

      @Target({TYPE, METHOD, FIELD})
      @Retention(RUNTIME)
      public @interface Converter {
        /**
         * (Required) Name this converter. The name should be unique
         * across the whole persistence unit.
         */
        String name();

        /**
         * (Required) The converter class to be used. This class
         * must implement the TopLink  
         * org.eclipse.persistence.mappings.converters.Converter
         * interface.
         */
        Class converterClass(); 
      }

      </xsd:documentation>
    </xsd:annotation>
    <xsd:attribute name="name" type="xsd:string" use="required"/>
    <xsd:attribute name="converter-class" type="xsd:string"/>
  </xsd:complexType>

Example </b TODO

<b> Design Processing is currently defined in the org.eclipse.persistence.internal.jpa.metadata.converters.MetadataConverter.

The org.eclipse.persistence.internal.jpa.metadata.converters.xml.XMLConverter class should be built and extend the MetadataConverter. Its only responsibility will be to override the metadata that is processed.

type-converter

The type-converter complex type will be added and is used to specify an org.eclipse.persistence.mappings.converters.TypeConversionConverter for modification of the data value(s) during the reading and writing of a mapped attribute

<xsd:complexType name="type-converter">
  <xsd:annotation>
    <xsd:documentation>

      @Target({TYPE, METHOD, FIELD})
      @Retention(RUNTIME)
      public @interface Converter {
        /**
         * (Required) Name this converter. The name should be unique
         * across the whole persistence unit.
         */
         String name();

        /**
         * (Optional) Specify the type stored on the database. The
         * default is inferred from the type of the persistence field 
         * or property.
         */
        Class dataType() default void.class;

        /**
         * (Optional) Specify the type stored on the entity. The
         * default is inferred from the type of the persistent field 
         * or property.
         */
        Class objectType() default void.class;
      }

    </xsd:documentation>
  </xsd:annotation>
  <xsd:attribute name="name" type="xsd:string" use="required"/>
  <xsd:attribute name="converter-class" type="xsd:string"/>
  <xsd:attribute name="data-type" type="xsd:string"/>
  <xsd:attribute name="object-type" type="xsd:string"/>
</xsd:complexType>

Example

Design Processing is currently defined in the org.eclipse.persistence.internal.jpa.metadata.converters.MetadataConverter. (see previous design doc)

The org.eclipse.persistence.internal.jpa.metadata.converters.xml.XMLTypeConverter class should be built and extend the MetadataTypeConverter. Its only responsibility will be to override the metadata that is processed.

object-type-converter

The object-type-converter complex type will be added and is used to specify an org.eclipse.persistence.mappings.converters.ObjectTypeConverter that converts a fixed number of database data value(s) to Java object value(s) during the reading and writing of a mapped attribute.

<xsd:complexType name="object-type-converter">
  <xsd:complexContent>	
    <xsd:extension base=type-converter>
      <xsd:annotation>
        <xsd:documentation>

          @Target({TYPE, METHOD, FIELD})
          @Retention(RUNTIME)
          public @interface ObjectTypeConverter {
            /**
             * (Required) Name this converter. The name should be unique
             * across the whole persistence unit.
             */
            String name();

            /**
             * (Optional) Specify the type stored on the database. The
             * default is inferred from the type of the persistence
             * field or property.
             */
            Class dataType() default void.class;

            /**
             * (Optional) Specify the type stored on the entity. The
             * default is inferred from the type of the persistent 
             * field or property.
             */
            Class objectType() default void.class;

            /**
             * (Required) Specify the conversion values to be used 
             * with the object converter.
             */
            ConversionValue[] conversionValues();

            /**
             * (Optional) Specify a default object value. Used for 
             * legacy data if the data value is missing.
             */
            String defaultObjectValue() default "";
          }
        </xsd:documentation>
      </xsd:annotation>
      <xsd:attribute name="conversion-values" type="orm:conversion-value" minOccurs=0 maxOccurs=unbounded/>
      <xsd:attribute name="default-object-value" type="xsd:string"/>
    </xsd:entension>
  </xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="conversion-value">
  <xsd:annotation>
    <xsd:documentation>

      @Target({})
      @Retention(RUNTIME)
      public @interface ConversionValue {
        /**
         * (Required) Specify the database value.
         */
        String dataValue();

        /**
         * (Required) Specify the object value.
         */
        String objectValue();
      }
    </xsd:documentation>
  </xsd:annotation>
  <xsd:attribute name="data-type" type="xsd:string"/>
  <xsd:attribute name="object-type" type="xsd:string"/>
</xsd:complexType>

Exceptions:

  • If multiple objectValue's are specified for the same dataValue an exception will be thrown.

Example

Design Processing is currently defined in the org.eclipse.persistence.internal.jpa.metadata.converters.MetadataObjectConverter. (see previous design doc)

The org.eclipse.persistence.internal.jpa.metadata.converters.xml.XMLObjectConverter class should be built and extend the MetadataObjectConverter. Its only responsibility will be to override the metadata that is processed.

convert

The convert specifies that a named converter should be used with the corresponding mapped attribute. The convert has the following reserved names:

  • serialized - Will place an org.eclipse.persistence.mappings.converters.SerializedObjectConverter on

the associated mapping.

  • none - Will place no converter on the associated mapping.
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Convert {
    /**
     * (Required) The name of the converter to be used.
     */
    String value() default "none";
}

Warnings:

  • @Convert used with @Lob, @Enumeration, @Temporal will log a warning that those annotations

are being ignored.

Exceptions:

  • A @Convert name that does not match up with a TopLink converter will throw an exception.
  • Example*

{code} @Entity @Table(name="EMPLOYEE") @Converter(

   name="myGenderConverter",
   converterClass=my.gender.converter.class

) public class Employee implements Serializable {

   @Basic
   @Convert("myGenderConverter");
   public String getGender() {
       return gender;
   }
   ...

{code}

  • Internal processing*
  • See individual (@Converter, @TypeConverter, @ObjectTypeConverter) internal processing details.
  • Design*
  • @Convert is processed in DirectAccessor's process() method.

h2. Mapping Annotations

  • [^Accessors.png] design
  • Mapping related annotations will be searched in the following order of precedence. The first one

found is applied and all others are ignored (if defined). That is, there will be no log warning, nor an exception thrown unless otherwise stated.

    1. BasicCollection
    2. BasicMap
    3. EmbeddedId
    4. Embedded
    5. ManyToMany
    6. ManyToOne
    7. OneToMany
    8. OneToOne
    9. Basic (if none of the above are found, it falls into a basic mapping)

h3. @CollectionTable A @CollectionTable is used in conjunction with a @BasicCollection annotation or a @BasicMap annotation. If the @CollectionTable is not defined, one will be defaulted as stated in the Javadoc comments for the annotation.

{code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface CollectionTable {

   /**
    * (Optional) The name of the collection table. If it is not specified, it is defaulted to the
    * concatenation of the following: the name of the source entity; "_" ; the name of the 
    * relationship property or field of the source entity.
    */
   String name() default ""; 
   /**
    * (Optional) The catalog of the table. It defaults to the persistence unit default catalog.
    */
   String catalog() default ""; 
   /**
    * (Optional) The schema of the table. It defaults to the persistence unit default schema.
    */
   String schema() default ""; 
   /**
    * (Optional) Used to specify a primary key column that is used as a foreign key to join to
    * another table. If the source entity uses a composite primary key, a primary key join column 
    * must be specified for each field of the composite primary key. In a single primary key case, 
    * a primary key join column may optionally be specified. Defaulting will apply otherwise as 
    * follows:
    * name, the same name as the primary key column of the primary table of the source entity.
    * referencedColumnName, the same name of primary key column of the primary table of the source 
    * entity.
    */
   PrimaryKeyJoinColumn[] primaryKeyJoinColumns() default {}; 

   /**
    * (Optional) Unique constraints that are to be placed on the table. These are only
    * used if table generation is in effect.
    */
   UniqueConstraint[] uniqueConstraints() default {}; 

} {code}

Warnings:

  • None

Exceptions:

  • If the source entity uses a composite primary key and the primary key join columns are not fully

specified, then an exception will be thrown.

h3. @BasicCollection The @BasicCollection annotation is used to map a oracle.toplink.mappings.DirectCollectionMapping which stores a collection of simple types (String, Number, Date, etc.) into a single table. The table must store the value and a foreign key to the source object. {code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface BasicCollection {

   /**
    * (Optional) Defines whether the value of the field or property should
    * be lazily loaded or must be eagerly fetched. The EAGER strategy is a 
    * requirement on the persistence provider runtime that the value must be 
    * eagerly fetched. The LAZY strategy is a hint to the persistence provider 
    * runtime. If not specified, defaults to LAZY.
    */
   FetchType fetch() default LAZY; 

   /**
    * (Optional) The name of the value column that holds the direct collection 
    * data. Defaults to the property or field name.
    */
   Column valueColumn() default @Column;

} {code}

The @BasicCollection annotation is used in conjunction with a @CollectionTable.

A @BasicCollection can be used in conjunction with @Convert to modify the data value(s) during reading and writing of the collection.

Warnings:

  • A log warning will be issued if the following annotations are found on the same mapped attribute

as a @BasicCollection:

    1. @BasicMap
    2. @EmbeddedId
    3. Embedded
    4. ManyToMany
    5. ManyToOne
    6. @OneTomany
    7. @OneToOne
    8. @Basic

Exceptions:

  • If a @BasicCollection is specified on an attribute of type Map, an exception will be thrown.
  • Example*

{code} @BasicCollection(

   fetch="LAZY",
   valueColumn=@Column(name="DESCRIPTION")

) @CollectionTable(

   name="RESPONS",
   primaryKeyJoinColumns={@PrimaryKeyJoinColumn(name="EMPLOYEE_ID", referencedColumnName="EMP_ID")}

) public CollectiongetResponsibilities() {

   return responsibilities;

} {code}

  • Internal processing*

{code} // Build a DirectCollectionMapping mapping = new DirectCollectionMapping();

// Attribute name is equal to the field or property name. mapping.setAttributeName("responsibilities");

// Process fetch() type mapping.useBasicIndirection(); // LAZY setting mapping.useCollectionClass(java.util.Vector.class); // EAGER setting

// Set the accessor methods if the access for this class is PROPERTY setAccessorMethods(mapping);

// Process the @CollectionTable metadata

// Process the name(), schema() and catalog() into a DatabaseTable and set as the reference table for // this mapping. In this example, table name is "RESPONS" mapping.setReferenceTable(table);

// Process valueColumn() into a DatabaseField and set as the direct field for this mapping. // In this example, the fully qualified field name is "RESPONSE.DESCRIPTION" mapping.setDirectField(directField);

// Process the primaryKeyJoinColumns() to set the target foreign key and the source primary key fields mapping.addReferenceKeyFieldName("RESPONS.EMPLOYEE_ID", "EMPLOYEE.EMP_ID");

// Mapping added to the descriptor. descriptor.addMapping(mapping); {code}

  • Design*
  • See Common design for @BasicCollection and @BasicMap for more information.
  • Build new BasicCollectionAccessor that extends DirectCollectionAccessor. When the time comes,

oracle.toplink.ejb.cmp3.xml.accessors.XMLBasicCollectionAccessor.class will be built that will extend this class and will provide XML overriding and merging.

  • Implement getColumn() on BasicCollectionAccessor to extract column information from within the

@BasicCollection

  • @BasicCollection must be processed in stage two, that is, with relationship mappings since we

require a primary key to be processed before hand.

  • ClassAccessor will build BasicCollectionAccessor.
  • BasicCollectionAccessor must implement process()

h3. @BasicMap The @BasicMap annotation is used to map a oracle.toplink.mappings.BasicMapMapping which stores a collection of key-value pairs. The key and value must be simple types (String, Number, Date, etc.) and stored in a single table along with a foreing key to the source object.

{code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface BasicMap {

   /**
    * (Optional) Defines whether the value of the field or property should
    * be lazily loaded or must be eagerly fetched. The EAGER strategy is a 
    * requirement on the persistence provider runtime that the value must be 
    * eagerly fetched. The LAZY strategy is a hint to the persistence provider 
    * runtime. If not specified, defaults to LAZY.
    */
   FetchType fetch() default LAZY;
   /**
    * (Optional) The name of the data column that holds the direct map key.
    * If the name on te key column is "", the name will default to:
    * the name of the property or field; "_key".
    */
   Column keyColumn();
   /**
    * (Optional) Specify the key converter. Default is equivalent to specifying
    * @Convert("none"), meaning no converter will be added to the direct map key.
    */
   Convert keyConverter() default @Convert;
   /**
    * (Optional) The name of the data column that holds the direct collection data.
    * Defaults to the property or field name.
    */
   Column valueColumn() default @Column;
   /**
    * (Optional) Specify the value converter. Default is equivalent to specifying 
    * @Convert("none"), meaning no converter will be added to the value column mapping.
    */
   Convert valueConverter() default @Convert;

} {code}

The @BasicMap annotation is used in conjunction with a @CollectionTable. See @CollectionTable for more info.

Warnings:

  • A log warning will be issued if the following annotations are found on the same mapped attribute

as a @BasicMap:

    1. @EmbeddedId
    2. @Embedded
    3. @ManyToMany
    4. @ManyToOne
    5. @OneTomany
    6. @OneToOne
    7. @Basic

Exceptions:

  • If a @BasicMap is specified on an attribute of type Collection, an exception will be thrown.
  • Example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @TypeConverter(

   name="Integer2String"
   dataType=Integer.class;

) public class Employee implements Serializable {

   ...
   @BasicMap(
       fetch=LAZY,
       keyColumn=@Column(name="LICENSE"),
       keyConverter=@Convert("licenseConverter"),
       valueColumn=@Column(name="STATUS")),
       valueTypeConverter=@Convert("Integer2String")
   )
   @ObjectConverter({
       name="licenseConverter",
       conversionValues={
         @ConversionValue(dataValue="AL", objectValue="Alcohol License"),
         @ConversionValue(dataValue="FD", objectValue="Food License"),
         @ConversionValue(dataValue="SM", objectValue="Smoking License"),
         @ConversionValue(dataValue="SL", objectValue="Site Licence")
       }
   })
   @CollectionTable(
       name="LICENSE",
       primaryKeyJoinColumns={@PrimaryKeyJoinColumn(name="REST_ID")}
   )
   public Map<String> getLicenses() {
       return licenses;
   }
   ...

{code}

  • Internal processing*

{code} DirectMapMapping mapping = new DirectMapMapping(); mapping.setAttributeName("licenses");

// Process the fetch type mapping.useTransparentMap(); // LAZY mapping.useMapClass(target); // EAGER

// Set the accessor methods if the access for this class is PROPERTY setAccessorMethods(mapping);

// Process table() into a DatabaseTable (fully qualified using any XML defaults) and // set as the reference table for this mapping. In this example, table name is "LICENSE" mapping.setReferenceTable(table);

// Process key() into a DatabaseField and set as the direct key field for this mapping. // In this example, the fully qualified field name is "LICENSE.LICENSE" mapping.setDirectKeyField(directKeyField);

// Process value() into a DatabaseField and set as the direct field for this mapping. // In this example, the fully qualified field name is "LICENSE.STATUS" mapping.setDirectField(directField)

// Match up the keyConverter to create the correct converter for the key. ObjectTypeConverter keyConverter = new ObjectTypeConverter(); keyConverter.addConversionValue("AL", "Alcohol License"); keyConverter.addConversionValue("FD", "Food License"); keyConverter.addConversionValue("SM", "Smoking License"); keyConverter.addConversionValue("SL", "Site Licence");

// Set the key converter on the mapping. mapping.setKeyConverter(licensesKeyConverter);

// Match up the valueConverter to create the correct converter for the value. TypeConversionConverter valueConverter = new TypeConversionConverter(); valueConverter.setObjectClass(Boolean.class); valueConverter.setDataClass(Integer.class);

// Set the value converter on the mapping. mapping.setValueConverter(valueConverter);

// Primary key join columns are processed to produce the following fields. // Note: This method needs to be added to DirectMapMapping as it does not currently exist. mapping.addReferenceKeyField(new DatabaseField("LICENSE.REST_ID"), new DatabaseField("RESTAURANT.ID"));

// Mapping added to the descriptor. descriptor.addMapping(mapping); {code}

  • Design*
  • See Common design notes for @BasicCollection and @BasicMap for more information.
  • Build new BasicMapAccessor that extends DirectCollectionAccessor. When the time comes,

oracle.toplink.ejb.cmp3.xml.accessors.XMLBasicMapAccessor.class will be built that will extend this class and will provide XML overriding and merging.

  • Implement getColumn() on BasicMapAccessor to extract column information from within the @BasicMap
  • @BasicMap must be processed in stage two, that is, with relationship mappings since we require a

primary key to be processed before hand.

  • ClassAccessor will build BasicMapAccessor.
  • BasicMapAccessor must implement process()
  • The matching up of converters with the key and value will throw an exception if the converter with

the matching name is not found. The converters are expected to be defined on the corresponding accessor.

  • Common design for @BasicCollection and @BasicMap*
  • Build new abstract DirectAccessor class and move common basic processing code @Enumerated, @Lob,

@Temporal and serialized processing from BasicAccessor to DirectAccessor.

  • BasicAccessor will now extend DirectAccessor
  • Build new abstract DirectCollectionAccessor which will hold common processing for @BasicCollection

and @BasicMap

  • Move getDatabaseField() to DirectAccessor from BasicAccessor. Allows re-use and includes attribute

override support for @BasicCollection and @BasicMap

  • Table processing to be re-used from ClassAccessor. Will provide logging for any defaulting to the

table name (schema and catalog).

  • Process PrimaryKeyJoinColumns is available from MetadataAccessor, therefore, validation is available

for re-use in this case. (Validating the number of primary key join columns specified with regards to the number of primary key fields etc)

h3. @PrivateOwned A @PrivateOwned annotation can be used in conjunction with @BasicCollection, @BasicMap, @OneToOne, and @OneToMany.

{code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface PrivateOwned {} {code}

Warnings:

  • Warning logged if @PrivateOwned used with a @ManyToOne.
  • Warning logged if @PrivateOwned used with a @ManyToMany.

Exceptions:

  • None
  • Example*

{code} @OneToMany(cascade=ALL, mappedBy="manager") @PrivateOwned public Collection getManagedEmployees() {

   return managedEmployees;

} {code}

  • Internal processing*

{code} mapping.setPrivateOwned(true); {code}

  • Design*
  • For @OneToOne and @ManyToOne mappings, this value will be set in ObjectAccessor's initOneToOne()

method

  • For @OneToMany and @ManyToOne mappings, this value is set in CollectionAccessor's

populateCollectionMapping() method

  • For @BasicCollection and @BasicMap, this value should be set in their respective process()

methods. Any common processing between these mappings and @BasicAccessor should be moved up to DirectAccessor or DirectCollectionAccessor.

  • Common processing code should be moved up to MetadataAccessor for re-use within those accessors

that support this functionality. The @PrivateOwned annotation is ignored by all those accessors (eg. BasicAccessor) where it does not apply. No exception is thrown.

  • @PrivateOwned is automatically applied to @Embedded and @EmbeddedCollection annotations. No

exception is thrown or warning is logged if @PrivateOwned is set on these attributes.

h2. Optimistic Locking The following annotation will be added to allow 5 different optimisitic locking policies implemented by TopLink. Namely:

  • oracle.toplink.descriptors.AllFieldsLockingPolicy
  • oracle.toplink.descriptors.ChangedFieldsLockingPolicy
  • oracle.toplink.descriptors.SelectedFieldsLockingPolicy
  • oracle.toplink.descriptors.VersionLockingPolicy
  • oracle.toplink.descriptors.TimestampLockingPolicy)

h3. @OptimisticLocking The @OptimisticLocking is used to specify the type of optimistic locking TopLink should use when updating or deleting entities.

{code} @Target({TYPE}) @Retention(RUNTIME) public @interface OptimisticLocking {

   /**
    * (Optional) The type of optimistic locking policy to use.
    */
   OptimisticLockingType type() default VERSION_COLUMN;
   /**
    * (Optional) For an optimistic locking policy of type SELECTED_COLUMNS, this annotation
    * member becomes a (Required) field.
    */
   Column[] selectedColumns() default {};
   /**
    * (Optional) Specify where the optimistic locking policy should cascade lock. Currently
    * only supported with VERSION_COLUMN locking.
    */
   boolean cascade() default false;

}

public enum OptimisticLockingType {

   /**
    * Using this type of locking policy compares every field in the table
    * in the WHERE clause when doing an update or a delete. If any field
    * has been changed, an optimistic locking exception will be thrown.
    */
   ALL_COLUMNS,
   /**
    * Using this type of locking policy compares only the changed fields
    * in the WHERE clause when doing an update. If any field has been
    * changed, an optimistic locking exception will be thrown. A delete
    * will only compare the primary key.
    */
   CHANGED_COLUMNS,
   /**
    * Using this type of locking compares selected fields in the WHERE
    * clause when doing an update or a delete. If any field has been
    * changed, an optimistic locking exception will be thrown. Note that
    * the fields specified must be mapped and not be primary keys.
    */
   SELECTED_COLUMNS,
   /**
    * Using this type of locking policy compares a single version number
    * in the where clause when doing an update. The version field must be
    * mapped and not be the primary key.
    */
   VERSION_COLUMN

} {code}

Setting an @OptimisticLocking 'could' override any @Version specification on the entity. No exception is thrown, instead a warning message will be logged. Since @Version was introduced by JPA, a @Version without any @OptimisticLocking specification is still a valid way to define a VersionLockingPolicy on the source entity.

Warnings:

  • If a @Version annotation is found on an entity with an OptimisticLocking policy type other than

VERSION_COLUMN, a log warning will be issued that states the @Version annotation is being ignored.

  • If selectedColumns are defined with any other optimistic locking type other than SELECTED_COLUMNS,

a log warning will be issued stating that those @Column specifications are being ignored.

Exceptions:

  • An exception will be thrown if the SELECTED_COLUMNS type is specified and the selectedColumns are

not (which also includes if the name on Column is not specified)

  • An exception will be thrown if the VERSION_COLUMN type is specified and no corresponding @Version

is found.


  • All_COLUMNS example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @OptimisticLocking(type=ALL_COLUMNS) public class Employee implements Serializable {

   private Integer id;
   private String firstName;
   private String lastName;
   ...

} {code}

  • CHANGED_COLUMNS example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @OptimisticLocking(type=CHANGED_COLUMNS) public class Employee implements Serializable {

   private Integer id;
   private String firstName;
   private String lastName;
   ...

} {code}

  • SELECTED_COLUMNS example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @OptimisticLocking(

   type=SELECTED_COLUMNS,
   selectedColumns={@Column(name="id"), @Column(name="firstName")}

) public class Employee implements Serializable {

   private Integer id;
   private String firstName;
   private String lastName;
   ...

} {code}

  • VERSION_COLUMN example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @OptimisticLocking(

   type=VERSION_COLUMN,

) public class Employee implements Serializable {

   private Integer id;
   private String firstName;
   private String lastName;
   @Version private int version;
   ...

} {code}

  • ALL_COLUMNS internal processing*

{code} descriptor.setOptimisticLockingPolicy(new AllFieldsLockingPolicy()); {code}

  • CHANGED_COLUMNS internal processing*

{code} descriptor.setOptimisticLockingPolicy(new ChangedFieldsLockingPolicy()); {code}

  • SELECTED_COLUMNS internal processing*

{code} descriptor.setOptimisticLockingPolicy(new SelectedFieldsLockingPolicy());

// For all selectedColumns defined on the entity ... ((SelectedFieldsLockingPolicy) descriptor.getOptimisticLockingPolicy()).addLockField(selectedColumn); {code}

  • VERSION_COLUMN internal processing*

{code} // Find the @Version mapping within the entity and process its @Column information into a // DatabaseField. VersionLockingPolicy policy = new VersionLockingPolicy(versionField);

// Process cascade() policy.setIsCascaded(cascade());

descriptor.setOptimisticLockingPolicy(policy); {code}

  • Design*
  • @OptimisticLocking is processed in ClassAccessor's process() method.
  • @Version is processed in DirectAccessor's process() method.

h2. Entity Caching TopLink uses identity maps to cache objects for performance and maintain object identity. Users can control the cache and its behavior by decorating their entity classes with a @Cache annotation.

h3. @Cache The @Cache annotation may be defined on an @Entity or @MappedSuperclass. In the case of inheritance, a @Cache annotation can only be defined on the root of the inheritance hierarchy.

{code} @Target({TYPE}) @Retention(RUNTIME) public @interface Cache {

   /**
    * (Optional) The type of cache to use.
    */ 
   CacheType type() default SOFT_WEAK;
   /**
    * (Optional) Cached instances in the shared cache or a client 
    * isolated cache.
    */ 
   boolean isolated() default false;
   /**
    * (Optional) Expire cached instance after a fix period of time (ms). 
    * Queries executed against the cache after this will be forced back 
    * to the database for a refreshed copy
    */ 
   int expiry() default -1; // minus one is no expiry.
   /**
    * (Optional) Expire cached instance a specific time of day. Queries 
    * executed against the cache after this will be forced back to the 
    * database for a refreshed copy
    */ 
   TimeOfDay expiryTimeOfDay() default @TimeOfDay(specified=false);
   /**
    * (Optional) Force all queries that go to the database to always 
    * refresh the cache.
    */ 
   boolean alwaysRefresh() default false;
   /**
    * (Optional) For all queries that go to the database, refresh the cache 
    * only if the data received from the database by a query is newer than 
    * the data in the cache (as determined by the optimistic locking field)
    */ 
   boolean refreshOnlyIfNewer() default false;
   /**
    * (Optional) Setting to true will force all queries to bypass the 
    * cache for hits but still resolve against the cache for identity. 
    * This forces all queries to hit the database.
    */ 
   boolean disableHits() default false;
   /**
    * (Optional) The cache coordination mode.
    */ 
   CacheCoordinationType coordinationType() default SEND_OBJECT_CHANGES;

}

public enum CacheType {

   /**
    * Provides full caching and guaranteed identity. Caches all objects
    * and does not remove them. 
    * WARNING: This method may be memory intensive when many objects are 
    * read.
    */
   FULL,
   /**
    * Similar to the FULL identity map except that the map holds the
    * objects using weak references. This method allows full garbage
    * collection and provides full caching and guaranteed identity.
    */
   WEAK,
   /**
    * Similar to the WEAK identity map except that it maintains a
    * most-frequently-used sub-cache. The size of the sub-cache is
    * proportional to the size of the identity map as specified by
    * descriptor's setIdentityMapSize() method. The sub-cache
    * uses soft references to ensure that these objects are
    * garbage-collected only if the system is low on memory.
    */
   SOFT_WEAK,
   /**
    * Identical to the soft cache weak (SOFT_WEAK) identity map except 
    * that it uses hard references in the sub-cache. Use this identity 
    * map if soft references do not behave properly on your platform.
    */
   HARD_WEAK,
   /**
    * A cache identity map maintains a fixed number of objects
    * specified by the application. Objects are removed from the cache
    * on a least-recently-used basis. This method allows object
    * identity for the most commonly used objects.
    * WARNING: Furnishes caching and identity, but does not guarantee 
    * identity.
    */
   CACHE,
   /**
    * WARNING: Does not preserve object identity and does not cache 
    * objects.
    */
   NONE

}

public enum CacheCoordinationType {

   /**
    * Sends a list of changed objects including data about the changes. This data is merged into 
    * the receiving cache.
    */
   SEND_OBJECT_CHANGES,
   /**
    * Sends a list of the identities of the objects that have changed. The receiving cache 
    * invalidates the objects (rather than changing any of the data)
    */
   INVALIDATE_CHANGED_OBJECTS,
   /**
    * Same as SEND_OBJECT_CHANGES except it also includes any newly created objects from the 
    * transaction.
    */
   SEND_NEW_OBJECTS_WITH_CHANGES,
   /**
    * Does no cache coordination.
    */
   NONE

} {code}

Warnings:

  • An @Cache annotation defined on an inheritance subclass will be ignored. A log warning will

be issued.

Exceptions:

  • An @Cache annotation defined on an @Embeddable will cause an exception to be thrown.
  • Both expiry() and expiryTimeOfDay() specified will cause and exception to be thrown.
  • Example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @Cache(

   type=FULL,
   isolated=false,
   expiry=1000,
   alwaysRefresh=true,
   disableHits=true,
   coordinationType=INVALIDATE_CHANGED_OBJECTS

) public class Employee implements Serializable {

   ...

} {code}

  • Internal processing*

{code} // Process type() descriptor.useFullIdentityMap(); // FULL descriptor.useWeakIdentityMap(); // WEAK descriptor.useSoftCacheWeakIdentityMap(); // SOFT_WEAK descriptor.useHardCacheWeakIdentityMap(); // HARD_SOFT descriptor.useCacheIdentityMap(); // CACHE descriptor.useNoIdentityMap(); // NONE

// Process isolated() descriptor.setIsIsolated(isolated());

// Process expiry() descriptor.setCacheInvalidationPolicy(new TimeToLiveCacheInvalidationPolicy(expiry()));

// If expiryTimeOfDay() was specified TimeOfDay tod = expiryTimeOfDay(); DailyCacheInvalidationPolicy policy = new DailyCacheInvalidationPolicy(tod.hour(), tod.minute(), tod.second(), tod.millisecond()); descriptor.setCacheInvalidationPolicy(policy);

// Process alwaysRefresh() desriptor.setShouldAlwaysRefreshCache(alwaysRefresh());

// Process refreshOnlyIfNewer() descriptor.setShouldOnlyRefreshCacheIfNewerVersion(refreshOnlyIfNewer());

// Process disableHits() descriptor.setShouldDisableCacheHits(disableHits());

// Process coordinationType() // The ordinal values co-incide with their internal int values. descriptor.setCacheSynchronizationType(coordinationType().ordinal); {code}

  • Design*
  • @Cache values are processed in ClassAccessor's process() method.

h3. @TimeOfDay @TimeOfDay is used to specify a specific time of day using a Calendar instance.

{code} @Target({}) @Retention(RUNTIME) public @interface TimeOfDay {

   /**
    * (Optional) Hour of the day.
    */ 
   int hour() default 0;
   /**
    * (Optional) Minute of the day.
    */ 
   int minute() default 0;
   /**
    * (Optional) Second of the day.
    */ 
   int second() default 0;
   /**
    * (Optional) Millisecond of the day.
    */ 
   int millisecond() default 0;
   /**
    * Internal use. Do not modify.
    */ 
   boolean specified() default true;

} {code}

h2. Change Tracking

h3. @ChangeTracking {code} @Target({TYPE}) @Retention(RUNTIME) public @interface ChangeTracking {

   /**
    * (Optional) The type of change tracking to use.
    */ 
   ChangeTrackingType value() default AUTO;

}

public enum ChangeTrackingType {

   /**
    * An ATTRIBUTE change tracking type allows change tracking at the attribute 
    * level of an object. Objects with changed attributes will be processed in 
    * the commit process to include any changes in the results of the commit.
    * Unchanged objects will be ignored.
    */
   ATTRIBUTE,
   /**
    * An OBJECT change tracking policy allows an object to calculate for itself 
    * whether it has changed. Changed objects will be processed in the commit 
    * process to include any changes in the results of the commit.
    * Unchanged objects will be ignored.
    */
   OBJECT,
   /**
    * A DEFERRED change tracking policy defers all change detection to the 
    * UnitOfWork's change detection process. Essentially, the calculateChanges() 
    * method will run for all objects in a UnitOfWork. 
    * This is the default ObjectChangePolicy
    */
   DEFERRED,
   /**
    * Will not set any change tracking policy.
    */
   AUTO

} {code}

  • Example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @ChangeTracking(OBJECT) public class Employee implements Serializable {

   ...

} {code}

  • Internal processing*

{code} descriptor.setObjectChangePolicy(new AttributeChangeTrackingPolicy()); // ATTRIBUTE descriptor.setObjectChangePolicy(new ObjectChangeTrackingPolicy()); // OBJECT descriptor.setObjectChangePolicy(new DeferredChangeDetectionPolicy()); // DEFERRED descriptor.setObjectChangePolicy(new AutoChangeDetectionPolicy()): // AUTO // NOTE: AutoChangeDetectionPolicy does not currently exist. It will need to be created. {code}

  • Design*
  • @ChangeTracking value is processed in ClassAccessor's process() method.

h2. Descriptor Customizer h3. @Customizer The @Customizer annotation is used to specify a class that implements the oracle.toplink.tools.sessionconfiguration.DescriptorCustomizer interface and that is to be run against a class' descriptor after all metadata processing has been completed.

The @Customizer annotation may be defined on an @Entity, @MappedSuperclass or @Embeddable. In the case of inheritance, a @Customizer is not inherited from its parent classes.

{code} @Target({TYPE}) @Retention(RUNTIME) public @interface Customizer {

   /**
    * Defines the name of the descriptor customizer that should be
    * applied to this entity's descriptor.
    */
   Class value(); 

} {code}

  • Example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @Customizer(my.package.customizer.class) public class Employee implements Serializable {

   ...

} {code}

  • Internal Processing*

{code} Class customizerClass = Customizer.value(); try {

   DescriptorCustomizer customizer = (DescriptorCustomizer) customizerClass.newInstance();
   customizer.customize(descriptor);

} catch (Exception ex) {

   // should be called through the appropriate validator
   ValidationException.errorInstantiatingDescriptorCustomizer(customizerClass , ex);

}

{code}

  • Design*
  • @Customizer values should be processed after pre-deploy. That is, in deploy, after the entity

listeners and queries have been processed, another call should be made to process the descriptor customizers. This must be the last call to the metadata processor to manipulate metadata.

h3. @ReadOnly The @ReadOnly annotation is used to specify that a class is read only.

The @ReadOnly annotation may be defined on an @Entity or @MappedSuperclass. In the case of inheritance, a @ReadOnly annotation can only be defined on the root of the inheritance hierarchy.

{code} @Target({TYPE}) @Retention(RUNTIME) public @interface ReadOnly {} {code}

  • Example*

{code} @Entity @ReadOnly public class Employee implements Serializable {

   ...

} {code}

  • Internal Processing*

{code} descriptor.setReadOnly(); {code}

  • Design*
  • @ReadOnly is processed in ClassAccessor's process() method.

h2. Returning policy Allows for INSERT or UPDATE operations to return values back into the object being written. This allows for table default values, trigger or stored procedures computed values to be set back into the object.

h3. @ReturnInsert {code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ReturnInsert {

   boolean returnOnly() default false;

} {code}

Warnings:

  • A @ReturnInsert annotation can only be specified for a @Basic mapping. If used with @BasicCollection,

@BasicMap, @EmbeddedId, @Embedded, @ManyToMany, @ManyToOne, @OneToMany or @OneToOne, a log warning will be issued.

Exceptions:

  • None
  • Example 1*

{code} @ReturnInsert() public String getFirstName() {

   return firstName;

} {code}

  • Example 2*

{code} @ReturnInsert(returnOnly=true) public String getFirstName() {

   return firstName;

} {code}

  • Internal Processing*

{code} // For both examples, a database field is created from the @Column (if specified), // otherwise defaulted as normal for any direct to field mapping. This also takes // into consideration @AttributeOverride from a subclass of a @MappedSuperclass.

// Example 1 descriptor.getReturningPolicy().addFieldForInsert(field);

// Example 2 descriptor.getReturningPolicy().addFieldForInsertReturnOnly(field); {code}

  • Design*
  • Processed in BasicAccessor's process() method.

h3. @ReturnUpdate {code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ReturnUpdate {} {code}

Warnings:

  • A @ReturnUpdate annotation can only be specified for a @Basic mapping. If used with

@BasicCollection, @BasicMap, @EmbeddedId, @Embedded, @ManyToMany, @ManyToOne, @OneToMany or @OneToOne, a log warning will be issued.

Exceptions:

  • None
  • Example*

{code} @ReturnUpdate() public String getFirstName() {

   return firstName;

} {code}

  • Internal Processing*

{code} // A database field is created from the @Column (if specified), otherwise defaulted // as normal for any direct to field mapping. descriptor.getReturningPolicy().addFieldForUpdate(field); {code}

  • Design*
  • Processed in BasicAccessor's process() method.

h2. Stored procedure query

h3. @NamedStoredProcedureQuery @NamedStoredProcedureQuery allows the definition of queries that call stored procedures as named queries.

{code} @Target({TYPE}) @Retention(RUNTIME) public @interface NamedStoredProcedureQuery {

   /**
    * (Required) Unique name that references this stored procedure query.
    */
   String name();
   /**
    * (Optional) Query hints.
    */
   QueryHint[] hints() default {};
   /**
    * (Optional) Refers to the class of the result.
    */
   Class resultClass() default void.class;
   /**
    * (Optional) The name of the SQLResultMapping.
    */
   String resultSetMapping() default "";
   /**
    * (Required) The name of the stored procedure.
    */
   String procedureName();
   /**
    * (Optional) Whether the query shouod return a result set.
    */
   boolean returnsResultSet() default true; 
   /**
    * (Optional) Defines arguments to the stored procedure.
    */
   StoredProcedureParameter[] procedureParameters() default {};

}

@Target({}) @Retention(RUNTIME) public @interface StoredProcedureParameter {

   /**
    * (Optional) The direction of the stored procedure parameter.
    */
   Direction procedureParameterDirection() default IN;
   /**
    * (Optional) Stored procedure parameter name.
    */
   String name() default "";
   /**
    * (Required) The query parameter name.
    */
   String queryParameter();
   /**
    * (Optional) The type of Java class desired back from the procedure, 
    * this is dependent on the type returned from the procedure.
    */
   Class type() default void.class;
   /**
    * (Optional) The JDBC type code, this dependent on the type returned 
    * from the procedure.
    */
   int jdbcType() default -1;
   /**
    * (Optional) The JDBC type name, this may be required for ARRAY or 
    * STRUCT types.
    */
   String jdbcTypeName() default "";

}

public enum Direction {

   /**
    * Input parameter
    */
   IN,
   /**
    * Output parameter
    */
   OUT,
   /**
    * Input and output parameter
    */
   IN_OUT,
   /**
    * Output cursor
    */
   OUT_CURSOR

} {code}

Warnings

  • None

Exceptions:

  • If more than one StoreProcedureQuery of type OUTPUT_CURSOR is specified, an exception will be thrown.
  • Example*

{code} @Entity @Table(name="CMP3_EMPLOYEE") @NamedStoredProcedureQuery(

   name="ReadEmployee",
   procedureName="Read_Employee",
   storedProcedureParameters={@StoredProcedureParameter(queryParamater="EMP_ID")}

) public class Employee {

   ...

} {code}

  • Internal Processing*

{code} // Process name(). // Check if a named stored procedure query with that name already exists, and if so, log a // warning message.

// If there is a resultClass() ReadAllQuery query = new ReadAllQuery(resultClass()); // ... process the stored procedure paramaters (see below) ...

// If there is a resultSetMapping() ResultSetMappingQuery query = new ResultSetMappingQuery(); query.setSQLResultSetMappingName(resultSetMapping()); // ... process the stored procedure paramaters (see below) ...

// If there is no resultClass() or resultSetMapping() ResultSetMappingQuery query = new ResultSetMappingQuery(); // ... process the stored procedure paramaters (see below) ...

// Build a StoredProcedureCall. StoredProcedureCall call = new StoredProcedureCall();

// Process procedureName() to set the procedure name on the call. call.setProcedureName(procedureName());

// Process returnsResultSet() call.setReturnsResultSet(returnsResultSet());

// Processing the stored procedure parameters ...

// Process procedureParameterDirection() ... String argumentFieldName = queryParameter(); String procedureParameterName = name().equals("") ? argumentFieldName : name();

// ** For IN direction ** // If type() is specified call.addNamedArgument(procedureParameterName, argumentFieldName, type()); // If jdbcType() is specified call.addNamedArgument(procedureParameterName, argumentFieldName, jdbcType()); // If jdbcType() and jdbcTypeName() are specified call.addNamedArgument(procedureParameterName, argumentFieldName, jdbcType(), jdbcTypeName()); // If no types are specified call.addNamedArgument(procedureParameterName, argumentFieldName); // Then add the argument to the query. query.addArgument(argumentFieldName);

// ** For OUT direction ** // If type() is specified call.addNamedOutputArgument(procedureParameterName, argumentFieldName, type()); // If jdbcType() is specified call.addNamedOutputArgument(procedureParameterName, argumentFieldName, jdbcType()); // If jdbcType() and jdbcTypeName() are specified call.addNamedOutputArgument(procedureParameterName, argumentFieldName, jdbcType(), jdbcTypeName()); // If no types are specified call.addNamedOutputArgument(procedureParameterName, argumentFieldName); // Then add the argument to the query. query.addArgument(argumentFieldName);

// ** For IN_OUT direction ** // If type() is specified call.addNamedInOutputArgument(procedureParameterName, argumentFieldName, argumentFieldName, type()); // If jdbcType() is specified call.addNamedInOutputArgument(procedureParameterName, argumentFieldName, argumentFieldName, jdbcType()); // If jdbcType() and jdbcTypeName() are specified call.addNamedInOutputArgument(procedureParameterName, argumentFieldName, argumentFieldName, jdbcType(), jdbcTypeName()); // If no types are specified call.addNamedInOutputArgument(procedureParameterName, argumentFieldName); // Then add the argument to the query. query.addArgument(argumentFieldName);

// ** For OUT_CURSOR direction ** call.useNamedCursorOutputAsResultSet(argumentFieldName);

// Process hints() // Same as @NamedQuery and @NamedNativeQuery

// And finally ... query.setCall(call); session.addQuery(query); {code}

  • Design*
  • Processed in ClassAccessor, during the same call from MetadataProcessor to process named query annotations.

h2. Future enhancements h3. @EmbeddedCollection The @EmbeddedCollection annotation is used to represent an aggregate relationship between a single source object and a collection of target objects. It maps to a TopLink AggregateCollectionMapping. The target objects cannot exist without the existence of the source object (private owned) and there must be a target table mapped from the target objects.

An @EmbeddedCollection will work similarly to the existing @Embedded annotation. In that, the access type for an embedded collection will be discovered in the same manner that it is currnetly discovered for an embedded object (That is, according to Sahoo's fix of Glassfish issue 831). Also, the @EmbeddedCollection is defaulted if the source entity holds a collection of objects that are decorated with the @Embeddable annotation.

The @EmbeddedCollection annotation is used in conjunction with @PrimaryKeyJoinColumn(s). If the source entity uses a composite primary key, a primary key join column must be specified for each field of the composite primary key. In a single primary key case, a primary key join column may optionally be specified. Defaulting will apply otherwise as follows:

  • name, the concatenation of the following: Embeddable.Table.name; "_";

the primary key column of the source entity.

  • referencedColumnName, the primary key column of the source entity.

{code} @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface EmbeddedCollection {

   /**
    * (Optional) The target collection type.
    */
   Class target() default java.util.Vector.class;
   /**
    * (Optional) The target embeddable class. It should be specified if
    * generics are not used.
    */
   Class targetEmbeddable() default void.class;
   /**
    * (Optional) Defines whether the value of the field or property should
    * be lazily loaded or must be eagerly fetched. The EAGER
    * strategy is a requirement on the persistence provider runtime
    * that the value must be eagerly fetched. TheLAZY
    * strategy is a hint to the persistence provider runtime.
    * If not specified, defaults toLAZY.
    */
   FetchType fetch() default LAZY;

} {code}

  • Example*

{code} @EmbeddedCollection @PrimaryKeyJoinColumn(name="CUSTOMER_ID", referenceColumnName="CUSTOMER_ID") public CollectiongetDependants() {

   return dependants;

}

@Embeddable @Table(name="DEPEND") public Dependant {

   ...

} {code}

  • Internal processing*

{code} AggregateCollectionMapping mapping = new AggregateCollectionMapping(); mapping.setAttributeName("dependants"); mapping.setIsReadOnly(false);

// Process fetch() type mapping.useBasicIndirection(); // LAZY setting mapping.useCollectionClass(target); // EAGER setting

// Set the accessor methods if the access for this class is PROPERTY setAccessorMethods(mapping);

// Process targetEmbeddable() to set the reference class for the mapping. // In this example, the reference class is "Dependant.class" mapping.setReferenceClass(referenceClass);

// Primary key join columns are processed to produce the following fields. mapping.addTargetForeignKeyField(new DatabaseField("DEPEND.CUSTOMER_ID"), new DatabaseField("CUSTOMER.CUSTOMER_ID")); {code}

  • Design*
  • A @Table must be specified (therefore, processed) on the @Embeddable if it is to be used

in an @EmbeddableCollection.

  • Build new EmbeddedCollectionAccessor that extends EmbeddedAccessor. When the time comes,

oracle.toplink.ejb.cmp3.xml.accessors.XMLEmbeddedCollectionAccessor.class will be built that will extend this class and will provide XML overriding and merging.

  • @EmbeddedCollection must be processed in stage two, that is, with relationship mappings since we

require a primary key to be processed before hand.

  • ClassAccessor will build EmbeddedCollectionAccessor.
  • EmbeddedCollectionAccessor must implement process()
  • Process PrimaryKeyJoinColumns is available from MetadataAccessor, therefore, validation is available

for re-use in this case. (Validating the number of primary key join columns specified with regards to the number of primary key fields etc)

h3. Other items

  • Descriptor event listeners (in addition to what is already defined in JPA)
  • Collect all exceptions and throw at the end of metadata processing.
  • Order by
  • Collection type
  • Fetch joining
  • Batch reading
  • Complex multiple table
  • Complex 1-1 foreign key
  • Variable 1-1
  • Interfaces
  • Transformation mapping
  • XML type
  • EIS mappings
  • Nullable embeddables
  • Custom sql/calls
  • Complex selection criteria
  • Class name for indicator
  • Complex inheritance
  • Read-only
  • Properties
  • History
  • Relationship maintenance
  • Additional join
  • Fetch groups
  • Does exist
  • Query timeout
  • Constraint dependency
  • Copy policy
  • Instantiation policy
  • UOW isolation
  • Other query types
  • Data read
  • Data modification
  • Inheritance with embeddables
  • Relationships with embeddables
  • 1-1 to be specified as part of a primary key @Id on 1-1
  • 1-m without requiring a mapped-by, allowing a @JoinColumn instead

of a @JoinTable (assume the foreign key is mapped as a basic or other in the target)

  • Relationships from an embedable
  • 1-1 with composite foriegn keys and mixed source/target foreign

keys

  • Mixed access type at the field/property level
  • Composite primary keys without a PK class
  • Remove the "must have the equal amount of JoinColumns specified as composite primary key parts when relating two entities" requirement. That is allow, the foreign key to map to a column or columns that are not the primary key columns. (Works in single case, but not composite primary key case)

Back to the top