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. Advanced validation |
19. Refining the standard
behavior |
20.
Behavior & business logic |
21.
References & collections |
A.
Architecture & philosophy |
B.
Java Persistence API |
C.
Annotations |
D.
Automated testing
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:

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:

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:

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 INVOICING.INVOICE_DETAILS;
DROP TABLE INVOICING.INVOICE;
To execute these SQL statements, first make sure your application
is running, then use the menu option
OpenXava > Database Manager
of OpenXava Studio:

Then:

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:

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.