Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/Embeddable"

(Relationships)
(Inheritance)
 
(15 intermediate revisions by the same user not shown)
Line 8: Line 8:
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/Embeddable.html @Embeddable]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/Embeddable.html @Embeddable]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/Embedded.html @Embedded]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/Embedded.html @Embedded]
 +
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/ElementCollection.html @ElementCollection]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AttributeOverride.html @AttributeOverride]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AttributeOverride.html @AttributeOverride]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AttributeOverrides.html @AttributeOverrides]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AttributeOverrides.html @AttributeOverrides]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AssociationOverride.html @AssociationOverride]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AssociationOverride.html @AssociationOverride]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AssociationOverrides.html @AssociationOverrides]
 
* [http://www.eclipse.org/eclipselink/api/latest/javax/persistence/AssociationOverrides.html @AssociationOverrides]
 +
|nativeapi=y
 +
|nativeapis=
 +
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/mappings/AggregateObjectMapping.html AggregateObjectMapping]
 
}}
 
}}
  
Line 23: Line 27:
 
Embeddables do not currently support the <tt>@Inheritance</tt> annotation, but inheritance is supported in EclipseLink through usage of a <tt>DescriptorCustomizer</tt>.
 
Embeddables do not currently support the <tt>@Inheritance</tt> annotation, but inheritance is supported in EclipseLink through usage of a <tt>DescriptorCustomizer</tt>.
  
An embeddable cannot define a <tt>@Table</tt> annotation, and the columns used in its mappings are be default the names of the column is its parent's table.  If the embeddable class is shared with multiple parents, then each parent can override the column names using the <tt>@AttributeOverride</tt> and <tt>@AssociationOverride</tt> annotations.
+
An embeddable cannot define a <tt>@Table</tt> annotation, and the columns used in its mappings are by default the names of the column is its parent's table.  If the embeddable class is shared with multiple parents, then each parent can override the column names using the <tt>@AttributeOverride</tt> and <tt>@AssociationOverride</tt> annotations.
  
 
The <tt>@Embeddable</tt> annotation does not have any attributes.
 
The <tt>@Embeddable</tt> annotation does not have any attributes.
Line 89: Line 93:
 
==Advanced Embeddable Configuration==
 
==Advanced Embeddable Configuration==
 
===Sharing===
 
===Sharing===
A embeddable class can be shared by more than one class, or even referenced twice in the same class.  Since an embeddable does not define a table, this means the class will stored in multiple different tables.  As long as all of the table that stored the embeddable have its columns this is fine.  If any of the table have different columns names, this must be handle through using <code>@AttributeOverride</code> and <code>@AssociationOverride</code>.
+
A embeddable class can be shared by more than one class, or even referenced twice in the same class.  Since an embeddable does not define a table, this means the class will be stored in multiple different tables.  As long as all of the tables that stored the embeddable have its columns this is fine.  If any of the table have different columns names, this must be handle through using <code>@AttributeOverride</code> and <code>@AssociationOverride</code>.
  
 
Note that the same embeddable object instance should never be shared, each parent should have its own object instance as the objects are always stored in their parents table.  Only the class can be shared.
 
Note that the same embeddable object instance should never be shared, each parent should have its own object instance as the objects are always stored in their parents table.  Only the class can be shared.
Line 196: Line 200:
 
Relationships to another object's embeddable are not allowed.
 
Relationships to another object's embeddable are not allowed.
  
======''Example: Shared embeddable''======
+
There is no standard way for an embeddable to reference its parent entity.  The easiest way to implement this is to use property access to set the embedded object on the entity and in the set method set the back reference from the embeddable to its parent.  The parent would be marked as <code>@Transient</code> as it is not a persistent relationship.
 +
 
 +
======''Example: Embeddable with relationships''======
  
 
<source lang="java">
 
<source lang="java">
Line 218: Line 224:
 
     private String name;
 
     private String name;
 
     @Embedded
 
     @Embedded
     private ProjectInfo projectStatus;
+
     private ProjectInfo projectInfo;
 
     ...
 
     ...
 
}
 
}
Line 231: Line 237:
 
     @Basic
 
     @Basic
 
     private String name;
 
     private String name;
     @ManyToMany(mappedBy="projectStatus.projects")
+
     @ManyToMany(mappedBy="projectInfo.projects")
 
     private List<Employee> employees;
 
     private List<Employee> employees;
 
     ...
 
     ...
Line 238: Line 244:
  
 
===Nesting===
 
===Nesting===
 +
An embeddable object can be embedded inside another embeddable object.  All of the columns will be aggregated into the parent entity's table.
 +
 +
If an entity needs to override the column names for a nested embeddable the dot notation can be used inside the <code>@AttributeOverride</code>.
 +
 +
Nested embeddables cannot contain embedded references to the same embeddable class or cycles, as there would be no way to store this in a relational table.
 +
 +
======''Example: Nested embeddable''======
 +
 +
<source lang="java">
 +
@Embeddable
 +
public class ProjectInfo implements Serializable {
 +
    @Basic
 +
    private String name;
 +
    @Basic
 +
    private BigDecimal budget;
 +
    @Embedded
 +
    private ProjectStatus status;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Embeddable
 +
public class ProjectStatus implements Serializable {
 +
    @Basic
 +
    private String status;
 +
    @Basic
 +
    private boolean isBehindSchedule;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
public class Project implements Serializable {
 +
    @Id
 +
    private long id;
 +
    @Embedded
 +
    @AttributeOverride(name="budget", column=@Column(name="PROJECT_BUDGET"))
 +
    @AttributeOverride(name="status.isBehindSchedule", column=@Column(name="BEHIND_SCHEDULE"))
 +
    private ProjectInfo info;
 +
    ...
 +
}
 +
</source>
  
 
===Inheritance===
 
===Inheritance===
 +
The JPA specification does not define inheritance for embeddables.  EclipseLink does not currently support using annotations or XML to define inheritance with embeddables, but EclipseLink does support inheritance with embeddables through the native API.
 +
 +
To define inheritance with a hierarchy of embedded classes a <code>DescriptorCustomizer</code> and <code>SessionCustomizer</code> can be used.  The customizers can be used to define a discriminator column (class indicator field) in the root embeddable class' descriptor.  The child descriptors can either be defined using a <code>SessionCustomizer</code>, or use a <code>DescriptorCustomizer</code> to set their parent class.
 +
 +
======''Example: Using a DescriptorCustomizer to enable inheritance in an embeddable''======
 +
<source lang="java">
 +
public class StatusCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        descriptor.getInheritancePolicy().setClassIndicatorFieldName("STATUS_TYPE");
 +
        descriptor.getInheritancePolicy().addClassIndicator(RetiredStatus.class, "R");
 +
        descriptor.getInheritancePolicy().addClassIndicator(TerminatedStatus.class, "T");
 +
        descriptor.getInheritancePolicy().addClassIndicator(ActiveStatus.class, "A");
 +
        descriptor.getInheritancePolicy().addClassIndicator(UnActiveStatus.class, "U");
 +
    }
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
public class ActiveStatusCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        descriptor.getInheritancePolicy().setParentClass(Status.class);
 +
    }
 +
}
 +
</source>
  
 
===Embedded Ids===
 
===Embedded Ids===
Line 245: Line 319:
  
 
===Nullable embedded values===
 
===Nullable embedded values===
 +
In Java the value of an embedded reference could be <code>null</code>, but since only the attributes of the embeddable are stored into columns in the database table, there is no simple way to represent <code>null</code> in the database.  One way of representing <code>null</code> is to assume that if every column value in the database is <code>null</code>, then this means that the reference to the embeddable is <code>null</code>.  However, this means that an embeddable object with all <code>null</code>s cannot be represented.
 +
 +
EclipseLink allows you to configure each embedded mapping to either not allow a <code>null</code> reference, or to map <code>null</code> to having each column value be <code>null</code>.  The default is to map <code>null</code> to <code>null</code> column values, unless the embeddable has relationships, then <code>null</code> is not allowed.
 +
 +
The <code>allowNull</code> option cannot currently be configure through annotations or XML, but a <code>DescriptorCustomizer</code> can be used.
 +
 +
======''Example: Using a DescriptorCustomizer to disallow a null embedded value''======
 +
<source lang="java">
 +
public class EmployeeCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        ((AggregateObjectMapping)descriptor.getMappingForAttributeName("status")).setIsNullAllowed(false);
 +
    }
 +
}
 +
</source>
  
 
===Querying===
 
===Querying===

Latest revision as of 15:37, 25 October 2011

EclipseLink JPA

@Embeddable

You can use the @Embeddable annotation or <embeddable> XML element to map an embedded class. An embeddable is a special type of class that is not directly persistent, but persisted only with its parent entity. An embeddable has no persistent identity, and no Id mapping. An embeddable can contain any type of attribute mapping including @Basic, @OneToOne, @ManyToOne, @OneToMany, @ManyToMany, @ElementCollection or another @Embedded. Embeddables cannot contain an @Id mapping.

An embeddable can be used to define an entities Id, normally in the case of a composite id. The id attributes in the embeddable will be mapped as @Basic not as @Id, as all attributes are required to be part of the @Id. The parent entity will use the @EmbeddedId to map the embeddeable instead of the @Embedded mapping.

An embeddable can be referenced from an entity or another embeddable using the @Embedded annotation for a single reference, or the @ElementCollection annotation for a Collection or Map reference. An embeddable can also be used in any Map key using the @MapKeyClass annotation.

Embeddables do not currently support the @Inheritance annotation, but inheritance is supported in EclipseLink through usage of a DescriptorCustomizer.

An embeddable cannot define a @Table annotation, and the columns used in its mappings are by default the names of the column is its parent's table. If the embeddable class is shared with multiple parents, then each parent can override the column names using the @AttributeOverride and @AssociationOverride annotations.

The @Embeddable annotation does not have any attributes.

Elug javaspec icon.gif

For more information, see Section 2.5 "Embeddable Classes" in the JPA Specification.

Elug javaspec icon.gif

For more information, see Section 11.1.13 "Embeddable Annotation" in the JPA Specification.

The following example shows usage of an embbeddable.

Example: Using @Embeddable annotation
@Embeddable 
public class EmployeementStatus implements Serializable {
    @Basic
    private String status;
    @Basic
    private String level;
    @Basic
    private java.sql.Date startDate;
    @Basic
    private java.sql.Date endDate;
    ...
}
@Entity
@Table(name="EMP")
public class Employee implements Serializable {
    @Id
    private Long id;
    @Basic
    private String name;
    @Embedded
    private EmployeementStatus status;
    ...
}
Example: Using <embeddable> XML
<entity class="Employee">
    <table name="EMP">
    <attributes>
        <id name="id"/>
        <basic name="name"/>
        <embeddable name="status"/>
        ...
    </attributes>
</entity>
<embeddable class="EmployeementStatus">
    <attributes>
        <basic name="status"/>
        <basic name="level"/>
        <basic name="startDate"/>
        <basic name="endDate"/>
    </attributes>
</embeddable >


Advanced Embeddable Configuration

Sharing

A embeddable class can be shared by more than one class, or even referenced twice in the same class. Since an embeddable does not define a table, this means the class will be stored in multiple different tables. As long as all of the tables that stored the embeddable have its columns this is fine. If any of the table have different columns names, this must be handle through using @AttributeOverride and @AssociationOverride.

Note that the same embeddable object instance should never be shared, each parent should have its own object instance as the objects are always stored in their parents table. Only the class can be shared.

@AttributeOverride

You can use the @AttributeOverride and @AttributeOverrides annotations, or <attribute-override> XML element to override the column for a basic attribute in an embedded relationship mapping. This allows for the column name to be different if the embeddable class is used by more than entity or if an entity defines multiple embedded relationships.

@AttributeOverride Attributes
Attribute Description Default Required?
name The name of the attribute. Yes
column The column in the source table for the embedded relationship. column defined in embeddable No
Elug javaspec icon.gif

For more information, see Section 11.1.4 "AttributeOverride Annotation" in the JPA Specification.

@AssociationOverride

You can use the @AssociationOverride and @AssociationOverrides annotations, or <association-override> XML element to override the join column or join table for a relationship attribute in an embedded relationship mapping. This allows for the join column name to be different if the embeddable class is used by more than entity or if an entity defines multiple embedded relationships.

@AssociationOverrideAttributes
Attribute Description Default Required?
name The name of the attribute. Yes
joinColumn The join column in the source table for the embedded relationship. join column defined in embeddable No
joinTable The join table for the subclass. join table defined in embeddable No
Elug javaspec icon.gif

For more information, see Section 11.1.2 "AssociationOverride Annotation" in the JPA Specification.

Example: Shared embeddable
@Embeddable 
public class Status implements Serializable {
    @Basic
    private String status;
    @Basic
    private String level;
    @Basic
    private java.sql.Date startDate;
    @Basic
    private java.sql.Date endDate;
    ...
}
@Entity
@Table(name="EMP")
public class Employee implements Serializable {
    @Id
    private Long id;
    @Basic
    private String name;
    @Embedded
    @AttributeOverrides({
      @AttributeOverride(name="level", column=@Column(name="EMPLOYMENT_LEVEL"))
      @AttributeOverride(name="status", column=@Column(name="EMPLOYMENT_STATUS"))})
    private Status status;
    ...
}
@Entity
@Table(name="PROJ")
public class Project implements Serializable {
    @Id
    private Long id;
    @Basic
    private String name;
    @Embedded
    @AttributeOverrides({
      @AttributeOverride(name="level", column=@Column(name="PROJECT_LEVEL"))
      @AttributeOverride(name="status", column=@Column(name="PROJECT_STATUS"))})
    private Status status;
    ...
}

Relationships

Embeddable objects can define relationships. Any relationship type can be used including a nested embedded relationship.

For bi-directional relationships, the target object's inverse relationship must be back to the parent of the embeddable. The mappedBy can either reference the embeddable attribute directly, or use the dot notation from the parent class. Relationships to another object's embeddable are not allowed.

There is no standard way for an embeddable to reference its parent entity. The easiest way to implement this is to use property access to set the embedded object on the entity and in the set method set the back reference from the embeddable to its parent. The parent would be marked as @Transient as it is not a persistent relationship.

Example: Embeddable with relationships
@Embeddable 
public class ProjectInfo implements Serializable {
    @Basic
    private boolean available;
    @ManyToMany
    private List<Project> projects;
    ...
}
@Entity
@Table(name="EMP")
public class Employee implements Serializable {
    @Id
    private Long id;
    @Basic
    private String name;
    @Embedded
    private ProjectInfo projectInfo;
    ...
}
@Entity
@Table(name="PROJ")
public class Project implements Serializable {
    @Id
    private Long id;
    @Basic
    private String name;
    @ManyToMany(mappedBy="projectInfo.projects")
    private List<Employee> employees;
    ...
}

Nesting

An embeddable object can be embedded inside another embeddable object. All of the columns will be aggregated into the parent entity's table.

If an entity needs to override the column names for a nested embeddable the dot notation can be used inside the @AttributeOverride.

Nested embeddables cannot contain embedded references to the same embeddable class or cycles, as there would be no way to store this in a relational table.

Example: Nested embeddable
@Embeddable 
public class ProjectInfo implements Serializable {
    @Basic
    private String name;
    @Basic
    private BigDecimal budget;
    @Embedded
    private ProjectStatus status;
    ...
}
@Embeddable 
public class ProjectStatus implements Serializable {
    @Basic
    private String status;
    @Basic
    private boolean isBehindSchedule;
    ...
}
@Entity
public class Project implements Serializable {
    @Id
    private long id;
    @Embedded
    @AttributeOverride(name="budget", column=@Column(name="PROJECT_BUDGET"))
    @AttributeOverride(name="status.isBehindSchedule", column=@Column(name="BEHIND_SCHEDULE"))
    private ProjectInfo info;
    ...
}

Inheritance

The JPA specification does not define inheritance for embeddables. EclipseLink does not currently support using annotations or XML to define inheritance with embeddables, but EclipseLink does support inheritance with embeddables through the native API.

To define inheritance with a hierarchy of embedded classes a DescriptorCustomizer and SessionCustomizer can be used. The customizers can be used to define a discriminator column (class indicator field) in the root embeddable class' descriptor. The child descriptors can either be defined using a SessionCustomizer, or use a DescriptorCustomizer to set their parent class.

Example: Using a DescriptorCustomizer to enable inheritance in an embeddable
public class StatusCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        descriptor.getInheritancePolicy().setClassIndicatorFieldName("STATUS_TYPE");
        descriptor.getInheritancePolicy().addClassIndicator(RetiredStatus.class, "R");
        descriptor.getInheritancePolicy().addClassIndicator(TerminatedStatus.class, "T");
        descriptor.getInheritancePolicy().addClassIndicator(ActiveStatus.class, "A");
        descriptor.getInheritancePolicy().addClassIndicator(UnActiveStatus.class, "U");
    }
}
public class ActiveStatusCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        descriptor.getInheritancePolicy().setParentClass(Status.class);
    }
}

Embedded Ids

See, @EmbeddedId.

Nullable embedded values

In Java the value of an embedded reference could be null, but since only the attributes of the embeddable are stored into columns in the database table, there is no simple way to represent null in the database. One way of representing null is to assume that if every column value in the database is null, then this means that the reference to the embeddable is null. However, this means that an embeddable object with all nulls cannot be represented.

EclipseLink allows you to configure each embedded mapping to either not allow a null reference, or to map null to having each column value be null. The default is to map null to null column values, unless the embeddable has relationships, then null is not allowed.

The allowNull option cannot currently be configure through annotations or XML, but a DescriptorCustomizer can be used.

Example: Using a DescriptorCustomizer to disallow a null embedded value
public class EmployeeCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        ((AggregateObjectMapping)descriptor.getMappingForAttributeName("status")).setIsNullAllowed(false);
    }
}

Querying

Embeddable objects cannot be queried directly, but they can be queried in the context of their parent. Typically it is best to select the parent, and access the embeddable from the parent. This will ensure the embeddable is registered with the persistence context. If the embeddable is selected in a query, the resulting objects will be detached, and changes will not be tracked.

Example: Querying an embeddable
SELECT employee.period FROM Employee employee WHERE employee.period.endDate = :param

Eclipselink-logo.gif
Version: 2.2.0 DRAFT
Other versions...