openxava / documentation / Lesson 15: Multi user default value calculation

Course: 1. Getting started | 2. Basic domain model (1) | 3. Basic domain model (2) | 4. Refining the user interface | 5. Agile development6. 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 properties | 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 15: Multi user default value calculation
JPA callback methods
Multiuser safe default value calculation
Summary
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.

Download source code of this lesson

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