Course: 1. Getting started | 2. Modeling with Java | 3. Automated testing | 4. Inheritance | 5. Basic business logic | 6. Advanced validation | 7. Refining the standard behavior | 8. Behavior & business logic | 9. References & collections | A. Architecture & philosophy | B. Java Persistence API | C. Annotations

Lesson 2: Modeling with Java

Once you have your environment configured and know how to develop an application with it, it's time to make your own project. In this lesson you will create all the entities required for your project in order to get your application working.
By now I assume that you know how to create a new entity with Eclipse and how to run the application, because you have already read Lesson1: Getting started, right?

Basic domain model

First, we'll create the entities for your Invoicing application. The domain model is rather basic, but enough to learn a lot of interesting things:
modeling_en010.png
We'll start with six classes. Later on we'll add a few more to it. Remember that you already have an initial version of Customer and Product.

Reference (ManyToOne) as descriptions list (combo)

Let's start with the most simple case. We are going to create a Category entity and associate it to the Product, displaying it as a combo.
The code for Category entity is:
package org.openxava.invoicing.model;
 
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;
import org.openxava.annotations.*;
 
@Entity
public class Category {
 
    @Id
    @Hidden // The property is not shown to the user. It's an internal identifier
    @GeneratedValue(generator="system-uuid") // Universally Unique Identifier (1)
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(length=32)
    private String oid;
 
    @Column(length=50)
    private String description;
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
 
}
It only has an identifier and a description property. In this case we use the Universally Unique Identifier (shown as 1) algorithm to generate the id. The advantage of this id generator is that you can migrate your application to another database (DB2, MySQL, Oracle, Informix, etc) without touching your code. The other id generators of JPA rely on the database to generate the id thereby making them not so portable as UUID.
Although we have not included the getters and setters in the above code snippet, the getters and setter must be included in the code. Fortunately Eclipse can write them for us. First choose Source > Generate Getters and Setters... from the Eclipse menu:
modeling020.png

Then click on Select All and then OK to have the getters and setters in your code:
modeling_en030.png
Remember to do this every time you create a new entity or add a new field to an existing one. This utility is also available with the right mouse
button on the entity code, or Ctrl-1 on a field.
Execute the Category module and add some categories:
modeling_en035.png
Now, we'll associate Product with Category: Add the next category reference declaration in your Product entity:
public class Product {
 
    ...
 
    @ManyToOne( // The reference is persisted as a database relationship
        fetch=FetchType.LAZY, // The reference is loaded on demand
        optional=true) // The reference can have no value
    @DescriptionsList // Thus the reference is displayed using a combo
    private Category category; // A regular Java reference
 
    // REMEMBER TO GENERATE THE GETTER AND SETTER FOR category
 
}
This is a plain JPA many-to-one relationship, the one that you can learn more about in appendix B. In this case, thanks to the @DescriptionsList annotation it is displayed as a combo:
modeling_en020.png
Now it's time to complete your Product entity.

Stereotypes

Product entity at least needs to have attributes such as price, etc. Also it would be nice to have photos and a field for remarks. We are going to use stereotypes to do that. A stereotype specifies a special use of a type. For example, you can use String to store names, comments or identifiers, and you can use a BigDecimal to store percentages, money or quantities. Different uses of stereotypes is to mark this specific use.
The best way to understand what a stereotype is, is to see it in action. Let's add price, photo, morePhotos and remarks properties to your Product entity:
@Stereotype("MONEY") // The price property is used to store money
private BigDecimal price; // Include the import java.math.*  BigDecimal is typically used for money
 
@Stereotype("PHOTO") // The user can view and change a photo
@Column(length=16777216) // This size to store big photos
private byte [] photo;
 
@Stereotype("IMAGES_GALLERY") // A complete image gallery is available
@Column(length=32) // The 32 length string is for storing the key of the gallery
private String morePhotos;
 
@Stereotype("MEMO") // This is for a big text, a text area or equivalent will be used
private String remarks;
 
// REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
Note that you have to add the import java.math.* to use BigDecimal. Eclipse helps you to add imports, just put the cursor at the end of the type, at the end of BigDecimal in this case, and press Ctrl - Space Bar.
You have seen how to use stereotypes. Now you only have to use the name of the stereotype and OpenXava will apply special treatment. Execute the module for the Product now, and you will see:
modeling_en040.png
As you can see, each stereotype produces an effect in the user interface. Stereotypes have effects in sizes, validations, editors, etc. Stereotypes also allow you to reuse built-in functionality easily. For example, with a mere marking of a simple string property as @Stereotype(“IMAGES_GALLERY”) you have a full image gallery available. Click on the camera of the morePhotos property and you'll see the gallery:
modeling_en050.png
Apart from these, OpenXava has many other useful built in stereotypes such as LABEL, BOLD_LABEL, TIME, DATETIME, ZEROS_FILLED, HTML_TEXT, IMAGE_LABEL, EMAIL, TELEPHONE, WEBURL, IP, ISBN, CREDIT_CARD, EMAIL_LIST, PASSWORD, MAC, FILE, FILES, ICON, etc.
Now you have Product ready to use. Let's refine the Customer now.

Embeddable

We are going to add Address to our until now pretty naked Customer. The customer address is not shared by other customers, and when the customer is removed his address is removed too. Therefore we'll model the address concept as an embeddable class in this case. You can learn more about this in appendix B.
Add the Address class to your project:
package org.openxava.invoicing.model;
 
import javax.persistence.*;
 
@Embeddable // We use @Embeddable instead of @Entity
public class Address {
 
    @Column(length = 30) // The members are annotated as in entity case
    private String street;
 
    @Column(length = 5)
    private int zipCode;
 
    @Column(length = 20)
    private String city;
 
    @Column(length = 30)
    private String state;
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
 
}
You can see how the regular class has been annotated as @Embeddable. Its properties are annotated in the same way as entities, though embeddable classes do not support all functionality of entities.
Now, you can use Address in any entity. Just add a reference to your Customer entity:
public class Customer {
 
    ...
 
    @Embedded // This is the way to reference an embeddable class
    private Address address; // A regular Java reference
 
    public Address getAddress() {
        if (address == null) address = new Address(); // Thus it never is null
        return address;
    }
 
    public void setAddress(Address address) {
        this.address = address;
    }
 
}
The Address data is stored in the same table as the Customer data. And from a user interface perspective you have a frame around address. If you do not like the frame you only have to annotate the reference with @NoFrame thus:
@Embedded @NoFrame // With @NoFrame no frame is shown for address
private Address address;
Here's the user interface for an embedded reference with and without @NoFrame:
modeling_en060.png
Now that we have the basic entities running, it's time to face the core entity of your application, the Invoice entity. Let's start with it step by step.

Composite key

We are not going to use a composite key for Invoice. It's better to avoid the use of composite keys. You always have the option to use an auto generated hidden id. Although, sometimes you may need to connect to a legacy database or maybe the schema design has been done by someone that likes composite keys, and you must use composite keys albeit it's not the best choice. Therefore, here you will learn how to use a composite key, though at the end we'll change it to a single auto generated id.
Let's start with a simple version of Invoice entity:
package org.openxava.invoicing.model;
 
import java.util.*;
import javax.persistence.*;
import org.openxava.annotations.*;
 
@Entity
@IdClass(InvoiceKey.class) // The id class has all the key properties (1)
public class Invoice {
 
    @Id // Though we have id class it still needs to be marked as @Id (2)
    @Column(length = 4)
    private int year;
 
    @Id // Though we have id class it still needs to be marked as @Id (2)
    @Column(length = 6)
    private int number;
 
    @Required
    private Date date;
 
    @Stereotype("MEMO")
    private String remarks;
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
 
}
If you want to use year and number as a composite key for your Invoice, a way to do so is by marking year and number as @Id (shown as 2), and also to have an id class (shown as 1). The id class must have year and number as properties. You can see InvoiceKey here:
package org.openxava.invoicing.model;
 
public class InvoiceKey implements java.io.Serializable { // The key class must be serializable
 
    private int year; // It contains the properties marked ...
    private int number; // ... as @Id in the entity
 
    public boolean equals(Object obj) { // It must define equals method
        if (obj == null) return false;
        return obj.toString().equals(this.toString());
    }
 
    public int hashCode() { // It must define hashCode method
        return toString().hashCode();
    }
 
    public String toString() {
        return "InvoiceKey::" + year + ":" + number;
    }
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR year AND number
 
}
This code shows some of the requirements of a primary key class, such as to be serializable and to implement hashCode() and equals(). Eclipse can generate them for you with Source > Generate hashCode and equals()...
You already know how to use a composite key, and given that we have control over our schema, in the end we are going to use a UUID identifier for Invoice. Rewrite the Invoice entity to leave it as shown:
package org.openxava.invoicing.model;
 
import java.util.*;
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator; // ADD THIS IMPORT
import org.openxava.annotations.*;
 
@Entity
// @IdClass(InvoiceKey.class) // REMOVE THE @IdClass ANNOTATION
public class Invoice {
 
    // ADD THE oid PROPERTY
    @Id
    @GeneratedValue(generator="system-uuid")
    @Hidden
    @GenericGenerator(name="system-uuid", strategy="uuid")
    @Column(length=32)
    private String oid;
 
    // REMEMBER TO GENERATE THE GETTER AND SETTER FOR oid
 
    // @Id // REMOVE THE @Id ANNOTATION
    @Column(length=4)
    private int year;
 
    // @Id // REMOVE THE @Id ANNOTATION
    @Column(length=6)
    private int number;
 
    ...
 
}
Also delete the InvoiceKey class. Using a hidden auto-generated key for Invoice class has several practical advantages over a composite key: You do not have to write the boring InvoiceKey class, you can modify the invoice number without losing any association from other objects and you can store in the same table the orders and invoices with the year/number repeated.
The code you have is enough to run the Invoice module. Do it and add some invoices if you want. Still a lot of work is remaining to be done in Invoice, like the default values for year, number and date.

Calculating default values

Currently the user needs to type the year, number and date in order to enter an invoice. It would be nice to have default values for them. It's easy to do it using the @DefaultValueCalculator annotation. In the next code you see how we can add the default values for year and date:
@Column(length=4)
@DefaultValueCalculator(CurrentYearCalculator.class) // Current year
private int year;
 
@Required
@DefaultValueCalculator(CurrentDateCalculator.class) // Current date
private Date date;
Also you have to add the next import at the top of your class:
import org.openxava.calculators.*;
From now on when the user clicks on the 'new' button the year field will have the current year, and the date field the current date. These two calculators (CurrentYearCalculator and CurrentDateCalculator) are included in OpenXava. You can explore the org.openxava.calculators package to see other useful built-in calculators.
But sometimes you need your own logic for calculating the default value. For example, for number we want to add one to the last invoice number in the same year. Creating your own calculator with your logic is easy. First, create a package for calculators and call it org.openxava.invoicing.calculators. Then create in it a NextNumberForYearCalculator class, with the next code:
package org.openxava.invoicing.calculators;
 
import javax.persistence.*;
import org.openxava.calculators.*;
import org.openxava.jpa.*;
 
public class NextNumberForYearCalculator implements ICalculator { // A calculator must implement ICalculator
 
    private int year; // This value will be injected (using its setter) before calculating
 
    public Object calculate() throws Exception { // It does the calculation
        Query query = XPersistence.getManager() // A JPA query
            .createQuery("select max(i.number) from Invoice i" + " where i.year = :year"); // The query returns
                                                                // the max invoice number of the indicated year
        query.setParameter("year", year); // We use the injected year as a parameter for the query
        Integer lastNumber = (Integer) query.getSingleResult();
        return lastNumber == null ? 1 : lastNumber + 1; // Returns the last invoice number
                                                        // of the year + 1 or 1 if there is no last number
    }
 
    public int getYear() {
        return year;
    }
 
    public void setYear(int year) {
        this.year = year;
    }
 
}
Your calculator must implement ICalculator interface (and therefore have a calculate() method). We declare a year property to put in the year of the calculation. To implement the logic we use a JPA query. You can learn how to use JPA in appendix B. Now we only have to annotate the number property in the Invoice entity:
@Id @Column(length=6)
@DefaultValueCalculator(value=NextNumberForYearCalculator.class,
    properties=@PropertyValue(name="year") // To inject the value of year from Invoice to
                                           // the calculator before calling to calculate()
)
private int number;
Remember to add the import:
import org.openxava.invoicing.calculators.*;
In this case you see something new, an annotation @PropertyValue. By using this annotation you're saying that the value of year property of the current Invoice will be moved to the property year of the calculator before doing the calculation. Now when ever the user clicks on 'new' the next invoice number is available for the year field. The way of calculating the invoice number is not the best for many concurrent users adding invoices. Don't worry, we'll improve this issue later on.
This is the visual effect of the default value calculators:
modeling_en070.png
Default values are only the initial values. The user can change them if he wishes to.

Regular reference (ManyToOne)

Now that we have all atomic properties ready to use it's time to add relationships with other entities. We'll begin adding a reference from Invoice to Customer, because an invoice without customer is not very useful. Before adding the customer use the Invoice module to remove all the current invoices because we're going to make the customer required, so the old data could fail.
Add the next code to the Invoice entity.
@ManyToOne(fetch=FetchType.LAZY, optional=false) // Customer is required
private Customer customer;
 
// REMEMBER TO GENERATE THE GETTER AND SETTER FOR customer
Nothing more is required. The Invoice module is now like this one:
modeling_en080.png
There is no more work left here now. Let's add the collection of details to your Invoice.

Collection of dependent objects

Usually an invoice needs to have a couple of lines with the details of products, quantities, etc. These details are part of the invoice. They are not shared with other invoices, and when an invoice is deleted its details are also deleted. So, the more natural way of modeling the invoice details is to use a collection of embeddable objects. To do it with JPA, declare a regular collection annotated with @ElementCollection:
@ElementCollection
private Collection<Detail> details;
 
// REMEMBER TO GENERATE THE GETTER AND SETTER FOR details
Using @ElementCollection when the invoice is removed its details are removed too. The details are not saved in the database until the invoice is saved and they are saved all at once.
In order to make this collection work you need to write the Detail class:
package org.openxava.invoicing.model;
 
import javax.persistence.*;
 
@Embeddable
public class Detail {
 
    private int quantity;
 
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private Product product;
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
 
}
Note that Detail is annotated with @Embeddable not with @Entity, you cannot define an @ElementCollection of entities. This @Embeddable class can contain properties and references but not collections.
At the moment we only have quantity and product and that is enough to get the Invoice running with details. The user can add, edit and remove elements from the collection just as in a spreadsheet:
modeling_en090.png
This screenshot emphasizes that the properties to show by default in a collection are the plain ones, that is the properties of references are not included by default. This fact produces an ugly user interface for our collection of invoice details, because only the quantity property is shown. You can fix it using @ListProperties, in this way:
@ElementCollection
@ListProperties("product.number, product.description, quantity")
private Collection<Detail> details;
As you can see, you only have to feed the value for the annotation @ListProperties with the list of the properties you wish, separated by commas. You can use qualified properties, that is, to use the dot notation for accessing properties of references, such as product.number and product.description in this case. The visual result is:
modeling_en100.png

Refining the user interface

Congratulations! You have finished your domain model classes, and you have an application running. Now the user can work with products, categories, customers and even create invoices. In the case of products, categories and customers the user interface is pretty decent, though the user interface for invoices still can be improved a little.
By the way, you already have used some OpenXava annotations for refining the presentation, such as @DescriptionsList, @NoFrame and @ListProperties. In this section we'll use more of these annotations to give the Invoice user interface a better look without too much effort.
Default user interface
This is the default user interface for Invoice:
modeling_en110.png
As you can see, OpenXava shows all the members, one per row, in the order you have declared them in the source code. Also, you can see how in the case of the customer reference the default view of the Customer is created.
We are going to do some improvements. First, we'll define the layout of the members explicitly. In this way we can put year, number and date in the same row. Second, we are going to use a simpler view for Customer. The user does not need to see all the data of the customer when he is entering the invoice.

Using @View for defining layout

For defining the layout of Invoice members in the user interface you have to use the @View annotation. It is easy because you only have to enumerate the members to be shown. Look at the code:
@View(members= // This view has no name, so it will be the view used by default
    "year, number, date;" + // Comma separated means in the same line
    "customer;" + // Semicolon means a new line
    "details;" +
    "remarks"
)
public class Invoice {
At the end, we show all the members of Invoice, but we use commas to separate year, number and date. Thus they are in the same line, producing a more compact user interface, as this:
modeling_en120.png

Using @ReferenceView to refine the user interface for reference

You still need to refine the way the customer reference is displayed, because it displays all the members of Customer, and for entering data for an Invoice, a simpler view of the customer may be better. To do so, you have to define a Simple view in Customer, and then indicate in Invoice that you want to use the Simple view of Customer to display it.
First, let's define the Simple view in Customer:
@View(name="Simple", // This view is used only when “Simple” is specified
    members="number, name" // Shows only number and name in the same line
)
public class Customer {
When a view has a name, as in this case, then that view is only used when that name is specified. That is, though Customer has only this @View annotation, when you try to display a Customer it will not use this Simple view, but the one generated by default. If you define a @View with no name, that view will be the default one, though that is not the case here.
Now, you have to indicate that the reference to Customer from Invoice must use this Simple view. This is done by means of @ReferenceView:
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@ReferenceView("Simple") // The view named 'Simple' is used to display this reference
private Customer customer;
Really simple, you only have to indicate the name of the view of the referenced entity you want to use.
After this the customer reference will be shown in a more compact way:
modeling_en130.png
You can see below how you have refined your Invoice interface.

Refined user interface

This is the result of our refinements in the Invoice user interface:
modeling_en140.png
You have seen how easy it is to use @View and @ReferenceView to get a more compact user interface for Invoice.
Now you have a user interface good enough to start working, and you really have done little work to get it going.

Agile development

Nowadays agile development is no longer a “new and breaking technique”, but an established way to do software development, even the ideal way to go for many people.
If you are not familiar with agile development you can have a look at www.agilemanifesto.org. Basically, agile development encourages the use of feedback from a working product over a careful upfront design. This gives a more prominent role to programmers and users, and minimizes the importance of analysts and software architects.
This type of development also needs a different type of tools. Because you need a working application rapidly. It must be as rapid to develop the initial application as it would be writing the functional description. Moreover, you need to respond to the user feedback quickly. The user needs to see his proposals running in short time.
OpenXava is ideal for agile development because not only does it allow a very rapid initial development, but it also allows you to make changes and see the effects instantly. Let's see a little example of this.
For example, once the user has looked at your application and starts to play with it, he takes into account that he works with books, music, software and so on. All these products have an author, and it would be useful to store the author, and see products by author.
Adding this new feature to your application is simple and rapid. First, create a new class for Author, with this:
package org.openxava.invoicing.model;
 
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;
import org.openxava.annotations.*;
 
@Entity
public class Author {
 
    @Id @GeneratedValue(generator="system-uuid") @Hidden
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(length=32)
    private String oid;
 
    @Column(length=50) @Required
    private String name;
 
    // REMEMBER TO GENERATE THE GETTERS AND SETTERS FOR THE ABOVE FIELDS
 
}
Now, add the next code to the existing Product entity.
@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList
private Author author;
 
// REMEMBER TO GENERATE THE GETTER AND SETTER FOR author
Thus, your Product entity has a reference to Author. Remember to generate the setter and setter for author.
Really you have written a little amount of code. In order to see the effect, you only need to build your project (just press Ctrl-B in your Eclipse), which is just immediate. Then go to the browser and reload the page with the Product module, and you will see there, a combo for choosing the author of the product, just as you see here:
modeling_en150.png
What if the user wants to choose an author and see all his products? Well. This is plain vanilla. You only have to make the relationship between Product and Author bidirectional. Go to the Author class and add the next code:
@OneToMany(mappedBy="author")
@ListProperties("number, description, price")
private Collection<Product> products;
 
// REMEMBER TO GENERATE THE GETTER AND SETTER FOR products
To use Collection you have to add the next import:
import java.util.*;
Remember to generate the getter and setter for products. Now, press Ctrl+B (to build) and refresh the browser with Author module. Choose an author and you will see his products. You have to see something like this:
modeling_en160.png
Yes, you added a new collection, refreshed your browser and there you get the full user interface to manage it. In this case when the user click on Add button he will be able to choose a book from a list of all existing books, moreover when the author is removed his books are still there in the database. You can define another behavior with cascade as REMOVE or ALL, thus:
@OneToMany(mappedBy="author", cascade=CascadeType.REMOVE)
In this way when your user try to add a new book a dialog will be open to entry all the data for the book and with a save button. Moreover, when the author would be removed his books will be removed too. For the author/books case we don't want this behavior, but it can be useful in many cases where the @ElementCollection is not enough.
In this section you have the complete code and steps required to do changes and see the result in the most interactive way. You have seen how OpenXava is an agile tool, ideal for doing agile development.

Summary

In this lesson you have learned how to use simple Java classes to create a Java Web application. With a mere few Java classes required to define your domain, you have a running application. Also, you have learned how to refine the default user interface using some OpenXava annotations. If you want to know all possible ways of refining the user interface with OpenXava look at the reference guide.
Yes! Now you have a working application with little effort. Although this application “as is” can be useful as a CRUD utility or a prototype, you still need to add validations, business logic, user interface behavior, security and so on in order to convert these entities you have written into a business application ready for your user.
You will learn all these advanced topics in the forthcoming lessons.

Download source code of this lesson

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