JPA1.0 Inheritance Test Scenarios




1. Inheritance Mapping Strategy
2. Inheritance Type
3. Related Annotation
4. Related Relationship
5. Related Access Type
6. Related Query
7. Summary
8. Enhancement

Appendix A. Class Hierarchy
Appendix B. Relationship
Appendix C. Inheritance Category
Appendix D: Test Case Description



1. Inheritance Mapping Strategy
Test Scenario
Description
Spec
Status
1.1 Inheritance
Single Table per Class Hierarchy Strategy
All the class in a hierarchy are mapped in one table with a discriminator column. 
1.1.1 Specify strategy in root entity
For example, Account.java has
@Entity
@Inheritance(strategy=
   InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DCOL")
then CheckingAccount is shown in one table:
  • Account + BankAccount + CheckingAccount+DCOL
1.1.2 Not specify strategy to use default strategy
For example, VIPClientAgent.java has
@Entity
VIPClientAgent and VIPClientAgentExt  are shown in one table,
  • VIPClientAgent + VIPClientAgentExt + DTYPE

2.1.10.1
implemented
1.2 Inheritance Joined Subclass Mapping
The root of the class hierarchy is represented by a single table. Each sub class is represented by a separate table.
1.2.1 Specify strategy in root entity
For example, PhoneAgent.java has
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
then PhoneAgent and PhoneAgentExt  are shown in two table,
  • PhoneAgent + DTYPE
  • PhoneAgentExt + PK of PhoneAgent
1.2.2  Specify strategy in child entity
 For example, BrokerageAccount with JOINED tries to overrides Account with SINGLE_TABLE.
It is not supported by top-link so that one table is shown for Account and BrokerageAccount.    The usage should be avoided.
2.1.10.3
implemented
1.3 Annotation Override with XML
for Mapping Strategy
Orm.xml overrides strategy in annotation.
For example, BaseInfo.java has one strategy,
@Entity
@Table(name="B_INFO")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DC", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("B_I")

orm.xml has a different strategy,
        <entity name="BaseInfo" class="BaseInfo"
                access="PROPERTY">
                <table name="BASE_INFO"/>
                <inheritance strategy="JOINED"/>
                <discriminator-value>BASE
                       </discriminator-value>
                <discriminator-column name="INFO_TYPE"/>
                <exclude-default-listeners/>
                <exclude-superclass-listeners/>
                <entity-listeners/>
                <attributes/>
        </entity>
The strategy in orm.xml is used. Two tables are shown as
  • BASE_INFO + INFO_TYPE
  • CLIENT_INFO  + PK of BASE_INFO
10.1
6.1.2
implemented

2. Inheritance Type
Test Scenario Description Spec
Status
2.1 Abstract
Entity Class
  • An entity inherits a non-entity, e.g. Account.
2.1.9.1
implemented
2.2 Concrete
Entity Class
  • It extends non-entity, e.g.  ClientInfo.
  • It extends entity, e.g. MarginAccount
  • Dummy Entity in the chain, e.g. DummyClient
2.1.9.1 implemented
2.3 Abstract Mapped
Superclass
  • It is not at root of entity inheritance, e.g.  BankAccount.
2.1.9.2
implemented
2.4 Concrete Mapped
Superclass
  • It is at the root of entity inheritance and has a primary key, e.g. Client with @Id.
  • It is at the root of the entity inheritance and doesn't have a primary key, e.g. BaseAgent.
  • Its subclasses have different number of primary keys, e.g.  VIPClientAgent with @IdClasss for 2 keys or PhoneAgent with @EmbeddedId for 2 keys.
2.1.9.2 implemented
2.5 Abstract Non-Entity
Class
  • An entity inherits a non-entity,  e.g. non-entity BaseAccount is a super class of an abstract entity
2.1.9.3
implemented
2.6 Concrete Non-Entity
Class
  • An non-entity inherits an entity, e.g. Info is a subclass of an entity.
2.1.9.3 implemented

3. Related Annotation
Test Scenario Description Spec
Status
3.1 Discriminator
Column
It is a column whose value identifies the specific sub-class by the row belongs. 
3.1 Specify discriminator column
For example, in Account,
@DiscriminatorColumn(name="DCOL")
where DCOL has values of SavingsAccount,
CheckingAccount, ...
3.1.2 Use default discriminator column
VIPClientAgent has default DTYPE.
2.1.9.1
9.1.30
implemented
3.2 Primary Key
Join Column
ClientInfo has
@PrimaryKeyJoinColumn(name="INFO_ID")
It inherits Id  from BaseInfo, but mapped in this class to INFO_ID.
9.1.32
implemented
3.3 Primary Key
Join Column
A subclass needs to specified PrimaryKeyJoinColumns when its super class has multiple primary keys and uses JOINED strategy,
For example,  PhoneAgentExt has
@Entity
@PrimaryKeyJoinColumns({
   @PrimaryKeyJoinColumn(name="PA_PK1",
       referencedColumnName="PK1"),
   @PrimaryKeyJoinColumn(name="PA_PK2",
       referencedColumnName="PK2")
   })
9.1.33
implemented
3.4 Attribute
Override
The mapping attribute of an inherited class is override.   For example, the mapping attribute bankName in the CheckingAccount is override as "bank_name", i.e.
@AttributeOverride(name="bankName",  
    column=@Column
   (name="BANK_NAME"))

9.1.10
implemented
3.5 Association
Override
It is used to override a ManyToOne or OneToOne mapping property or field for an entity relationship on a mapped supperclass.
For example, Client has OneToMany relationship with BaseInfo.   DummyClient overrides the relationship from baseInfo to client_info, i.e.
@AssociationOverride(name="baseInfo",
   
joinColumns=@JoinColumn
   (name="CLIENT_INFO")
)
9.1.12
implemented


4. Related Relationship
Test Scenario Description Spec
Status
4.1 Uni-directional OneToOne OneToOne has EAGER fetch in default, e.g.  ClientInfo can be found from Client.
The Casecase Persist is used, e.g.
For example, from Client to BaseInfo.
 @OneToOne(cascade=CascadeType.PERSIST)
 @JoinColumn(name="FK_INFO")
  public BaseInfo getBaseInfo() {
2.1.8.3
9.1.23
implemented
4.2 Uni-directional
OneToMany
with EAGER fetch
OneToMany has LAZY fetch in default.  The relationship is not fetched back to a separate JVM.  Specifying EAGER fetch to get the relationship.  For example, find Account from Client.
JoinTable is specified for the mapping, e.g.
@OneToMany (fetch = FetchType.EAGER)
@JoinTable(name="CLIENT_ACCT",
    joinColumns=@JoinColumn
      (name="C_ID",
      referencedColumnName="CLIENTID"),
    inverseJoinColumns=@JoinColumn
        (name="A_ID",
        referencedColumnName="ACCTNUM")

)
2.1.8.5.1
9.1.24
3.2.4.2
implemented
4.3 Uni-directional ManyToMany
For example, from Client to PhoneAgent with a composite key,
@ManyToMany
@JoinTable(name="CLIENT_PHONEAGENT",
   joinColumns=@JoinColumn(name="C_ID",
       referencedColumnName="CLIENTID"),
   inverseJoinColumns={
       (name="PA_ID1",

           referencedColumnName="PK1"),
       (name="PA_ID2",

           referencedColumnName="PK2")
)
2.1.8.5.2
implemented
4.4 Bi-directional
ManyToOne
For example, from VIPClient to VIPClientAgent with a composite key,
@ManyToOne
    @JoinColumns ({
        @JoinColumn(name="FK_VA1",
              referencedColumnName="PK1I"),
        @JoinColumn(name="FK_VA2",
             referencedColumnName="PK2S")
    })

2.1.8.2 implemented

5. Related Access Type
Test Scenario Description
Spec
Status
5.1  Property
Based Access
Whole class hierarchy use property based access, e.g. BaseAccount.
2.1.1
implemented
5.2  Field
Based Access
Other class hierarchy use field based access, e.g. ClientInfo.
2.1.1
Any side-effect with the relationship between two classes with different access types?

6. Related Query
Test Scenario Description Spec
Status
6.1 Polymorphic Query
The instances returned by a query include instances of the subclasses.  For example, 
em.createQuery(SELECT a From Account a
     WHERE a.status = :acctStatus)
.setParameter("acctStatus", Account.Status.OPEN)
.getResultList();
where "Account a" is an abstract entity and has sub-classes such as CheckingAccount and SavingsAccount as long as its status is OPEN.
3.6.5
4.4.8
implemented
6.2 Eager fetch
across JVM
See 4.1 and 4.2
3.2.4.2
implemented

7. Summary

8.  Enhancement
The list below is based on the review by development and CTS feedback.
8.1. check self-referencing relation with inheritance,
8.2. investigate the non-entity behavior difference between JavaEE and JavaSE in inheritance,
8.3. enhance current JPA QL with more subclass and superclass referencing related to the inhesitance.

Appendix A. Class Hierarchy Appendix B. Relationship
Appendix C. Inheritance Category
Type
Abstract
Concrete
Entity
Account
CheckingAccount
SavingsAccount
BrokerageAccount
MarginAccount
CreditCardAccount
VIPClientAgent
VIPClientAgentExt
PhoneAgent
PhoneAgentExt
ClientInfo
Mapped
Superclass
BankAccount
Client
BaseAgent
Plain
BaseAccount

BaseClient
Info


Appendix D: Test Case Description
No.
Name
Description
1
create1
create SavingsAccount called account1 by em.persist.
2
find1
find account1 with em.find(Account.class, acctNum), in which account details are rendered to show its subclasses.
3
create2
create CheckingAccount called accoun2 by em.persist.
4
find2
find account2 with em.find(Account.class, acctNum), in which account details are rendered to show its subclasses.
5
create3
create VIPClientAgent @IdClasss for 2 keys called agent1 by em.persist,
create PhoneAgent with @EmbeddedId for 2 keys called agent2 and agent3 by em.persist,
create a ClientInfo, which will be Cascade.persist by VIPClient.
set relationhsips so that
  • Entity VIPClient has Bi-directional ManyToOne relation with Entity VIPClientAgent as agent1.
  • Mapped Supercalss, Client has Uni-directional ManyToMany relation with Entity PhoneAgent as agent2 and agent3.
  • Mapped Supercalss, Client has Uni-directional OneToOne relation with Entity BaseInfo.
  • Mapped Supercalss, Client has Uni-directional OneToMany relation with Entity Account, which is created before as account1 by create1 and account2 by create2.
create a VIPClient called client with em.persist.
6
find3
find client by em.find(DummyClient.class, clientId), in which the client details are rendered to show Client, ClientInfo and VIPClientAgent, PhoneAgent.
7
create4
create CheckingAccount called accoun3 by em.persist.
8
find4
find account3 by em.find(Account.class, acctNum),
9
create5
create CreditCardAccount called account4 by em.persit
10
create6
create BrokerageAccount called account5 by em.persist
11
create7
create MarginAccount called account5 by em.persist
12
create8
create RegularClient  by em.persist, which has account4, agent2, agent4, and its ClientInfo
13
find6
find the client in test 12 by em.find(DummyClient.class, clientId);
14
create8 create RegularClient  by em.persist, which has account5, account6, agent2, agent4, and its ClientInfo
15
find9
find ClientInfo in test 13 by em.find(BaseInfo.class, clientInfoId).
16
update4
change account3 by em.find(Account.class, acctNum), account.setName(name), then em.merge(account).
17
remove4
remove account3 by account = em.find(Account.class, acctNum), and then em.remove(account).
18
native1
use native query to verify the followings:
  • annotation override
    • from DTYPE to @DiscriminatorColumn(name="DCOL")
    • "SELECT a.DCOL from ACCOUNT a"
  • attribute override
    • from BANKNAME to BANK_NAME,
    • "SELECT ca.BANK_NAME from ACCOUNT ca" with override or
    • "SELECT sa.BANKNAME from ACCOUNT sa" without override.
  • association override
    • from BASE_INFO to CLIENT_INFO
    • "SELECT dc.CLIENT_INFO from DUMMYCLIENT dc"
  • XML override annontation
    • from Single Table B_INFO to Joined BASE_INFO by annotation override first
    • and then to C_INFO by Orm.xml override next
    • "SELECT bi.INFO_TYPE from BASE_INFO bi"
    • "SELECT ci.INFO_ID from C_INFO ci"
19
jpaql1
Polymorphic Query by String, e.g.
em.createQuery("SELECT a From Account a WHERE a.name like :acctName")
.setParameter("acctName", acct1)
.getResultList();
which finds one "Account a" by acctName, while "Account a" is an abstract entity with sub-classes.

Polymorphic Query by Enum, e.g.
em.createQuery(SELECT a From Account a
                           WHERE a.status = :acctStatus)
.setParameter("acctStatus", Account.Status.OPEN)
.getResultList();
where finds all open "Account a", each of account is an abstract entity with sub-classes such as CheckingAccount and SavingsAccount.
20
jpaql2
Update by String, e.g.
Query q = em.createQuery("Update Account a SET a.name = :acctName
                                            WHERE a.acctNum = :acctNum");

q.setParameter("acctNum", "A10001");
q.setParameter("acctName","savings-sherry");
q.executeUpdate();
which changes one account's name to "saving-sherry" while its acctNum is "A10001".

Update by Enum, e.g.
Query q = manager.createQuery("Update Account a SET a.status = :acctStatus
                                                      WHERE LOCATE(:acctName, a.name) > 0 ");

q.setParameter("acctStatus", Account.Status.OPEN);
q.setParameter("acctName", "sherry");
q.executeUpdate();
which changes all accounts to status open if its name contains "sherry".