How-To Use EJB 3.0 O-R Mapping Annotations

Date: 2/28/05
Author: Guy Pelletier

Introduction

This example application demonstrates Oracle's support for the EJB 3.0 annotation specification; specifically, the Metadata for Object/Relational (O/R) Mappings.

The Metadata for O/R Mapping annotations allow users to decorate their EJB 3.0 entity bean classes with O/R mapping metadata. This metadata is then used to define the persistence and retrieval of their entity beans.

This document will outline the many details of each O/R mapping annotation supported in this preview release. This also describes how each of these metadata relates to the mapping API used with Oracle TopLink.

@Table

The @Table specifies the primary table for the annotated entity.

Annotation specification

@Target({TYPE}) @Retention(RUNTIME)
public @interface Table {
  String name() default "";
  String catalog() default "";
  String schema() default "";
  UniqueConstraint[] uniqueConstraints() default {};
  boolean specified() default true; // For internal use only
} }

Example

From: Employee.java

@Entity
@Table(name="EJB_EMPLOYEE")
@SecondaryTable(name="EJB_SALARY")
@JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID")
@GeneratedIdTable(name="EMPLOYEE_GENERATOR_TABLE", table=@Table(name="EJB_EMPLOYEE_SEQ"), pkColumnName="SEQ_NAME", valueColumnName="SEQ_COUNT")
@NamedQuery (
  name="findAllEmployeesByFirstName",
  queryString="SELECT OBJECT(employee) FROM Employee employee WHERE employee.firstName = :firstname"
)
public class Employee implements Serializable {
   ...
}

TopLink defaults and equivalent code

descriptor.addTableName(tableName);

Where:

tableName is built by appending the following annotation members: schema().catalog().name(). If the name() annotation member is not specified, then it defaults to the descriptor alias (which defaults to the class name of the annotated entity). In the example above, the tableName == EJB_EMPLOYEE.


@SecondaryTable

The @SecondaryTable is used to specify a secondary table for an entity bean class. Specifying one or more secondary tables indicates that the entity data is stored across multiple tables. A @Table must already have been specified for the annotated entity.

Annotation specification

@Target({TYPE}) @Retention(RUNTIME)
public @interface SecondaryTable {
  String name();
  String catalog() default "";
  String schema() default "";
  JoinColumn[] join() default {};
  UniqueConstraint[] uniqueConstraints() default {};
} }

Example

From: Employee.java

@Entity
@Table(name="EJB_EMPLOYEE")
@SecondaryTable(name="EJB_SALARY")
@JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID")
@GeneratedIdTable(name="EMPLOYEE_GENERATOR_TABLE", table=@Table(name="EJB_EMPLOYEE_SEQ"), pkColumnName="SEQ_NAME", valueColumnName="SEQ_COUNT")
@NamedQuery (
  name="findAllEmployeesByFirstName",
  queryString="SELECT OBJECT(employee) FROM Employee employee WHERE employee.firstName = :firstname"
)
public class Employee implements Serializable {
   ...
}

TopLink defaults and equivalent code

descriptor.addTableName(secondaryTableName);
descriptor.addMultipleTablePrimaryKeyFieldName(fkColumn, pkColumn);

Where:

secondaryTableName is built by appending the following annotation members: schema().catalog().name(). If the name() annotation member is not specified, then it defaults to the descriptor alias (which defaults to the class name of the annotated entity). In the example above, the secondaryTableName == EJB_SALARY.

pkColumn and fkColumn are extracted from the join() annotation member which contains an array of @JoinColumns. addMultipleTablePrimaryKeyFieldName(fkColumn, pkColumn) is called for each @JoinColumn specified.

pkColumn is equal to JoinColumn.referencedColumnName() annotation member. If it is not specified it is defaulted to the primary key of the primary table. In the example above, the pkColumn == EJB_EMPLOYEE.EMP_ID.

fkColumn is equal to JoinColumn.name() annotation member. If it is not specified it is defaulted to the primary key of the primary table. In the example above, the fkColumn == EJB_SALARY.EMP_ID.


@Column

The @Column is used to specify a mapped column for a persistent property or field.


Annotation specification

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface Column {
  String name() default "";
  boolean primaryKey() default false;
  boolean unique() default false;
  boolean nullable() default true;
  boolean insertable() default true;
  boolean updatable() default true;
  String columnDefinition() default "";
  String secondaryTable() default "";
  int length() default 255;
  int precision() default 0;
  int scale() default 0;
  boolean specified() default true; // For internal use only
} }

Example

From: Employee.java

@Column(name="F_NAME")
public String getFirstName() {
  return firstName;
}

TopLink defaults and equivalent code

mapping.setFieldName(columnName);
mapping.setIsReadOnly(isReadOnly)

Where:

columnName is equals to the name() annotation member. In the example above, columnName == F_NAME. If no name() annotation member is specified, then, if @Entity PROPERTY access, the column name is extracted from a getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then the column name equals the field name. In the example above, the columnName == F_NAME.

isReadOnly is determined from the insertable() and updatable() annotation members. If both are true, then the mapping is set to read only. In the example above, the isReadOnly == false.


@Id

The @Id selects the identifier property of an entity root class.

Annotation specification

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Id {
   GeneratorType generate() default NONE;
   String generator() default "";
}

Example

From: Address.java

@Id(generate=TABLE, generator="ADDRESS_TABLE_GENERATOR")
@TableGenerator(name="ADDRESS_TABLE_GENERATOR", tableName="EMPLOYEE_GENERATOR_TABLE", pkColumnValue="ADDRESS_SEQ")
@Column(name="ADDRESS_ID", primaryKey=true)
public Integer getId() {
  return id;
}

Note

GeneratorType specified by Id.generate() imposes the following restrictions on Id.generator():

  • NONE - no generator should be specified, no generator will be used.
  • AUTO - no generator should be specified, container will use generator named "SEQ_GEN", which uses container defaults (unless specified by user).
  • ENTITY, SEQUENCE - generator should be a SequenceGenerator.
  • TABLE - generator should be a TableGenerator.
  • TopLink defaults and equivalent code

    descriptor.addPrimaryKeyFieldName(columnName);



    columnName is extracted from a @Column. In the case of an id field, if a @Column name() annotation member is not specified, columnName will default to ID. In the example above, columnName == ADDRESS_ID.


    @Version

    The @Version is a marker annotation that keeps track of the version property (optimistic lock value, TopLink OptimisticLockingPolicy) of an entity class. This is used to ensure integrity when reattaching and for overall optimistic concurrency control. It can be used in conjunction with a @Column and/or @Id but may appear on its own. See @Column and @Id TopLink defaults and equivalent code for more information.


    Annotation specification

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

    Example

    From: Employee.java

    @Version
    @Column(name="VERSION")
    public int getVersion() {
      return version;
    }

    TopLink defaults and equivalent code

    VersionLockingPolicy vlp = new VersionLockingPolicy(); vlp.setWriteLockFieldName(columnName); descriptor.setOptimisticLockingPolicy(vlp);

    Where:

    columnName is extracted from a @Column or its defaults. In the example above, columnName == VERSION.


    @Basic

    The @Basic maps directly to a TopLink DirectToFieldMapping. It can be used in conjunction with a @Column but it is not necessary. See @Column TopLink defaults and equivalent code for more information.


    Annotation specification

    public enum FetchType { LAZY, EAGER };

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

    Example

    From: Employee.java

    @Basic(fetch=EAGER)
    @Column(name="F_NAME")
    public String getFirstName() {
      return firstName;
    }

    TopLink defaults and equivalent code

    DirectToFieldMapping mapping = new DirectToFieldMapping();
    mapping.setFieldName(columnName);
    mapping.setIsReadOnly(isReadOnly);
    mapping.setAttributeName(attributeName);

    Where:

    columnName is extracted from a @Column or its defaults. In the example above, columnName == F_NAME.

    isReadOnly is extracted from a @Column or its defaults. In the example above, isReadOnly == false.

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == firstName.

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getFirstName.

    setMethodName is equal to the setXyz method (built from the get method). In the example above, setMethodName == setFirstName.


    @Lob

    The @Lob specifies that a persistent property or field should be persisted as a large object to a database-supported large object type. A Lob may be either a binary or character type. It maps directly to a TopLink TypeConversionMapping. It can be used in conjunction with a @Column but it is not necessary. See @Column TopLink defaults and equivalent code for more information.


    Annotation specification

    public enum FetchType { LAZY, EAGER };
    public enum LobType { BLOB, CLOB };

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface Lob {
      FetchType fetch() default FetchType.LAZY;
      LobType type() default BLOB;
    }

    Example

    @Lob(fetch=EAGER, type=BLOB)
    @Column(name="IMAGE")
    public Byte[] getImage() {
      return image;
    }

    TopLink defaults and equivalent code

    TypeConversionMapping mapping = new TypeConversionMappingMapping();
    mapping.setFieldName(columnName);
    mapping.setIsReadOnly(isReadOnly);
    mapping.setAttributeName(attributeName);
    mapping.setFieldClassification(fieldClassification);

    Where:

    columnName is extracted from a @Column or its defaults. In the example above, columnName == IMAGE.

    isReadOnly is extracted from a @Column or its defaults. In the example above, isReadOnly == false.

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == image.

    fieldClassification, if the type() annotation member is equal to LobType.BLOB, then it is java.sql.Blob.class. If type() annotation member is equal to LobType.CLOB, it is java.sql.Clob.class.

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getImage.

    setMethodName is equal to the setXyz method (built from the get method). In the example above, setMethodName == setImage.

    @Serialized

    The @Serialized specifies that a persistent property should be persisted as a serialized bytestream. It maps directly to a TopLink DirectToFieldMapping with a SerializedObjectConverter. It can be used in conjunction with a @Column but it is not necessary. See @Column TopLink defaults and equivalent code for more information.


    Annotation specification

    public enum FetchType { LAZY, EAGER };

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface Serialized {
      FetchType fetch() default FetchType.EAGER;
    }

    Example

    @Serialized(fetch=EAGER)
    @Column(name="PICTURE")
    public byte[] getPicture() {
      return picture;
    }

    TopLink defaults and equivalent code

    DirectToFieldMapping mapping = new DirectToFieldMapping();
    mapping.setFieldName(columnName);
    mapping.setIsReadOnly(isReadOnly);
    mapping.setAttributeName(attributeName); mapping.setConverter(new SerializedObjectConverter(mapping));

    Where:

    columnName is extracted from a @Column or its defaults. In the example above, columnName == PICTURE.

    isReadOnly is extracted from a @Column or its defaults. In the example above, isReadOnly == false.

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == picture.

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getPicture.

    setMethodName is equal to the setXyz method (built from the get method). In the example above, setMethodName == setPicture.


    @JoinColumn

    The @JoinColumn is used to specify a mapped column for joining an entity association or a secondary table. The name() annotation member defines the name of the foreign key column. The other annotation members refer to this column and have the same semantics as for the @Column.

    Annotation specification

    @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
    public @interface JoinColumn {
      String name() default "";
      String referencedColumnName() default "";
      boolean primaryKey() default false;
      boolean unique() default false;
      boolean nullable() default true;
      boolean insertable() default true;
      boolean updatable() default true;
      String columnDefinition() default "";
      String secondaryTable() default "";
    }

    Example

    Used within the following annotation contexts. See their TopLink defaults and equivalent code for more information. @SecondaryTable, @OneToOne, @ManyToOne, @OneToMany.


    @OneToOne

    The @OneToOne maps directly to a TopLink OneToOneMapping. It can be used in conjunction with a @JoinColumn but it is not necessary. See @JoinColumn TopLink defaults and equivalent code for more information.

    Annotation specification

    public enum FetchType { LAZY, EAGER };
    public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH };

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface OneToOne {
      String targetEntity() default "";
      CascadeType[] cascade() default {};
      FetchType fetch() default EAGER;
      boolean optional() default true;
      String mappedBy() default "";
      boolean usePKasFK() default false;
    }

    Example

    From: Employee.java

    @OneToOne(cascade=ALL, fetch=LAZY)
    @JoinColumn(name="ADDR_ID")
    public Address getAddress() {
      return address;
    }

    TopLink defaults and equivalent code

    OneToOneMapping mapping = new OneToOneMapping(); mapping.setIsReadOnly(false);
    mapping.setIsPrivateOwned(false);
    mapping.setAttributeName(attributeName);
    mapping.setReferenceClass(referenceClass);
    mapping.setUsesIndirection(usesIndirection);

    Where:

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == address.

    referenceClass is equal to targetEntity(). If it is not defined then referenceClass is extracted from the generic return type if @Entity PROPERTY access. If @Entity FIELD access, it is extracted from the generic field type. In the example above, referenceClass == Address.

    usesIndirection is true when the fetch() annotation member is equal to FetchType.LAZY and false when the fetch() annotation member is equal to FetchType.EAGER.

    For the cascade() annotation member

    mapping.setCascadeAll(true); // when CascadeType.All
    mapping.setCascadeMerge(true); // when CascadeType.MERGE
    mapping.setCascadeCreate(true); // when CascadeType.CREATE
    mapping.setCascadeRefresh(true); // when CascadeType.REFRESH
    mapping.setCascadeRemove(true); // when CascadeType.REMOVE

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getAddress.

    setMethodName is equal to the setXyz method (built from the method being processed). In the example above, setMethodName == setAddress.

    A @JoinColumn is processed for a @OneToOne for the foreign and primary keys.

    mapping.addForeignKeyFieldName(fkColumn, pkColumn);

    Where:

    pkColumn is equal to the @JoinColumn referencedColumnName() annotation member. If it is not specified, the default is the primary key column name of the referenced class. In the example above, pkColumn == EJB_ADDRESS.ADDRESS_ID .

    fkColumn is equal to the @JoinColumn name() annotation member. If it is not specified, the default is the primary key column name of the referenced class. In the example above, fkColumn == EJB_EMPLOYEE.ADDR_ID.


    @ManyToOne

    The @ManyToOne maps directly to a TopLink OneToOneMapping. It can be used in conjunction with a @JoinColumn but it is not necessary. See @JoinColumn TopLink defaults and equivalent code for more information.

    Annotation specification

    public enum FetchType { LAZY, EAGER };
    public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH};

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface ManyToOne {
      String targetEntity() default "";
      CascadeType[] cascade() default {};
      FetchType fetch() default EAGER;
      boolean optional() default true;
    }

    Example

    From: Employee.java

    @ManyToOne(cascade=PERSIST, fetch=LAZY)
    @JoinColumn(name="MANAGER_ID", referencedColumnName="EMP_ID")
    public Employee getManager() {
      return manager;
    }

    TopLink defaults and equivalent code

    OneToOneMapping mapping = new OneToOneMapping();
    mapping.setIsReadOnly(false);
    mapping.setIsPrivateOwned(false);
    mapping.setAttributeName(attributeName);
    mapping.setReferenceClass(referenceClass);
    mapping.setUsesIndirection(usesIndirection);

    Where:

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == manager.

    referenceClass is equal to targetEntity(). If it is not defined then referenceClass is extracted from the generic return type if @Entity PROPERTY access. If @Entity FIELD access, it is extracted from the generic field type. In the example above, referenceClass == Employee.

    usesIndirection is true when the fetch() annotation member is equal to FetchType.LAZY and false when the fetch() annotation member is equal to FetchType.EAGER. For the cascade() annotation member

    mapping.setCascadeAll(true); // when CascadeType.All
    mapping.setCascadeMerge(true); // when CascadeType.MERGE
    mapping.setCascadeCreate(true); // when CascadeType.CREATE
    mapping.setCascadeRefresh(true); // when CascadeType.REFRESH
    mapping.setCascadeRemove(true); // when CascadeType.REMOVE

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getManager.

    setMethodName is equal to the setXyz method (built from the method being processed). In the example above, setMethodName == setManager.

    A @JoinColumn is processed for a @ManyToOne for the foreign and primary keys.

    mapping.addForeignKeyFieldName(fkColumn, pkColumn);

    Where:

    pkColumn is equal to the @JoinColumn referencedColumnName() annotation member. If it is not specified, the default is the primary key column name of the referenced class. In the example above, pkColumn == EJB_EMPLOYEE.EMP_ID .

    fkColumn is equal to the @JoinColumn name() annotation member. If it is not specified, the default is the primary key column name of the referenced class. In the example above, fkColumn == EJB_EMPLOYEE.MANAGER_ID.


    @OneToMany

    The @OneToMany maps directly to a TopLink OneToManyMapping. It can be used in conjunction with a @JoinColumn but it is not necessary. See @JoinColumn TopLink defaults and equivalent code for more information.

    Annotation specification

    public enum FetchType { LAZY, EAGER };
    public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH};

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface OneToMany {
      String targetEntity() default "";
      CascadeType[] cascade() default {};
      FetchType fetch() default LAZY;
      String mappedBy() default "";
    }

    Example

    From: Employee.java

    @OneToMany(cascade=PERSIST)
    @JoinColumn(name="MANAGER_ID", referencedColumnName="EMP_ID")
    public Collection getManagedEmployees() {
      return managedEmployees;
    }

    TopLink defaults and equivalent code

    OneToManyMapping mapping = new OneToManyMapping();
    mapping.setIsReadOnly(false);
    mapping.setIsPrivateOwned(false);
    mapping.setAttributeName(attributeName);
    mapping.setReferenceClass(referenceClass);

    Where:

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == address.

    referenceClass is equal to targetEntity(). If it is not defined then referenceClass is extracted from the generic return type if @Entity PROPERTY access. If @Entity FIELD access, it is extracted from the generic field type. In the example above, referenceClass == Address. For the cascade() annotation member

    mapping.setCascadeAll(true); // when CascadeType.All
    mapping.setCascadeMerge(true); // when CascadeType.MERGE
    mapping.setCascadeCreate(true); // when CascadeType.CREATE
    mapping.setCascadeRefresh(true); // when CascadeType.REFRESH
    mapping.setCascadeRemove(true); // when CascadeType.REMOVE

    For the fetch() annotation member

    mapping.dontUseIndirection(); // when Fetch.EAGER mapping.useTransparentCollection(); // when FetchType.LAZY

    If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getAddress.

    setMethodName is equal to the setXyz method (built from the method being processed). In the example above, setMethodName == setAddress.

    A @JoinColumn is processed for a @ToOneMany for the foreign and primary keys.

    mapping.addTargetForeignKeyFieldName(fkColumn, pkColumn);

    Where:

    pkColumn is equal to the @JoinColumn referencedColumnName() annotation member. If it is not specified, the default is the primary key column name of the source class. In the example above, pkColumn == EJB_EMPLOYEE.EMP_ID .

    fkColumn is equal to the @JoinColumn name() annotation member. If it is not specified, the default is the primary key column name of the source class. In the example above, fkColumn == EJB_EMPLOYEE.MANAGER_ID.


    @ManyToMany

    The @ManyToMany maps directly to a TopLink ManyToManyMapping. It can be used in conjunction with an @AssociationTable but it is not necessary (see TopLink defaults and equivalent code below).

    Annotation specification

    public enum FetchType { LAZY, EAGER };
    public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH};

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface ManyToMany {
      String targetEntity() default "";
      CascadeType[] cascade() default {};
      FetchType fetch() default LAZY;
      String mappedBy() default "";
    }

    Example

    From: Employee.java

    @ManyToMany(cascade=PERSIST)
    @AssociationTable(
      table=@Table(name="EJB_PROJ_EMp),
      joinColumns=@JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID"),
      inverseJoinColumns=@JoinColumn(name="PROJ_ID", referencedColumnName="PROJ_ID")
    )

    public Collection getProjects() {
      return projects;
    }

    TopLink defaults and equivalent code

    ManyToManyMapping mapping = new ManyToManyMapping();
    mapping.setIsPrivateOwned(false);
    mapping.setAttributeName(attributeName);
    mapping.setReferenceClass(referenceClass);

    Where:

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == projects.

    referenceClass is equal to targetEntity(). If it is not defined then referenceClass is extracted from the generic return type if @Entity PROPERTY access. If @Entity FIELD access, it is extracted from the generic field type. In the example above, referenceClass == Project. For the cascade() annotation member

    mapping.setCascadeAll(true); // when CascadeType.All
    mapping.setCascadeMerge(true); // when CascadeType.MERGE
    mapping.setCascadeCreate(true); // when CascadeType.CREATE
    mapping.setCascadeRefresh(true); // when CascadeType.REFRESH
    mapping.setCascadeRemove(true); // when CascadeType.REMOVE

    For the fetch() annotation member

    mapping.dontUseIndirection(); // when Fetch.EAGER
    mapping.useTransparentCollection(); // when FetchType.LAZY

    For the mappedBy() annotation member. If it is set, it used to indicate that this is the non-owning side of the relationship. The source and target foreign keys are then processed from the owning side and no @AssociationTable is needed.

    mapping.setIsReadOnly(true);

    If this is the owning side of the relationship, then an AssociationTable is processed. See @AssociationTable for more information.


    @AssociationTable

    The @AssociationTable is normally specified by one side of a many-to-many association.

    Annotation specification

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface AssociationTable {
      Table table() default @Table(specified=false);
      JoinColumn[] joinColumns() default {};
      JoinColumn[] inverseJoinColumns() default {};
    }

    Example

    From: Employee.java

    @ManyToMany(cascade=PERSIST)
    @AssociationTable(
      table=@Table(name="EJB_PROJ_EMp),
      joinColumns=@JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID"),
      inverseJoinColumns=@JoinColumn(name="PROJ_ID", referencedColumnName="PROJ_ID")
    )

    public Collection getProjects() {
      return projects;
    }

    TopLink defaults and equivalent code

    mapping.addSourceRelationKeyFieldName(sourceFKColumn, sourcePKColumn); mapping.addTargetRelationKeyFieldName(targetFKColumn, targetPKColumn);

    Where:

    sourcePKColumn is extracted from the joinColumns annotation member, which is an array of @JoinColumn.
    sourcePKColumn == @JoinColumn referenceColumnName() annotation member. If it is not specified, the default is the primary key column name of the source class. In the example above, sourcePKColumn == EJB_EMPLOYEE.EMP_ID .

    sourceFKColumn is extracted from the inverseJoinColumns() annotation member, which is an array of @JoinColumn.
    sourceFKColumn == @JoinColumn name() annotation member. If it is not specified, the default is the primary key column name of the source class. In the example above, sourcePKColumn == EJB_PROJ_EMP.EMP_ID .

    targetPKColumn is extracted from the inverseJoinColumns annotation member, which is an array of @JoinColumn.
    targetPKColumn == @JoinColumn referenceColumnName() annotation member. If it is not specified, the default is the primary key column name of the target class. In the example above, targetPKColumn == EJB_PROJECT.PROJ_ID .

    targetFKColumn is extracted from the inverseJoinColumns() annotation member, which is an array of @JoinColumn.
    targetFKColumn == @JoinColumn name() annotation member. If it is not specified, the default is the primary key column name of the target class. In the example above, targetPKColumn == EJB_PROJ_EMP.PROJ_ID .

    If the @AssociationTable is missing, the default values of the annotation members apply. The name of the association table is assumed to be the table names of the associated primary tables concatenated together using an underscore.


    @Embedded

    The @Embedded maps directly to a TopLink AggregateObjectMapping. It may be used in an entity bean class when it is using a shared embeddable class. The entity may override the column mappings declared within the embeddable class to apply to its own entity table.

    Annotation specification

    @Target({METHOD, FIELD}) @Retention(RUNTIME)
    public @interface Embedded {
      AttributeOverride[] value() default {};
    }

    Example

    From: Employee.java

    @Embedded
    public EmploymentPeriod getPeriod() {
      return period;
    }

    TopLink defaults and equivalent code

    AggregateObjectMapping mapping = new AggregateObjectMapping();
    mapping.setIsReadOnly(false);
    mapping.setIsNullAllowed(true);
    mapping.setAttributeName(attributeName);
    mapping.setReferenceClass(referenceClass);

    Where:

    attributeName, if @Entity PROPERTY access, is extracted from the getXyz method name into an xyz attribute name. If @Entity FIELD accesss, then it equals the field name. In the example above, attributeName == firstName.

    referenceClass is extracted from a the return type if @Entity PROPERTY access. If @Entity FIELD access, then it is extracted from the field type. In the example above, the referenceClass == EmploymentPeriod. If @Entity PROPERTY access then the getter and setter access methods are set on the mapping.

    mapping.setGetMethodName(getMethodName);
    mapping.setSetMethodName(setMethodName);

    Where:

    getMethodName is equal to the getXyz method being processed. In the example above, getMethodName == getPeriod.

    setMethodName is equal to the setXyz method (built from the get method). In the example above, setMethodName == setPeriod.

    The @AttributeOverride value() annotation member is processed into TopLink field name translations. See @AttributeOverride for more information.


    @AttributeOverride

    The @AttributeOverride is used to specify TopLink field name translations for a TopLink AggregateObjectMapping. See @Embedded for more information.

    Annotation specification

    @Target({}) @Retention(RUNTIME)
    public @interface AttributeOverride {
      String name();
      Column[] column() default {};
    }

    Example

    @Embedded({
      @AttributeOverride(name="startDate", column=@Column("EMP_START")),
      @AttributeOverride(name="endDate", column=@Column("EMP_END"))
    })
    public EmploymentPeriod getPeriod() {
      return period;
    }

    TopLink defaults and equivalent code

    mapping.addFieldNameTranslation(columnName, name);

    The above example would generate the following code:

    mapping.addFieldNameTranslation("startDate", "EMP_START"); mapping.addFieldNameTranslation("endDate", "EMP_END");

    If an @AttributeOverride is defined on an @Embedded, it must define columns otherwise an exception is thrown.

    If no @AttributeOverride is defined, then TopLink will default a field name translation for every column (direct to field mapping) in the @Embeddable class.

    @Embeddable mapping

    The @Embeddable specifies that this class is an intrinsic part of an owning entity and shares the identity of that entity. The embeddable class is then processed for @Basic, @Table, @Column, and @Serialized mapping annotations.

    Annotation specification

    @Target({TYPE}) @Retention(RUNTIME)
    public @interface Embeddable {
      AccessType access() default PROPERTY;
    }

    Example

    From: EmploymentPeriod.java

    @Embeddable
    @Table(name="EJB_EMPLOYEE")
    public class EmploymentPeriod implements Serializable {
      private Date startDate;
      private Date endDate;

      public EmploymentPeriod() {
      }

      ...
    }

    TopLink defaults and equivalent code

    descriptor.descriptorIsAggregate();


    Sequencing annotations

    @GeneratedIdTable

    The @GeneratedIdTable defines a table that may be used by the container to store generated id values for entities. It's usually shared by multiple entity types that use table-based id generation. Each entity type will typically use its own row in the table to generate the id values for that entity class. The id values are positive integers.

    Annotation specification

    @Target({PACKAGE, TYPE}) @Retention(RUNTIME)
    public @interface GeneratedIdTable {
      String name() default "";
      Table table() default @Table(specified=false);
      String pkColumnName() default "";
      String valueColumnName() default "";
    }

    Example

    From: Employee.java

    @Entity
    @Table(name="EJB_EMPLOYEE")
    @SecondaryTable(name="EJB_SALARY")
    @JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID")
    @GeneratedIdTable(name="EMPLOYEE_GENERATOR_TABLE", table=@Table(name="EJB_EMPLOYEE_SEQ"), pkColumnName="SEQ_NAME", valueColumnName="SEQ_COUNT")
    @NamedQuery (
      name="findAllEmployeesByFirstName",
      queryString="SELECT OBJECT(employee) FROM Employee employee WHERE employee.firstName = :firstname"
    )
    public class Employee implements Serializable {
       ...
    }

    TopLink defaults and equivalent code

    No TopLink code is generated. @GeneratedIdTable are stored and used within @TableGenerator


    @TableGenerator

    The @TableGenerator defines a primary key or id generator which may be referenced by name when annotating the id attribute (see @Id). A generator may be defined at either the package, class, method, or field level. The level at which it is defined will depend upon the desired visibility and sharing of the generator. No scoping or visibility rules are actually enforced. However, it is good practice to define the generator at the level for which it will be used. It maps to a TopLink TableSequence.


    Annotation specification

    @Target({PACKAGE, TYPE, METHOD, FIELD}) @Retention(RUNTIME)
    public @interface TableGenerator {
      String name();
      String tableName() default "";
      String pkColumnValue() default "";
      int allocationSize() default 50;
    }

    Example

    From: Address.java

    @Id(generate=TABLE, generator="ADDRESS_TABLE_GENERATOR")
    @TableGenerator(name="ADDRESS_TABLE_GENERATOR", tableName="EMPLOYEE_GENERATOR_TABLE", pkColumnValue="ADDRESS_SEQ")
    @Column(name="ADDRESS_ID", primaryKey=true)
    public Integer getId() {
      return id;
    }

    TopLink defaults and equivalent code

    TableSequence sequence = new TableSequence(sequenceName, allocationSize);
    sequence.setTableName(tableName);
    sequence.setNameFieldName(pkColumnName);
    sequence.setCounterFieldName(valueColumnName);

    Where:

    sequenceName is equal to the pkColumnValue() annotation member. If the pkColumnValue() annotation member is not specified then it is equal to the TableGenerator name() annotation member.

    allocationSize is equal to the TableGenerator allocationSize() annotation member.

    tableName is equal to the @GeneratedIdTable table() annotation member if it is specified as follows, table.schema().table.catalog().table.name(). If the table() annotation member is not specified then it is equal to @TableGenerator name() annotation member.

    pkColumnName is equal to the @GeneratedIdTable pkColumnName() annotation member. The @GeneratedIdTable is looked up from the TableGenerator tableName() annotation member.

    valueColumnName is equal to the @GeneratedIdTable valuleColumnName() annotation member. The @GeneratedIdTable is looked up from the TableGenerator tableName() annotation member.


    @SequenceGenerator

    The @SequenceGenerator defines a primary key or id generator which may be referenced by name when annotating the id attribute (see @Id). A generator may be defined at either the package, class, method, or field level. The level at which it is defined will depend upon the desired visibility and sharing of the generator. No scoping or visibility rules are actually enforced. However, it is good practice to define the generator at the level for which it will be used. It maps to a TopLink NativeSequence.

    Annotation specification

    @Target({PACKAGE, TYPE, METHOD, FIELD}) @Retention(RUNTIME)
    public @interface SequenceGenerator {
      String name();
      String sequenceName() default "";
      int initialValue() default 0;
      int allocationSize() default 50;
    }

    Example

    @Id(generate=TABLE, generator="ADDRESS_TABLE_GENERATOR")
    @SequenceGenerator(name="ADDRESS_TABLE_GENERATOR", sequenceName="ADDRESS_SEQ")
    @Column(name="ADDRESS_ID", primaryKey=true)
    public Integer getId() {
      return id;
    }

    TopLink defaults and equivalent code

    NativeSequence sequence = new NativeSequence(sequenceName, allocationSize);

    Where:

    sequenceName is equal to the sequenceName annotation member. If the sequenceName() annotation member is not specified then it is equal to the SequenceGenerator name() annotation member.

    allocationSize is equal to the SequenceGenerator allocationSize() annotation member.


    Callback events

    @PrePersist

    The @PrePersist is used to register a method to be called on an object when that object has the create operation applied to it.

    Annotation specification

    @Target({METHOD}) @Retention(RUNTIME)
    public @interface PrePersist {}

    Example

    @PrePersist
    public int initialize() {
      ...
    }

    TopLink equivalent code

    descriptor.getEventManager().setPrePersistSelector("initialize");


    @PostPersist

    The @PostPersist is used to register a method to be called on an object that has just been inserted into the database. This event can be used to notify any dependent on the object, or to update information not accessible until the object has been inserted.

    Annotation specification

    @Target({METHOD}) @Retention(RUNTIME)
    public @interface PostPersist {}

    Example

    @PostPersist
    public int cleanUp() {
      ...
    }

    TopLink defaults and equivalent code

    descriptor.getEventManager().setPostInsertSelector("cleanUp);


    @PreRemove

    The @PreRemove is used to register a method to be called on an object when that object has the remove operation applied to it.

    Annotation specification

    @Target({METHOD}) @Retention(RUNTIME)
    public @interface PreRemove {}

    Example

    @PreRemove
    public int finalizeOperation() {
      ...
    }

    TopLink equivalent code

    descriptor.getEventManager().setPreRemoveSelector("finalizeOperation");


    @PostRemove

    The @PostRemove is used to register a method to be called on an object that has just been deleted from the database. This event can notify/remove any dependents on the object.

    Annotation specification

    @Target({METHOD}) @Retention(RUNTIME)
    public @interface PostRemove {}

    Example

    @PostRemove
    public int shutdownMaintenanceServer() {
      ...
    }

    TopLink equivalent code

    descriptor.getEventManager().setPostDeleteSelector("shutdownMaintenanceServer");


    @PreUpdate

    The @PreUpdate is used to register a method to be called when an object's row it about to be updated. TopLink uses the optional event argument of the DatabaseRow. This is different from pre/postUpdate because it occurs after the row has already been built, and it is ONLY called if the update is required (changed within a unit of work), as the other occur ALWAYS. This event can be used to modify the row before insert, such as adding a user inserted by.

    Annotation specification

    @Target({METHOD}) @Retention(RUNTIME) public @interface PreUpdate {}