In the previous lesson, we learned
how to modify our database schema manually in order to modify the data
without having to drop the tables. Now we will see how to define default
calculations to apply in multi-user environments.
If you don't like videos follow
the instructions below.
JPA
callback methods
Another useful way to add business logic to your model is using JPA
callback methods. A callback method is a method in your entity that is
called in some specific moment of its life cycle as a persistent object.
That is, you can specify some logic to execute on save, read, remove or
modification of the entity.
In this section we'll see some practical applications of JPA callback
methods.
Multiuser
safe default value calculation
Until now we were calculating the
Invoice and
Order
number using
@DefaultValueCalculator. This calculates the
default value when the user clicks to create a new
Invoice or
Order.
So, if several users click on the
New button at the same time all
of them get the same number. This is not multiuser safe. The way to
generate a unique number is by generating it just on save.
We are going to implement it using a JPA callback method. JPA allows you
to mark any method of your class to be executed in any part of its life
cycle. We'll indicate the calculation of the number just before the saving
of the
CommercialDocument. Using this approach we'll improve the
number calculation for having a different numeration for
Order
and
Invoice.
Edit the
CommercialDocument entity and add the
calculateNumber()
method:
@PrePersist // Executed just before saving the object for the first time
private void calculateNumber() throws Exception {
Query query = XPersistence.getManager()
.createQuery("select max(i.number) from " +
getClass().getSimpleName() + // Thus it's valid for both Invoice and Order
" i where i.year = :year");
query.setParameter("year", year);
Integer lastNumber = (Integer) query.getSingleResult();
this.number = lastNumber == null ? 1 : lastNumber + 1;
}
This code is the same as that of
the
NextNumberForYearCalculator but using
getClass().getSimpleName()
instead of “CommercialDocument”. The
getSimpleName() method
returns the name of the class without the package, i.e., just the entity
name. It will be “Order” for
Order and “Invoice” for
Invoice.
Thus we can get a different numeration for
Order and
Invoice.
JPA specification states that you should not use JPA API inside a JPA
callback method. So the above method is not legal from a strict JPA
viewpoint. But, Hibernate (the JPA implementation OpenXava uses by
default) allows you to use it in
@PrePersist. And since JPA is
the easier way to do this calculation we use it in our practice.
Now you can delete the
NextNumberForYearCalculator class from
your project, and modify the number property of
CommercialDocument
to avoid using it:
@Column(length=6)
// @DefaultValueCalculator(value=NextNumberForYearCalculator.class, // Remove this
// properties=@PropertyValue(name="year")
// )
@ReadOnly // The user cannot modify the value
int number;
Note that in addition to removing
@DefaultValueCalculator,
we have added the
@ReadOnly annotation. This means that the user
cannot enter or modify the
number. This is the right approach
given that the number is generated on saving the object, so the user typed
value would always be overridden.
Try now the
Invoice or
Order
module and you will see that the number is empty and not editable, and
when you save the document the number is calculated and a message is shown
with the year and the just generated number for that invoice/order.
Summary
In this lesson we saw how to define properties and values applicable by
default, something very useful if our application will be used in
multi-user or massive access environments. In future lessons we will see
other ways to add business logic, and how to synchronize properties.
Any problem with this lesson? Ask in the forum Everything fine?
Go
to Lesson 16