Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Difference between revisions of "EclipseLink/UserGuide/JPA/Basic JPA Development/Entities/Inheritance"
(→@DiscriminatorValue) |
(→Example: Using a DescriptorCustomizer to configure a secondary table joined through an Expression) |
||
Line 361: | Line 361: | ||
</source> | </source> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{{EclipseLink_JPA | {{EclipseLink_JPA |
Revision as of 14:02, 23 June 2011
EclipseLink JPA
EclipseLink | |
Website | |
Download | |
Community | |
Mailing List • Forums • IRC • mattermost | |
Issues | |
Open • Help Wanted • Bug Day | |
Contribute | |
Browse Source |
Native API
Contents
- 1 @Inheritance
- 1.1 @DiscriminatorColumn
- 1.2 @DiscriminatorValue
- 1.2.1 Example: Using SINGLE_TABLE with @Inheritance annotation
- 1.2.2 Example: Using SINGLE_TABLE with <inheritance> XML
- 1.2.3 Example: Using JOINED with @Inheritance annotation
- 1.2.4 Example: Using JOINED with <inheritance> XML
- 1.2.5 Example: Using TABLE_PER_CLASS with @Inheritance annotation
- 1.2.6 Example: Using TABLE_PER_CLASS with <inheritance> XML
- 1.3 Advanced Inheritance Configuration
@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.
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.
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.
- 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.
For more information, see Section 2.1.9 "Inheritance" in the JPA Specification.
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.
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 |
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
@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); } }