Skip to main content

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

Jump to: navigation, search

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

(Example: Using JOINED with mixed inheritance)
 
(40 intermediate revisions by the same user not shown)
Line 16: Line 16:
 
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/descriptors/ClassExtractor.html ClassExtractor]
 
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/descriptors/ClassExtractor.html ClassExtractor]
 
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/expressions/Expression.html Expression]
 
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/expressions/Expression.html Expression]
 +
|examples=y
 +
|example=
 +
* [[EclipseLink/Examples/JPA/Inheritance|How to Define Inheritance]]
 +
* [[EclipseLink/Examples/JPA/Inheritance#Inheritance_Examples|Advanced Inheritance Examples]]
 
}}
 
}}
  
Line 21: Line 25:
 
You can use the <tt>@Inheritance</tt> annotation or <code><nowiki><inheritance></nowiki></code> XML element to configure how entities with inheritance are persisted.   
 
You can use the <tt>@Inheritance</tt> annotation or <code><nowiki><inheritance></nowiki></code> XML element to configure how entities with inheritance are persisted.   
 
JPA defines three inheritance strategies <code>SINGLE_TABLE</code>, <code>JOINED</code>, and <code>TABLE_PER_CLASS</code>.  By default the <code>SINGLE_TABLE</code> strategy is used and all of the subclasses are persisted in a single table that contains all of the column of all of the subclasses.  In addition a discriminator column named <code>DTYPE</code> is required in the table to store the class type.
 
JPA defines three inheritance strategies <code>SINGLE_TABLE</code>, <code>JOINED</code>, and <code>TABLE_PER_CLASS</code>.  By default the <code>SINGLE_TABLE</code> strategy is used and all of the subclasses are persisted in a single table that contains all of the column of all of the subclasses.  In addition a discriminator column named <code>DTYPE</code> is required in the table to store the class type.
 +
 +
For other inheritance strategies see [[#Advanced Inheritance Configuration|Advanced Inheritance Configuration]].
  
 
The <tt>@Inheritance</tt> annotation has the following attributes:
 
The <tt>@Inheritance</tt> annotation has the following attributes:
* <tt>strategy</tt> – By default, the EclipseLink persistence provider assumes that all the classes in a hierarchy are mapped to a single table differentiated by the discriminator value (see [[#@DiscriminatorValue|@DiscriminatorValue]]) in the table's discriminator column (see [[#@DiscriminatorColumn|@DiscriminatorColumn]]): <tt>InheritanceType.SINGLE_TABLE</tt>.<br>If this is not appropriate for your application or if you must match an existing data model, set <tt>strategy</tt> to the desired <tt>InheritanceType</tt> enumerated type:
+
<ul>
** <tt>SINGLE_TABLE</tt> – all the classes in a hierarchy are mapped to a single table. The table has a discriminator column ([[#@DiscriminatorColumn|@DiscriminatorColumn]]) whose value ([[#@DiscriminatorValue|@DiscriminatorValue]]) identifies the specific subclass to which the instance that is represented by the row belongs.<br>{{EclipseLink_Note|note=This option provides the best support for both polymorphic relationships between entities and queries that range over the class hierarchy. The disadvantages of this option include the need to make nullable columns that should be <tt>NOT NULL</tt>.}}<br>
+
<li> <tt>strategy</tt> – By default, the EclipseLink persistence provider assumes that all the classes in a hierarchy are mapped to a single table differentiated by the discriminator value (see [[#@DiscriminatorValue|@DiscriminatorValue]]) in the table's discriminator column (see [[#@DiscriminatorColumn|@DiscriminatorColumn]]): <tt>InheritanceType.SINGLE_TABLE</tt>.<br>If this is not appropriate for your application or if you must match an existing data model, set <tt>strategy</tt> to the desired <tt>InheritanceType</tt> enumerated type:
** <tt>TABLE_PER_CLASS</tt> – each class is mapped to a separate table. All properties of the class, including inherited properties, are mapped to columns of the table for the class.
+
<ul><li> <tt>SINGLE_TABLE</tt> – all the classes in a hierarchy are mapped to a single table. The table has a discriminator column ([[#@DiscriminatorColumn Attributes|@DiscriminatorColumn]]) whose value ([[#@DiscriminatorValue|@DiscriminatorValue]]) identifies the specific subclass to which the instance that is represented by the row belongs.<br>
** <tt>JOINED</tt> – the root of the class hierarchy is represented by a single table and each subclass is represented by a separate table. Each subclass table contains only those fields that are specific to the subclass (not inherited from its superclass) and primary key columns that serve as foreign keys to the primary keys of the superclass table.
+
{{EclipseLink_Note|note=This option provides the best support for both polymorphic relationships between entities and queries that range over the class hierarchy. The disadvantages of this option include the need to make nullable columns that should be <tt>NOT NULL</tt>.}}<br>
 +
<li> <tt>TABLE_PER_CLASS</tt> – each class is mapped to a separate table. All properties of the class, including inherited properties, are mapped to columns of the table for the class.
 +
{{EclipseLink_Note|note=This option has several limitations when querying or having relationships to the root or branch classes.  Joins to root or branch classes are not supported.}}
 +
<li> <tt>JOINED</tt> – the root of the class hierarchy is represented by a single table and each subclass is represented by a separate table. Each subclass table contains only those fields that are specific to the subclass (not inherited from its superclass) and primary key columns that serve as foreign keys to the primary keys of the superclass table. The join from the primary table to the subclass table can be configured using the <code>@PrimaryKeyJoinColumn</code>, see [[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/SecondaryTable|SecondaryTable]] for more info.  If you have multiple levels of inheritance, each subclass table should join with the root table, and the discriminator column is only defined once in the root table.
 +
</ul></ul>
  
 
{{EclipseLink_Spec|section=Section 2.1.9 "Inheritance"}}
 
{{EclipseLink_Spec|section=Section 2.1.9 "Inheritance"}}
 
{{EclipseLink_Spec|section=Section 9.1.29 "Inheritance Annotation"}}
 
{{EclipseLink_Spec|section=Section 9.1.29 "Inheritance Annotation"}}
 +
 +
===@DiscriminatorColumn===
 +
You can use the <tt>@DiscriminatorColumn</tt> annotation or <code><nowiki><discriminator-column></nowiki></code> XML element to configure the name or type of the inheritance discriminator column.  The discriminator column is required for <code>SINGLE_TABLE</code> and <code>JOINED</code> inheritance and stores the associated entity type for the row.  The default name for the discriminator column is <code>DTYPE</code>.  JPA only allows String or Integer values for discriminators.  Through the EclipseLink API, it is possible to use other discriminator types, and it is possible to not have a discriminator, or use custom discriminator, see [[#Advanced Inheritance Configuration|Advanced Inheritance Configuration]].
  
 
{{EclipseLink_AttributeTable
 
{{EclipseLink_AttributeTable
Line 35: Line 48:
 
|content=<tr>
 
|content=<tr>
 
  <td>'''<tt>name</tt>'''</td>
 
  <td>'''<tt>name</tt>'''</td>
  <td>The name foreign key column in the secondary table that references the id column in the primary table
+
  <td>The name of column to be used to store the class discriminator value.
  <td>id column</td>
+
  <td><code>DTYPE</code></td>
 
  <td>No</td>
 
  <td>No</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
  <td>'''<tt>referencedColumnName</tt>'''</td>
+
  <td>'''<tt>discriminatorType</tt>'''</td>
  <td>A name of the id column in the primary table that is being referenced.  This is only required for composite ids.
+
  <td>The type of the discriminator value, defined in <code>DiscriminatorType</code>, one of <code>STRING</code>, <code>INTEGER</code>, and <code>CHAR</code>.
  <td>id column</td>
+
  <td><code>STRING</code></td>
 
  <td>No</td>
 
  <td>No</td>
 
</tr>
 
</tr>
Line 48: Line 61:
 
  <td>'''<tt>columnDefinition</tt>'''</td>
 
  <td>'''<tt>columnDefinition</tt>'''</td>
 
  <td>Optional column description for use with DDL generation.</td>
 
  <td>Optional column description for use with DDL generation.</td>
  <td></td>
+
  <td>generated base on <tt>discriminatorType</tt></td>
 +
<td>No</td>
 +
</tr>
 +
<tr>
 +
<td>'''<tt>length</tt>'''</td>
 +
<td>The size of the column for DDL generation.  Only relevant for <code>STRING</code> types.</td>
 +
<td>31</td>
 
  <td>No</td>
 
  <td>No</td>
 
</tr>
 
</tr>
 
}}
 
}}
 +
{{EclipseLink_Spec|section=Section 11.1.10 "DiscriminatorColumn Annotation"}}
  
{{EclipseLink_Spec|section=Section 11.1.xx "DiscriminatorColumn Annotation"}}
+
===@DiscriminatorValue===
 +
You can use the <tt>@DiscriminatorValue</tt> annotation or <code><nowiki><discriminator-value></nowiki></code> XML element to configure the value of the inheritance discriminator.  The discriminator value can be specified in each non-abstract class in the inheritance hierarchy.  By default the discriminator value is the entity's name, which defaults to its unprefixed class name. The discriminator value is always specified as a String, but is converted to the discriminator column type.
  
The following example shows how to use this annotation to specify the table for <code>Employee</code>.
+
 
 +
The following examples shows usages of the three different inheritance strategies for mapping an <code>Account</code> hierarchy.
  
 
<span id="Example: @Inheritance"></span>
 
<span id="Example: @Inheritance"></span>
======''Example: Using @Inheritance''======
+
======''Example: Using <code>SINGLE_TABLE</code> with @Inheritance annotation''======
 +
<source lang="SQL">
 +
create table ACCOUNT (ID NUMBER, ACCOUNT_TYPE VARCHAR(31), BALANCE NUMBER, INTERESTRATE NUMBER, RETURNCHECKS BOOLEAN)
 +
</source>
 +
 
 
<source lang="java">
 
<source lang="java">
 
@Entity
 
@Entity
@Table(name="EMP")
+
@Table(name="ACCOUNT")
@SecondaryTable(name="SALARY")
+
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Employee implements Serializable {
+
@DiscriminatorColumn(name="ACCOUNT_TYPE")
    ...
+
public abstract class Account implements Serializable {
 
     @Id
 
     @Id
     public Long getId() {
+
     private Long id;
        return id;
+
     @Basic
     }
+
    private BigDecimal balance;
 
     ...
 
     ...
 
}
 
}
 
</source>
 
</source>
  
======''Example: Using <code><nowiki><inheritance></nowiki></code>''======
+
<source lang="java">
 +
@Entity
 +
@DiscriminatorValue("SAVINGS")
 +
public class SavingAccount extends Account {
 +
    @Basic
 +
    private BigDecimal interestRate;
 +
}
 +
</source>
 +
 
 +
<source lang="java">
 +
@Entity
 +
@DiscriminatorValue("CHECKING")
 +
public class CheckingAccount extends Account {
 +
    @Basic
 +
    private boolean returnChecks;
 +
}
 +
</source>
 +
 
 +
======''Example: Using <code>SINGLE_TABLE</code> with <code><nowiki><inheritance></nowiki></code> XML''======
  
 
<source lang="xml">
 
<source lang="xml">
<entity class="Employee">
+
<entity class="Account">
     <table name="EMP"/>
+
     <table name="ACCOUNT"/>
     <secondary-table name="SALARY"/>
+
     <inheritance strategy="SINGLE_TABLE"/>
 +
    <discriminator-column name="ACCOUNT_TYPE"/>
 
     <attributes>
 
     <attributes>
 
         <id name="id"/>
 
         <id name="id"/>
         ...
+
         <basic name="balance"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
<source lang="xml">
 +
 
 +
<entity class="SavingAccount">
 +
    <discriminator-value>SAVINGS</discriminator-value>
 +
    <attributes>
 +
        <basic name="interestRate"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
 
 +
<source lang="xml">
 +
<entity class="CheckingAccount">
 +
    <discriminator-value>CHECKING</discriminator-value>
 +
    <attributes>
 +
        <basic name="returnChecks"/>
 
     </attributes>
 
     </attributes>
 
</entity>
 
</entity>
 
</source>
 
</source>
 
<br>
 
<br>
 +
 +
======''Example: Using <code>JOINED</code> with @Inheritance annotation''======
 +
<source lang="SQL">
 +
create table ACCOUNT (ID NUMBER, ACCOUNT_TYPE VARCHAR(31), BALANCE NUMBER)
 +
create table SAVING_ACCOUNT (ID NUMBER, INTERESTRATE NUMBER)
 +
create table CHECKING_ACCOUNT (ACCOUNT_ID NUMBER, RETURNCHECKS BOOLEAN)
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Table(name="ACCOUNT")
 +
@Inheritance(strategy=InheritanceType.JOINED)
 +
// This example shows usage of integer discriminators.
 +
@DiscriminatorColumn(name="ACCOUNT_TYPE", discriminatorType=DiscriminatorType.INTEGER)
 +
public abstract class Account implements Serializable {
 +
    @Id
 +
    private Long id;
 +
    @Basic
 +
    private BigDecimal balance;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Table(name="SAVING_ACCOUNT")
 +
@DiscriminatorValue("1")
 +
public class SavingAccount extends Account {
 +
    @Basic
 +
    private BigDecimal interestRate;
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Table(name="CHECKING_ACCOUNT")
 +
// This example shows usage of a join column as the id column is not the default name.
 +
@PrimaryKeyJoinColumn(name="ACCOUNT_ID" referencedColumnName="ID")
 +
@DiscriminatorValue("2")
 +
public class CheckingAccount extends Account {
 +
    @Basic
 +
    private boolean returnChecks;
 +
}
 +
</source>
 +
 +
======''Example: Using <code>JOINED</code> with <code><nowiki><inheritance></nowiki></code> XML''======
 +
 +
<source lang="xml">
 +
<entity class="Account">
 +
    <table name="ACCOUNT"/>
 +
    <inheritance strategy="JOINED"/>
 +
    <discriminator-column name="ACCOUNT_TYPE" discriminator-type="INTEGER"/>
 +
    <attributes>
 +
        <id name="id"/>
 +
        <basic name="balance"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
<source lang="xml">
 +
 +
<entity class="SavingAccount">
 +
    <table name="SAVINGS_ACCOUNT"/>
 +
    <discriminator-value>SAVINGS</discriminator-value>
 +
    <attributes>
 +
        <basic name="interestRate"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
 +
<source lang="xml">
 +
<entity class="CheckingAccount">
 +
    <table name="CHECKING_ACCOUNT"/>
 +
    <primary-key-join-column name="ACCOUNT_ID" referenced-column-name="ID"/>
 +
    <discriminator-value>CHECKING</discriminator-value>
 +
    <attributes>
 +
        <basic name="returnChecks"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
<br/>
 +
 +
======''Example: Using <code>TABLE_PER_CLASS</code> with @Inheritance annotation''======
 +
<source lang="SQL">
 +
create table SAVINGS_ACCOUNT (ID NUMBER, BALANCE NUMBER, INTERESTRATE NUMBER)
 +
create table CHECKING_ACCOUNT (ID NUMBER, BALANCE NUMBER, RETURNCHECKS BOOLEAN)
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
 +
public abstract class Account implements Serializable {
 +
    @Id
 +
    private Long id;
 +
    @Basic
 +
    private BigDecimal balance;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Table(name="SAVINGS_ACCOUNT")
 +
public class SavingAccount extends Account {
 +
    @Basic
 +
    private BigDecimal interestRate;
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
@Entity
 +
@Table(name="CHECKING_ACCOUNT")
 +
public class CheckingAccount extends Account {
 +
    @Basic
 +
    private boolean returnChecks;
 +
}
 +
</source>
 +
 +
======''Example: Using <code>TABLE_PER_CLASS</code> with <code><nowiki><inheritance></nowiki></code> XML''======
 +
 +
<source lang="xml">
 +
<entity class="Account">
 +
    <inheritance strategy="TABLE_PER_CLASS"/>
 +
    <attributes>
 +
        <id name="id"/>
 +
        <basic name="balance"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
<source lang="xml">
 +
 +
<entity class="SavingAccount">
 +
    <table>SAVINGS_ACCOUNT</table>
 +
    <attributes>
 +
        <basic name="interestRate"/>
 +
    </attributes>
 +
</entity>
 +
</source>
 +
 +
<source lang="xml">
 +
<entity class="CheckingAccount">
 +
    <table>CHECKING_ACCOUNT</table>
 +
    <attributes>
 +
        <basic name="returnChecks"/>
 +
    </attributes>
 +
</entity>
 +
</source>
  
 
==Advanced Inheritance Configuration==
 
==Advanced Inheritance Configuration==
JPA requires that secondary tables contain the id of the entity.  EclipseLink allows for the multiple table join to be based on foreign keys, or on specific criteria.
 
  
To define a secondary table that is joined through a foreign key, the <code>ClassDescriptor.addForeignKeyFieldNameForMultipleTable()</code> API is used.  This column name given to this API should be prefixed by its table name, this allows for foreign keys from any of the secondary tables to be used in the multiple table join. Note that all of the column values still need to be mapped at least once.
+
===Mixed Inheritance===
 +
JPA requires the inheritance strategy to only be defined in the root class.  If you want to use a mixture of <code>SINGLE_TABLE</code> and <code>JOINED</code>
 +
this can be achieved through using <code>JOINED</code> inheritance in the root class, and specifying the <code>Table</code> on the subclass to be the same as the parent.
 +
 
 +
======''Example: Using <code>JOINED</code> with mixed inheritance''======
 +
<source lang="java">
 +
@Entity
 +
// This subclass does not define its own table, but shares its parent's ACCOUNT table.
 +
@Table(name="ACCOUNT")
 +
@DiscriminatorValue("3")
 +
public class StandardAccount extends Account {
 +
  ...
 +
}
 +
</source>
  
If the multiple table join is more complex, an EclipseLink <code>Expression</code> can be used to define the join.  The <code>Expression</code> is set on the <code>ClassDescriptor</code>'s query manager using the <code>DescriptorQueryManager.setMultipleTableJoinExpression()</code> API.  It is even possible to define a join <code>Expression</code> that make use of an outer-join on certain databases.
+
===@ClassExtractor===
 +
If you are mapping to an existing database, and the tables do not have a discriminator column you can still define inheritance using the EclipseLink <code>@ClassExtractor</code> or <code><nowiki>class-extractor</nowiki></code> XML element.  The class extractor takes a class that implements the <code>ClassExtractor</code> interface.  EclipseLink uses a instance of this class to determine the class type to use for a database row.  The class extractor must define a <code>extractClassFromRow</code> method that takes the database <code>Record</code> and EclipseLink <code>Session</code>.
  
In certain advanced cases multiple tables can be encapsulated using a database view.  This allows for any table join to be define in SQL and for the entity to be simply mapped to the viewTo map an entity to a view, just use the view name instead of the table name.  If the view is not updateable, your database may support instead of triggers to allow updates.
+
If a class extractor is used with <code>SINGLE_TABLE</code> inheritance, EclipseLink needs to be able to filter the rows of the class type in queries.  This can be accomplished in EclipseLink through setting an <code>onlyInstancesExpression</code> or <code>withAllSubclassesExpression</code> for branch classesThese can be set to EclipseLink <code>Expression</code> objects using a <code>DescriptorCustomizer</code>.
  
======''Example: Using a DescriptorCustomizer to configure a secondary table joined through a foreign key''======
+
======''Example: Using <code>ClassExtractor</code> do define inheritance''======
 
<source lang="java">
 
<source lang="java">
 
@Entity
 
@Entity
@Table(name="EMP")
+
@Table(name="MILES_ACCOUNT")
@SecondaryTable(name="EMP_INFO")
+
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@Customizer(EmployeeCustomizer.class)
+
@ClassExtractor(AirMilesClassExtractor.class)
public class Employee implements Serializable {
+
@Customizer(AirMilesCustomizer.class)
 +
public class AirMilesAccount implements Serializable {
 
     @Id
 
     @Id
 
     private Long id;
 
     private Long id;
 
     @Basic
 
     @Basic
     @Column(name="ID", table="EMP_INFO")
+
    private String totalMiles;
     private String infoId;
+
     @Basic
 +
     private String milesBalance;
 
     ...
 
     ...
 
}
 
}
Line 113: Line 337:
  
 
<source lang="java">
 
<source lang="java">
public class EmployeeCustomizer implements DescriptorCustomizer {
+
@Entity
 +
@Customizer(PreferredCustomizer.class)
 +
public class PreferredAccount extends AirMilesAccount {
 +
    ...
 +
}
 +
</source>
 +
 
 +
<source lang="java">
 +
public class AirMilesClassExtractor implements ClassExtractor {
 +
    public void extractClassFromRow(Record row, Session session) {
 +
        if (row.get("TOTALMILES").lessThan(100000)) {
 +
            return AirMilesAccount.class;
 +
        } else {
 +
            return PreferredAccount.class;
 +
        }
 +
    }
 +
}
 +
</source>
 +
 
 +
<source lang="java">
 +
public class AirMilesCustomizer implements DescriptorCustomizer {
 
     public void customize(ClassDescriptor descriptor) {
 
     public void customize(ClassDescriptor descriptor) {
         descriptor.getAdditionalTablePrimaryKeyFields().clear();
+
         ExpressionBuilder account = new ExpressionBuilder();
         descriptor.getMultipleTableForeignKeys()().clear();
+
         Expression expression = account.getField("TOTALMILES").lessThan(100000);
         descriptor.addForeignKeyFieldNameForMultipleTable("EMP.INFO_ID", "EMP_INFO.ID");
+
         descriptor.getInheritancePolicy().setOnlyInstancesExpression(expression);
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
  
======''Example: Using a DescriptorCustomizer to configure a secondary table joined through an Expression''======
+
<source lang="java">
 +
public class PreferredCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        ExpressionBuilder account = new ExpressionBuilder();
 +
        Expression expression = account.getField("TOTALMILES").greaterThanEqual(100000);
 +
        descriptor.getInheritancePolicy().setOnlyInstancesExpression(expression);
 +
    }
 +
}
 +
</source>
 +
 
 +
===Using Other Discriminator Types===
 +
JPA only defines three discriminator types, <code>STRING</code>, <code>CHAR</code>, and <code>INTEGER</code>.  EclipseLink allows for any database type to be used for a discriminator.  To define discriminators using other types a <code>DescriptorCustomizer</code> can be used to customize the discriminators in the <code>ClassDescriptor</code>'s <code>InheritancePolicy</code> using the <code>addClassIndicator</code> API.
 +
 
 +
======''Example: Using a <code>DiscriminatorColumn</code> with a boolean type do define inheritance''======
 
<source lang="java">
 
<source lang="java">
 
@Entity
 
@Entity
@Table(name="EMP")
+
@Table(name="MILES_ACCOUNT")
@SecondaryTable(name="EMP_INFO")
+
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@Customizer(EmployeeCustomizer.class)
+
@DiscriminatorColumn(name="IS_PREFERRED")
public class Employee implements Serializable {
+
@Customizer(AirMilesCustomizer.class)
 +
public class AirMilesAccount implements Serializable {
 
     @Id
 
     @Id
 
     private Long id;
 
     private Long id;
 
     @Basic
 
     @Basic
    @Column(name="ID", table="EMP_INFO")
+
     private String milesBalance;
     private String infoId;
+
 
     ...
 
     ...
 
}
 
}
Line 139: Line 396:
  
 
<source lang="java">
 
<source lang="java">
public class EmployeeCustomizer implements DescriptorCustomizer {
+
@Entity
 +
public class PreferredAccount extends AirMilesAccount {
 +
    ...
 +
}
 +
</source>
 +
 
 +
<source lang="java">
 +
public class AirMilesCustomizer implements DescriptorCustomizer {
 
     public void customize(ClassDescriptor descriptor) {
 
     public void customize(ClassDescriptor descriptor) {
         ExpressionBuilder emp = new ExpressionBuilder();
+
         descriptor.getInheritancePolicy().getClassIndicatorMapping().clear();
        Expression join = emp.getField("EMP.INFO_ID").equal(emp.getField("EMP_INFO.ID").and(emp.getField("EMP_INFO.ARCHIVE").equal(false));
+
        descriptor.getInheritancePolicy().addClassIndicator(AirMilesAccount.class, false);
         descriptor.getQueryManager().setMultipleTableJoinExpression(join);
+
         descriptor.getInheritancePolicy().addClassIndicator(PreferredAccount.class, true);
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
 +
 +
===Outer Joining Subclasses===
 +
EclipseLink supports two mechanism for reading <code>JOINED</code> inheritance classes.  If you query to root or branch of a <code>JOINED</code> inheritance hierarhcy by default EclipseLink will query each concrete subclass separately and union the results in memory.  This is normally the most efficient mechanism, but this depends on the database and configuration.  If an order by is used, or joining, then EclipseLink will outer join all of the subclass tables in a single query.
 +
 +
To configure that an outer join always be used to query inheritance, a <code>DescriptorCustomizer</code> can be used to configure a <code>ClassDescriptor</code>'s <code>InheritancePolicy</code> using the API <code>setShouldOuterJoinSubclasses</code>.  This can also be configured for a specific query using the <code>"eclipselink.inheritance.outer-join"="true"</code> query hint.
 +
 +
======''Example: Configuring <code>JOINED</code> inheritance to use an outer join''======
 +
<source lang="java">
 +
@Entity
 +
@Table(name="ACCOUNT")
 +
@Inheritance(strategy=InheritanceType.JOINED)
 +
@DiscriminatorColumn(name="ACCOUNT_TYPE")
 +
@Customizer(AccountCustomizer.class)
 +
public class Account implements Serializable {
 +
    @Id
 +
    private Long id;
 +
    @Basic
 +
    private String balance;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
public class AccountCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        descriptor.getInheritancePolicy().setShouldOuterJoinSubclasses(true);
 +
    }
 +
}
 +
</source>
 +
 +
===Reading Concrete Superclasses===
 +
By default, when you query for a superclass, any valid subclass instances are also returned.  This is normally correct, and desired, but sometimes only the root or branch class instances are desired.
 +
For only a single query, this can be part of the query where clause using the JPQL <code>TYPE</code> function.  If all queries desire only the superclass, then this can be configured in EclipseLink by customizing the descriptor.
 +
 +
To configure that queries for a superclass not return subclasses, a <code>DescriptorCustomizer</code> can be used to configure a <code>ClassDescriptor</code>'s <code>InheritancePolicy</code> using the API <code>setShouldReadSubclasses</code>.
 +
 +
Another option in JPA is to make the superclass abstract and create another subclass to store the concrete instances.
 +
 +
======''Example: Configuring account to not read subclasses''======
 +
<source lang="java">
 +
@Entity
 +
@Inheritance(strategy=InheritanceType.JOINED)
 +
@DiscriminatorColumn(name="ACCOUNT_TYPE")
 +
@Customizer(AccountCustomizer.class)
 +
public class Account implements Serializable {
 +
    @Id
 +
    private Long id;
 +
    @Basic
 +
    private String balance;
 +
    ...
 +
}
 +
</source>
 +
 +
<source lang="java">
 +
public class AccountCustomizer implements DescriptorCustomizer {
 +
    public void customize(ClassDescriptor descriptor) {
 +
        descriptor.getInheritancePolicy().setShouldReadSubclasses(false);
 +
    }
 +
}
 +
</source>
 +
 +
===Avoiding Inheritance===
 +
JPA requires that any persistent subclass inherit the persistence meta data from its superclass, if the superclass is a persistent entity.  If you have a subclass, that does not want to inherit its persistence, this can be difficult.
 +
 +
One solution is to not define the superclass as an entity, but instead map it as a <code>MappedSuperclass</code>, see [[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/MappedSuperclass|@MappedSuperclass]].
 +
 +
Another solution is to not map the superclass as an entity.  This then allows each subclass to define its own persistence.  An orm XML file can be used to define mappings for the inherited attributes.
 +
 +
<code>TABLE_PER_CLASS</code> inheritance may also be used to avoid a common table.
 +
 +
EclipseLink allows for a subclass to be mapped independently of its superclass.  This can be done using a <code>SessionCustomizer</code> and adding a complete <code>RelationalDescriptor</code> for the subclass.
 +
 +
===Interfaces===
 +
EclipseLink has support for interfaces as well as inheritance.  See [[EclipseLink/UserGuide/JPA/Advanced JPA Development/Interfaces|Interfaces]] for more information on interfaces.
 +
  
 
{{EclipseLink_JPA
 
{{EclipseLink_JPA
 
|previous=[[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/SecondaryTable|@SecondaryTable]]
 
|previous=[[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/SecondaryTable|@SecondaryTable]]
|next=    [[EclipseLink/UserGuide/JPA/Basic JPA Development/Mapping|Configuring Mappings]]
+
|next=    [[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/MappedSuperclass|@MappedSuperclass]]
 
|up=      [[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/Creating_and_Configuring_Entities|Configuring Entities]]
 
|up=      [[EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/Creating_and_Configuring_Entities|Configuring Entities]]
 
|version=2.2.0 DRAFT}}
 
|version=2.2.0 DRAFT}}

Latest revision as of 10:48, 4 August 2011

EclipseLink JPA

@Inheritance

You can use the @Inheritance annotation or <inheritance> XML element to configure how entities with inheritance are persisted. JPA defines three inheritance strategies SINGLE_TABLE, JOINED, and TABLE_PER_CLASS. By default the SINGLE_TABLE strategy is used and all of the subclasses are persisted in a single table that contains all of the column of all of the subclasses. In addition a discriminator column named DTYPE is required in the table to store the class type.

For other inheritance strategies see Advanced Inheritance Configuration.

The @Inheritance annotation has the following attributes:

  • strategy – By default, the EclipseLink persistence provider assumes that all the classes in a hierarchy are mapped to a single table differentiated by the discriminator value (see @DiscriminatorValue) in the table's discriminator column (see @DiscriminatorColumn): InheritanceType.SINGLE_TABLE.
    If this is not appropriate for your application or if you must match an existing data model, set strategy to the desired InheritanceType enumerated type:
    • SINGLE_TABLE – all the classes in a hierarchy are mapped to a single table. The table has a discriminator column (@DiscriminatorColumn) whose value (@DiscriminatorValue) identifies the specific subclass to which the instance that is represented by the row belongs.

      Elug note icon.png

      Note: This option provides the best support for both polymorphic relationships between entities and queries that range over the class hierarchy. The disadvantages of this option include the need to make nullable columns that should be NOT NULL.


    • TABLE_PER_CLASS – each class is mapped to a separate table. All properties of the class, including inherited properties, are mapped to columns of the table for the class.

      Elug note icon.png

      Note: This option has several limitations when querying or having relationships to the root or branch classes. Joins to root or branch classes are not supported.

    • JOINED – the root of the class hierarchy is represented by a single table and each subclass is represented by a separate table. Each subclass table contains only those fields that are specific to the subclass (not inherited from its superclass) and primary key columns that serve as foreign keys to the primary keys of the superclass table. The join from the primary table to the subclass table can be configured using the @PrimaryKeyJoinColumn, see SecondaryTable for more info. If you have multiple levels of inheritance, each subclass table should join with the root table, and the discriminator column is only defined once in the root table.
Elug javaspec icon.gif

For more information, see Section 2.1.9 "Inheritance" in the JPA Specification.

Elug javaspec icon.gif

For more information, see Section 9.1.29 "Inheritance Annotation" in the JPA Specification.

@DiscriminatorColumn

You can use the @DiscriminatorColumn annotation or <discriminator-column> XML element to configure the name or type of the inheritance discriminator column. The discriminator column is required for SINGLE_TABLE and JOINED inheritance and stores the associated entity type for the row. The default name for the discriminator column is DTYPE. JPA only allows String or Integer values for discriminators. Through the EclipseLink API, it is possible to use other discriminator types, and it is possible to not have a discriminator, or use custom discriminator, see Advanced Inheritance Configuration.

@DiscriminatorColumn Attributes
Attribute Description Default Required?
name The name of column to be used to store the class discriminator value. DTYPE No
discriminatorType The type of the discriminator value, defined in DiscriminatorType, one of STRING, INTEGER, and CHAR. STRING No
columnDefinition Optional column description for use with DDL generation. generated base on discriminatorType No
length The size of the column for DDL generation. Only relevant for STRING types. 31 No
Elug javaspec icon.gif

For more information, see Section 11.1.10 "DiscriminatorColumn Annotation" in the JPA Specification.

@DiscriminatorValue

You can use the @DiscriminatorValue annotation or <discriminator-value> XML element to configure the value of the inheritance discriminator. The discriminator value can be specified in each non-abstract class in the inheritance hierarchy. By default the discriminator value is the entity's name, which defaults to its unprefixed class name. The discriminator value is always specified as a String, but is converted to the discriminator column type.


The following examples shows usages of the three different inheritance strategies for mapping an Account hierarchy.

Example: Using SINGLE_TABLE with @Inheritance annotation
CREATE TABLE ACCOUNT (ID NUMBER, ACCOUNT_TYPE VARCHAR(31), BALANCE NUMBER, INTERESTRATE NUMBER, RETURNCHECKS BOOLEAN)
@Entity
@Table(name="ACCOUNT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="ACCOUNT_TYPE")
public abstract class Account implements Serializable {
    @Id
    private Long id;
    @Basic
    private BigDecimal balance;
    ...
}
@Entity
@DiscriminatorValue("SAVINGS")
public class SavingAccount extends Account {
    @Basic
    private BigDecimal interestRate;
}
@Entity
@DiscriminatorValue("CHECKING")
public class CheckingAccount extends Account {
    @Basic
    private boolean returnChecks;
}
Example: Using SINGLE_TABLE with <inheritance> XML
<entity class="Account">
    <table name="ACCOUNT"/>
    <inheritance strategy="SINGLE_TABLE"/>
    <discriminator-column name="ACCOUNT_TYPE"/>
    <attributes>
        <id name="id"/>
        <basic name="balance"/>
    </attributes>
</entity>
<entity class="SavingAccount">
    <discriminator-value>SAVINGS</discriminator-value>
    <attributes>
        <basic name="interestRate"/>
    </attributes>
</entity>
<entity class="CheckingAccount">
    <discriminator-value>CHECKING</discriminator-value>
    <attributes>
        <basic name="returnChecks"/>
    </attributes>
</entity>


Example: Using JOINED with @Inheritance annotation
CREATE TABLE ACCOUNT (ID NUMBER, ACCOUNT_TYPE VARCHAR(31), BALANCE NUMBER)
CREATE TABLE SAVING_ACCOUNT (ID NUMBER, INTERESTRATE NUMBER)
CREATE TABLE CHECKING_ACCOUNT (ACCOUNT_ID NUMBER, RETURNCHECKS BOOLEAN)
@Entity
@Table(name="ACCOUNT")
@Inheritance(strategy=InheritanceType.JOINED)
// This example shows usage of integer discriminators.
@DiscriminatorColumn(name="ACCOUNT_TYPE", discriminatorType=DiscriminatorType.INTEGER)
public abstract class Account implements Serializable {
    @Id
    private Long id;
    @Basic
    private BigDecimal balance;
    ...
}
@Entity
@Table(name="SAVING_ACCOUNT")
@DiscriminatorValue("1")
public class SavingAccount extends Account {
    @Basic
    private BigDecimal interestRate;
}
@Entity
@Table(name="CHECKING_ACCOUNT")
// This example shows usage of a join column as the id column is not the default name.
@PrimaryKeyJoinColumn(name="ACCOUNT_ID" referencedColumnName="ID")
@DiscriminatorValue("2")
public class CheckingAccount extends Account {
    @Basic
    private boolean returnChecks;
}
Example: Using JOINED with <inheritance> XML
<entity class="Account">
    <table name="ACCOUNT"/>
    <inheritance strategy="JOINED"/>
    <discriminator-column name="ACCOUNT_TYPE" discriminator-type="INTEGER"/>
    <attributes>
        <id name="id"/>
        <basic name="balance"/>
    </attributes>
</entity>
<entity class="SavingAccount">
    <table name="SAVINGS_ACCOUNT"/>
    <discriminator-value>SAVINGS</discriminator-value>
    <attributes>
        <basic name="interestRate"/>
    </attributes>
</entity>
<entity class="CheckingAccount">
    <table name="CHECKING_ACCOUNT"/>
    <primary-key-join-column name="ACCOUNT_ID" referenced-column-name="ID"/>
    <discriminator-value>CHECKING</discriminator-value>
    <attributes>
        <basic name="returnChecks"/>
    </attributes>
</entity>


Example: Using TABLE_PER_CLASS with @Inheritance annotation
CREATE TABLE SAVINGS_ACCOUNT (ID NUMBER, BALANCE NUMBER, INTERESTRATE NUMBER)
CREATE TABLE CHECKING_ACCOUNT (ID NUMBER, BALANCE NUMBER, RETURNCHECKS BOOLEAN)
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Account implements Serializable {
    @Id
    private Long id;
    @Basic
    private BigDecimal balance;
    ...
}
@Entity
@Table(name="SAVINGS_ACCOUNT")
public class SavingAccount extends Account {
    @Basic
    private BigDecimal interestRate;
}
@Entity
@Table(name="CHECKING_ACCOUNT")
public class CheckingAccount extends Account {
    @Basic
    private boolean returnChecks;
}
Example: Using TABLE_PER_CLASS with <inheritance> XML
<entity class="Account">
    <inheritance strategy="TABLE_PER_CLASS"/>
    <attributes>
        <id name="id"/>
        <basic name="balance"/>
    </attributes>
</entity>
<entity class="SavingAccount">
    <table>SAVINGS_ACCOUNT</table>
    <attributes>
        <basic name="interestRate"/>
    </attributes>
</entity>
<entity class="CheckingAccount">
    <table>CHECKING_ACCOUNT</table>
    <attributes>
        <basic name="returnChecks"/>
    </attributes>
</entity>

Advanced Inheritance Configuration

Mixed Inheritance

JPA requires the inheritance strategy to only be defined in the root class. If you want to use a mixture of SINGLE_TABLE and JOINED this can be achieved through using JOINED inheritance in the root class, and specifying the Table on the subclass to be the same as the parent.

Example: Using JOINED with mixed inheritance
@Entity
// This subclass does not define its own table, but shares its parent's ACCOUNT table.
@Table(name="ACCOUNT")
@DiscriminatorValue("3")
public class StandardAccount extends Account {
   ...
}

@ClassExtractor

If you are mapping to an existing database, and the tables do not have a discriminator column you can still define inheritance using the EclipseLink @ClassExtractor or class-extractor XML element. The class extractor takes a class that implements the ClassExtractor interface. EclipseLink uses a instance of this class to determine the class type to use for a database row. The class extractor must define a extractClassFromRow method that takes the database Record and EclipseLink Session.

If a class extractor is used with SINGLE_TABLE inheritance, EclipseLink needs to be able to filter the rows of the class type in queries. This can be accomplished in EclipseLink through setting an onlyInstancesExpression or withAllSubclassesExpression for branch classes. These can be set to EclipseLink Expression objects using a DescriptorCustomizer.

Example: Using ClassExtractor do define inheritance
@Entity
@Table(name="MILES_ACCOUNT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@ClassExtractor(AirMilesClassExtractor.class)
@Customizer(AirMilesCustomizer.class)
public class AirMilesAccount implements Serializable {
    @Id
    private Long id;
    @Basic
    private String totalMiles;
    @Basic
    private String milesBalance;
    ...
}
@Entity
@Customizer(PreferredCustomizer.class)
public class PreferredAccount extends AirMilesAccount {
    ...
}
public class AirMilesClassExtractor implements ClassExtractor {
    public void extractClassFromRow(Record row, Session session) {
        if (row.get("TOTALMILES").lessThan(100000)) {
            return AirMilesAccount.class;
        } else {
            return PreferredAccount.class;
        }
    }
}
public class AirMilesCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        ExpressionBuilder account = new ExpressionBuilder();
        Expression expression = account.getField("TOTALMILES").lessThan(100000);
        descriptor.getInheritancePolicy().setOnlyInstancesExpression(expression);
    }
}
public class PreferredCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        ExpressionBuilder account = new ExpressionBuilder();
        Expression expression = account.getField("TOTALMILES").greaterThanEqual(100000);
        descriptor.getInheritancePolicy().setOnlyInstancesExpression(expression);
    }
}

Using Other Discriminator Types

JPA only defines three discriminator types, STRING, CHAR, and INTEGER. EclipseLink allows for any database type to be used for a discriminator. To define discriminators using other types a DescriptorCustomizer can be used to customize the discriminators in the ClassDescriptor's InheritancePolicy using the addClassIndicator API.

Example: Using a DiscriminatorColumn with a boolean type do define inheritance
@Entity
@Table(name="MILES_ACCOUNT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="IS_PREFERRED")
@Customizer(AirMilesCustomizer.class)
public class AirMilesAccount implements Serializable {
    @Id
    private Long id;
    @Basic
    private String milesBalance;
    ...
}
@Entity
public class PreferredAccount extends AirMilesAccount {
    ...
}
public class AirMilesCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        descriptor.getInheritancePolicy().getClassIndicatorMapping().clear();
        descriptor.getInheritancePolicy().addClassIndicator(AirMilesAccount.class, false);
        descriptor.getInheritancePolicy().addClassIndicator(PreferredAccount.class, true);
    }
}

Outer Joining Subclasses

EclipseLink supports two mechanism for reading JOINED inheritance classes. If you query to root or branch of a JOINED inheritance hierarhcy by default EclipseLink will query each concrete subclass separately and union the results in memory. This is normally the most efficient mechanism, but this depends on the database and configuration. If an order by is used, or joining, then EclipseLink will outer join all of the subclass tables in a single query.

To configure that an outer join always be used to query inheritance, a DescriptorCustomizer can be used to configure a ClassDescriptor's InheritancePolicy using the API setShouldOuterJoinSubclasses. This can also be configured for a specific query using the "eclipselink.inheritance.outer-join"="true" query hint.

Example: Configuring JOINED inheritance to use an outer join
@Entity
@Table(name="ACCOUNT")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="ACCOUNT_TYPE")
@Customizer(AccountCustomizer.class)
public class Account implements Serializable {
    @Id
    private Long id;
    @Basic
    private String balance;
    ...
}
public class AccountCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        descriptor.getInheritancePolicy().setShouldOuterJoinSubclasses(true);
    }
}

Reading Concrete Superclasses

By default, when you query for a superclass, any valid subclass instances are also returned. This is normally correct, and desired, but sometimes only the root or branch class instances are desired. For only a single query, this can be part of the query where clause using the JPQL TYPE function. If all queries desire only the superclass, then this can be configured in EclipseLink by customizing the descriptor.

To configure that queries for a superclass not return subclasses, a DescriptorCustomizer can be used to configure a ClassDescriptor's InheritancePolicy using the API setShouldReadSubclasses.

Another option in JPA is to make the superclass abstract and create another subclass to store the concrete instances.

Example: Configuring account to not read subclasses
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="ACCOUNT_TYPE")
@Customizer(AccountCustomizer.class)
public class Account implements Serializable {
    @Id
    private Long id;
    @Basic
    private String balance;
    ...
}
public class AccountCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        descriptor.getInheritancePolicy().setShouldReadSubclasses(false);
    }
}

Avoiding Inheritance

JPA requires that any persistent subclass inherit the persistence meta data from its superclass, if the superclass is a persistent entity. If you have a subclass, that does not want to inherit its persistence, this can be difficult.

One solution is to not define the superclass as an entity, but instead map it as a MappedSuperclass, see @MappedSuperclass.

Another solution is to not map the superclass as an entity. This then allows each subclass to define its own persistence. An orm XML file can be used to define mappings for the inherited attributes.

TABLE_PER_CLASS inheritance may also be used to avoid a common table.

EclipseLink allows for a subclass to be mapped independently of its superclass. This can be done using a SessionCustomizer and adding a complete RelationalDescriptor for the subclass.

Interfaces

EclipseLink has support for interfaces as well as inheritance. See Interfaces for more information on interfaces.


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

Back to the top