openxava / documentation / Lesson 7: Entity inheritance

Course: 1. Getting started | 2. Basic domain model (1) | 3. Basic domain model (2) | 4. Refining the user interface | 5. Agile development | 6. Mapped superclass inheritance | 7. Entity inheritance | 8. View inheritance | 9. Java properties | 10. Calculated properties | 11. @DefaultValueCalculator in collections | 12. @Calculation and collections totals | 13. @DefaultValueCalculator from file | 14. Manual schema evolution | 15. Multi user default value calculation | 16. Synchronize persistent and computed propierties | 17. Logic from database  | 18. Validating with @EntityValidator 19. Validation alternatives  | 20. Validation on remove  21. Custom Bean Validation annotation  | 22. REST service call from validation  | 23. Attributes in annotations  | 24. Refining the standard behavior | 25. Behavior & business logic | 26. References & collections | A. Architecture & philosophy | B. Java Persistence API | C. Annotations | D. Automated testing

Table of contents

Lesson 7: Entity inheritance
New Order entity
CommercialDocument as an abstract entity
Invoice refactored to use inheritance
Creating Order using inheritance
Naming convention and inheritance
Associating Order with Invoice
Summary
An entity may inherit from another entity. This entity inheritance is a useful tool to simplify your model. We are going to use it to add a new Order entity to your invoicing application.

If you don't like videos follow the instructions below.

New Order entity

We want to add a new concept to the invoicing application, i. e., order. While an invoice is something you want to charge your customer, an order is something the customer has ordered from us. These two concepts are strongly related, because you will charge the things your customer has ordered from you, and you actually have served him.
It would be nice to manage orders in your application, and to associate those orders with its corresponding invoices. Just as shown in the next UML diagram:
inheritance_en010.png
And with Java code:
@Entity
public class Invoice {
 
    @OneToMany(mappedBy="invoice")
    Collection<Order> orders;
    ...
}
 
@Entity
public class Order {
 
    @ManyToOne // Without lazy fetching (1)
    Invoice invoice;
    ...
}
That is, an invoice has several orders, and an order can reference an invoice. Note that we do not use lazy fetching for the invoice reference (1), this is because of a Hibernate bug when the reference owns the bidirectional relationship (that is, it's declared in the mappedBy attribute of the @OneToMany).
What shape does Order have? Well, it has a customer, several detail lines with product and quantity, a year and a number, something like this:
inheritance_en020.png
Incidentally, this UML diagram is identical to the Invoice diagram. That is, to create your Order entity you can just copy and paste the Invoice class, and the work is done. But, wait a moment! “Copy and paste” is a mortal sin. So, we have to find a way to reuse Invoice code for Order.

CommercialDocument as an abstract entity

A practical way to reuse the code for Invoice and Order is by using inheritance, moreover it's an excellent opportunity to learn how easy it is to use inheritance with JPA and OpenXava.
In most object oriented cultures you have to observe the is a rule. It means that we cannot do Invoice extends Order, because an Invoice is not an Order. The solution for this case is creating a base class for both Order and Invoice. We are going to call that class CommercialDocument.
Here you have the UML diagram for CommercialDocument:
inheritance_en030.png
And here you have the same idea expressed with Java:
public class CommercialDocument { ... }
public class Order extends CommercialDocument { ... }
public class Invoice extends CommercialDocument { ... }
Let's start to refactor your current code. First, rename (using Refactor > Rename) Invoice as CommercialDocument. Now, edit the CommercialDocument code to declare it as an abstract class, thus:
abstract public class CommercialDocument // We add the abstract modifier
We want to create instances of Invoice and Order, but we do not want to create instances of CommercialDocument directly, so we declare it as abstract.

Invoice refactored to use inheritance

Now, you have to create the Invoice entity extending it from CommercialDocument. See the new Invoice code:
package com.yourcompany.invoicing.model;
 
import javax.persistence.*;
import lombok.*;
 
@Entity @Getter @Setter
public class Invoice extends CommercialDocument {
 
}
Invoice now has very succinct code, indeed the body of the class is, by now, empty.
This new code requires a slightly different database schema, now invoices and orders will be stored in the same table (the CommercialDocument table) using a discriminator column. Therefore you need to remove the old tables executing the next SQL statements:
DROP TABLE INVOICE_DETAILS;
DROP TABLE INVOICE;
To execute these SQL statements, first make sure your application is running, then use the menu option OpenXava > Database Manager of OpenXava Studio:
inheritance040.png
Then:
inheritance_en050.png
Now you can execute the Invoice module and see it working in your browser. Also, execute the InvoiceTest. It must be green.
If you use your own version of Eclipse or IntelliJ you can run the Database Manager executing the runDBManager Ant target of OpenXava/build.xml.

Creating Order using inheritance

Thanks to CommercialDocument creating the Order entity is dead easy. See the code:
package com.yourcompany.invoicing.model;
 
import javax.persistence.*;
import lombok.*;
 
@Entity @Getter @Setter
public class Order extends CommercialDocument {
 
}
After writing the above Order class, although it is empty by now, you can use the Order module from your browser. Yes, creating a new entity with a structure like Invoice, that is any commercial document entity, is very easy and rapid. You see how good use of inheritance is an elegant way to have simpler code.
Order module works perfectly, but it has a little problem. The new order number is calculated from the last invoice number, not from the last order number. This is because the calculator for the next number is read from the Invoice entity. An obvious solution is to move the number property definition from CommercialDocument to Invoice and Order. Although, we are not going to do it in this way, because in the next lesson we'll refine the way to calculate the next number, for now we simply do a little adjustment in the current code so that it does not fail. Edit the NextNumberForYearCalculator class and in the query change “Invoice” for “CommercialDocument”, leaving the calculate() method, in this way:
public Object calculate() throws Exception {
    Query query = XPersistence.getManager().createQuery(
        "select max(i.number) from " +
        "CommercialDocument i " + // CommercialDocument instead of Invoice
        "where i.year = :year");
    query.setParameter("year", year);
    Integer lastNumber = (Integer) query.getSingleResult();
    return lastNumber == null?1:lastNumber + 1;
}
Now we search for the maximum number of any commercial document of the year in order to calculate the new number, therefore the numbering is shared across all the commercial documents. We'll improve it in the next lesson for having separate numbering for invoices and orders and to have better support for a multiuser environment.

Naming convention and inheritance

Note that you do not need to change the name of any property of Invoice to do the refactoring. This is because we follow the next pragmatic principle: Do not use the class name in member names, e.g., inside an Account class do not use the “Account” word in any method or property:
public class Account { // We'll not use Account in member names
 
    private int accountNumber; // Bad, because it uses “account”
    private int number; // Good, it does not use “account”
 
    public void cancelAccount() { } // Bad, because it uses “Account”
    public void cancel() { } // Good, it does not use “account”
    ...
}
Using this nomenclature you can refactor Account in an inheritance hierarchy without renaming its members, and you can write polymorphic code with Account.

Associating Order with Invoice

By now, Order and Invoice are exactly the same. We are going to do the first extensions on them, that is to associate Order with Invoice. Just as shown in the diagram:
inheritance_en010.png
You only need to add a reference from Order to Invoice:
package com.yourcompany.invoicing.model;
 
import javax.persistence.*;
import lombok.*;
 
@Entity @Getter @Setter
public class Order extends CommercialDocument {
 
    @ManyToOne
    Invoice invoice; // Reference to invoice added
 
}
Conversely in Invoice we add a collection of Order entities:
package com.yourcompany.invoicing.model;
 
import java.util.*; 
import javax.persistence.*;
import lombok.*;
 
@Entity @Getter @Setter
public class Invoice extends CommercialDocument {
 
    @OneToMany(mappedBy="invoice")
    Collection<Order> orders; // Collection of Order entities added
 
}
After writing this simple code you are ready to test the newly created relationship. Restart your application and open your browser and explore the Order and the Invoice modules. Note that at the end of the Order user interface you have a reference to Invoice. The user can use this reference to associate an invoice with the current order. On the other hand, if you explore the Invoice module, you will see a collection of orders at the end. The user can use it to add orders to the current invoice.
Try to add orders to invoice, and to associate an invoice to an order. It works, although the user interface is a little ugly. Don't worry we'll improve it in the next lesson.

Summary

This lesson has shown you some practical examples about how to use the inheritance of Java with JPA enties to simplify your code. We used the default JPA configuration for inheritance, though you can refine JPA behavior for inheritance using some JPA annotations like @Inheritance, @DiscriminatorColumn, @DiscriminatorValue, etc. To learn more about inheritance in JPA read the documents cited in Appendix B.

Download source code of this lesson

Any problem with this lesson? Ask in the forum Everything fine? Go to Lesson 8