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

Difference between revisions of "Http://wiki.eclipse.org/EclipseLink/DesignDocs/JPA XML metadata extensions"

(Design and processing)
(Exceptions)
Line 242: Line 242:
 
</pre></code>
 
</pre></code>
  
==== Exceptions ====
+
 
* If multiple objectValue's are specified for the same dataValue an exception will be thrown.
+
  
 
==== Design and processing ====
 
==== Design and processing ====

Revision as of 11:14, 19 September 2007

WARNING ... WORK IN PROGRESS ...

Design Specification: JPA XML metadata extensions

Document History

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

Goals

The goal of this design spec is to expose some features from EclipseLink that are currently not available through the use of JPA xml metadata. The features that will be exposed in this document already have their annotation counterparts implemented in the current version of EclipseLink. This document assumes familiarity with those annotations and will focus mainly on the XML extension portions. That is, in most cases, internal processing and design remains as already defined for the equivalent annotations. This document will discuss only those portions that will be required to be implemented or addressed as a result of introducing the new xml elements.

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.

Also note, the new features exposed by this design spec are by no means the end all of features available from EclipseLink. 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 EclipseLink deployment project with the configuration of annotations and/or XML.

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.

Converters

The following coverter complex types will be added (converter, type-converter, object-type-converter) and there usage is done through the convert element that will be added to those mapping elements that support using a converter (See basic, basic-collection and basic-map).

<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"/>

The converters can be specified at the following levels:

  • entity-mappings
  • entity
  • mapped-superclass

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)

Warnings

  • 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.

Exceptions

  • 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>

Design and processing

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

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>

Design and processing

  • The org.eclipse.persistence.internal.jpa.metadata.converters.xml.XMLTypeConverter class will be built and will extend the MetadataTypeConverter. Its only responsibility will be to override the metadata that is processed.
  • Processing is currently defined in the org.eclipse.persistence.internal.jpa.metadata.converters.MetadataConverter. (see previous design doc)

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>


Design and processing

  • 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.
  • Processing is currently defined in the org.eclipse.persistence.internal.jpa.metadata.converters.MetadataObjectConverter. (see previous design doc)
  • If multiple objectValue's are specified for the same dataValue an exception will be thrown.

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.
<xsd:complexType name="convert">
  <xsd:annotation>
    <xsd:documentation>
      @Target({METHOD, FIELD})
      @Retention(RUNTIME)
      public @interface Convert {
        /**
         * (Required) The name of the converter to be used.
         */
        String value() default "none";
      }

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

Exceptions

  • A convert name that does not match up with a converter will throw an exception.

Design and processing

  • See individual (@Converter, @TypeConverter, @ObjectTypeConverter) internal processing details.
  • A convert value is processed in org.eclipse.persistence.internal.jpa.metadata.accessors.DirectAccessor's process() method.

Mapping Annotations

  • The following new mapping related elements will be added.
  1. basic-collection
  2. basic-map
  3. collection-table

collection-table

A collection-table is used in conjunction with a basic-collection or a basic-map element. If no collection-table is defined with a basic-collection or basic-map then one will be defaulted.

The following collection-table complex type will be added. See the basic-collection and basic-map elements for more information.

<xsd:complexType name="collection-table">
  <xsd:annotation>
    <xsd:documentation>
      @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 {}; 
    }

    </xsd:documentation>
  </xsd:annotation>
  <xsd:attribute name="name" type="xsd:string" use="optional"/>
  <xsd:attribute name="catalog" type="xsd:string" use="optional"/>
  <xsd:attribute name="schema" type="xsd:string" use="optional"/>
  <xsd:element name="primary-key-join-column" type="orm:primary-key-join-column" minOccurs="0" maxOccurs="unbounded"/>
  <xsd:element name="unique-constraint" type="orm:unique-constraint" minOccurs="0" maxOccurs="unbounded"/>
</xsd:complexType>

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.

Design and processing

  • Processing of the collection-table is done within those mappings that support one. That is, basic-collection and basic-map. Therefore, the new accessor classes added to support those methogs will need to override the getCollectionTable() method to return an org.eclipse.persistence.internal.metadata.accessors.table.xml.XMLCollectionTable which will subclass the org.eclipse.persistence.internal.metadata.accessors.table.MetadataCollectionTable class.
  • The XMLCollectionTable's only responsibility will be to override the metadata that is processed.

basic-collection

The basic-collection element is used to map an org.eclipse.persistence.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. It is used in conjunction with the a collection-table and/or convert if necessary.

<xsd:complexType name="basic-collection">
  <xsd:annotation>
    <xsd:documentation>
      @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;
      }

    </xsd:documentation>
  </xsd:annotation>
  <xsd:attribute name="fetch" type="orm:fetch-type"/>
  <xsd:element name="column" type="orm:column" minOccurs="0"/>
  <xsd:attribute name="collection-table" type="xsd:collection-table" use="optional"/>
  <xsd:attribute name="convert" type="xsd:convert" use="optional"/>
</xsd:complexType>

Exceptions

  • If a basic-collection is specified on an attribute of type Map, an exception will be thrown.

Design and processing

  • See design for @BasicCollection and @BasicMap for more information.
  • Build new org.eclipse.persistence.internal.jpa.metadata.accessors.tables.xml.XMLBasicCollectionAccessor that extends org.eclipse.persistence.internal.jpa.metadata.accessors.tables.BasicCollectionAccessor. It's sole purpose will be to override the metatadat that is processed.
  • The XMLBasicCollectionAccessor's only responsibility will be to override the metadata that is processed.

basic-map

The basic-map xml element 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. A basic-map can be used in conjunction with a collection-table.

<xsd:complexType name="basic-collection">
  <xsd:annotation>
    <xsd:documentation>
      @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;
      }

    </xsd:documentation>
  </xsd:annotation>
  <xsd:attribute name="fetch" type="orm:fetch-type"/>
  <xsd:element name="key-column" type="orm:column" minOccurs="0"/>
  <xsd:attribute name="key-converter" type="xsd:convert" use="optional"/>
  <xsd:element name="value-column" type="orm:column" minOccurs="0"/>
  <xsd:attribute name="value-converter" type="xsd:convert" use="optional"/>
  <xsd:attribute name="collection-table" type="xsd:collection-table" use="optional"/>
</xsd:complexType>

Exceptions

  • If a basic-map is specified on an attribute of type Collection, an exception will be thrown.


Design and processing

  • See design for @BasicCollection and @BasicMap for more information.
  • Build new org.eclipse.persistence.internal.jpa.metadata.accessors.xml.XMLBasicMapAccessor that extends org.eclipse.persistence.internal.jpa.metadata.accessors.BasicMapAccessor.
  • The XMLBasicCollectionAccessor's only responsibility will be to override the metadata that is processed.
  • XMLClassAccessor will be responsible for building XMLBasicMapAccessor's.
  • 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.

private-owned

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.

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)

optimistic-locking

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

@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
}

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.

Design

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

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.

@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.

@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
}

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

@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 {
    ...
}
  • 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)

Back to the top