官术网_书友最值得收藏!

O/R Inheritance Mapping

Inheritance is a distinctive aspect of object oriented technology. Inheritance could not be utilized in earlier versions of EJB as an EJB 2.x entity bean cannot extend another EJB 2.x entity bean. With EJB 3, entities are more object oriented in that one entity can extend another. However, this leaves us with the matter of mapping entities in an inheritance hierarchy onto a relational database.

There are a number of strategies we can use to map an inheritance hierarchy onto relational database tables. As an example, suppose the Account class has CheckingAccount and SavingsAccount subclasses:

O/R Inheritance Mapping

CheckingAccount and SavingsAccount extend the base Account class. As the names suggest, they are entities that represent checking accounts and savings accounts respectively.

SINGLE_TABLE Strategy

The first inheritance mapping option is the Single Table strategy. All the classes in a hierarchy are mapped to a single table. The table must have a discriminator column whose value for a given row identifies the associated subclass. The ACCOUNT table below results from implementing the Single Table strategy for the above Account inheritance hierarchy. A couple of rows have been added for illustration.

ACCOUNT Table

ACCOUNT_TYPE is the discriminator column. A value of C indicates a checking account, a value of S indicates a saving account.

This strategy is efficient for both read and write operations because there is no need to perform joins. It is a rather wasteful use of database storage especially if there are many subclasses each containing a large number of attributes. Columns corresponding to subclass attributes, such as INTERESTRATE and OVERDRAFTLIMIT in our example, must be nullable.

We now turn to the code required to implement this strategy. The Account entity is shown below:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="ACCOUNT_TYPE")
public abstract class Account implements Serializable {
protected int id;
protected double balance;
protected String accountType;
// getters and setters
@Column(name="ACCOUNT_TYPE", length=2)
public String getAccountType() {
return accountType;
}
...
}

Note the Account entity is an abstract class. This is because the Account entity will never be instantiated, only CheckingAccount and SavingsAccount entities will be instantiated. Consequently there is no constructor for the Account entity. The @Inheritance annotation indicates that a Single Table mapping strategy is used. The @DiscriminatorColumn annotation specifies that the ACCOUNT_TYPE column acts as the discriminator column. Note that we need to use the @Column annotation to map the accountType field with the ACCOUNT_TYPE column.

Now take a look at the CheckingAccount entity:

@Entity
@DiscriminatorValue("C")
public class CheckingAccount extends Account {
private double overdraftLimit;
public CheckingAccount() {
}
// getter and setter
}

Note the use of the @DiscriminatorValue annotation to indicate that a checking account has a value of C in the discriminator column. As CheckingAccount is a concrete class, it contains a constructor.

The code for SavingsAccount has a similar structure to CheckingAccount:

@Entity
@DiscriminatorValue("S")
public class SavingsAccount extends Account {
private double interestRate;
public SavingsAccount() {}
// getter and setter
}

This time the @DiscriminatorValue annotation indicates that a savings account has a value of S in the discriminator column.

An important feature of the Single Table strategy is that polymorphic queries are supported. By a polymorphic query we mean that the FROM clause of the query includes not only instances of the concrete entity class to which it refers, but all subclasses of that class as well. If the query refers to an abstract entity class then it is polymorphic if it includes just the subclasses of that class. So the query,

SELECT a FROM Account a

will retrieve both CheckingAccounts and SavingsAccounts. We will cover queries in some detail in Chapter 5.

JOINED Strategy

In this strategy the root of the class hierarchy is represented by a single table. Each subclass is represented by a separate table which contains columns corresponding to fields which are specific to the subclass. The subclass table also contains a column that represents the primary key. The primary key column also acts as a foreign key to the root primary key. A Joined strategy for the Account inheritance hierarchy results in the following tables:

ACCOUNT Table

CHECKINGACCOUNT Table

SAVINGSACCOUNT Table

Note in both the CHECKINGACCOUNT and SAVINGSACCOUNT tables, ACCOUNT_ID is a foreign key referencing ACCOUNT(ACCOUNT_ID).

This strategy is efficient with respect to data storage, however as queries require joins, they are inefficient especially when inheritance hierarchies are wide or deep. Like the Single Table strategy, the Joined strategy also supports polymorphic queries.

We will take a look at the code to implement a Joined strategy for the Account inheritance hierarchy. First we take the Account entity:

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="ACCOUNT_TYPE")
public abstract class Account implements Serializable {
protected int id;
protected double balance;
protected String accountType;
// getters and setters
}

There is only one change from the Single Table strategy case, we specify a Joined strategy in the @Inheritance annotation. No changes are required for the CheckingAccount and SavingsAccount entities.

Table per Concrete Class Strategy

The final strategy is for each concrete subclass to have its own table. Each class has all its properties, including inherited properties, mapped to columns of the corresponding table. For the Account inheritance hierarchy this will result in the following tables:

CHECKINGACCOUNT Table

SAVINGSACCOUNT Table

Note there is no ACCOUNT table. This strategy is efficient in terms of data storage and has the added advantage that a discriminator column is not required. However, this strategy does not support polymorphism very well. It is also inefficient when queries span more than one table as an SQL UNION is typically required.

This strategy is optional. The EJB 3 specification does not require persistence providers to support this strategy. As the GlassFish application server, with the embedded Toplink persistence engine, does not support this strategy we will not go further into coding details.

主站蜘蛛池模板: 博白县| 禹城市| 青铜峡市| 华阴市| 潮州市| 上思县| 绍兴县| 浦城县| 蛟河市| 密山市| 禄劝| 军事| 延津县| 沈阳市| 临西县| 白沙| 工布江达县| 平安县| 左权县| 海南省| 梁平县| 斗六市| 南华县| 汝阳县| 青冈县| 巨鹿县| 明水县| 青阳县| 萨迦县| 灌云县| 玉屏| 吴忠市| 正安县| 台南县| 平南县| 茂名市| 顺平县| 青州市| 镇远县| 四子王旗| 凯里市|