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