3. Step 2 - Write the Model Code

In the second step, we create the model classes for our app, using a separate Java source code file (with extension .java) for each model class. In the information design model shown in Figure 4.1 above, there is only one class, representing the object type Book. So, we create a file Book.java in the folder src/pl/m with the following code:

package pl.m;

@Entity @Table( name="books")
public class Book {
  @Id private String isbn;
  private String title;
  private int year;
  // default constructor (required for entity classes)
  public Book() {}
  // constructor
  public Book( String isbn, String title, int year) {
    this.setIsbn( isbn);
    this.setTitle( title);
    this.setYear( year);
  }
  // getter and setter methods
  ...
}

Notice that the model class Book is coded as a JPA entity class, which is a JavaBean class enriched with the following JPA annotations:

  1. The annotation @Entity designates a class as an entity class implying that the instances of this class will be stored persistently.

  2. The annotation @Table( name="books") specifies the name of the database table to be used for storing the Book entities. This annotation is optional and defaults to a table name being the same as the class name but in lower case (that is, it would be book in our case).

  3. The @Id annotation marks the standard identifier attribute, implying that the corresponding column of the underlying SQL database table is designated as the PRIMARY KEY. In our example, isbn is used as the standard identifier attribute, and the corresponding isbn column of the books table stores the primary key values.

In the entity class Book, we also define the following static (class-level) methods:

  1. Book.create for creating a new Book instance.

  2. Book.retrieveAll for retrieving all Book instances from the persistent data store.

  3. Book.retrieve for retrieving a specific Book instance from the persistent data store by means of its standard identifier.

  4. Book.update for updating an existing Book instance.

  5. Book.delete for deleting a Book instance.

  6. Book.generateTestData for creating a few example book records to be used as test data.

  7. Book.clearData for clearing the book database table.

The signatures of these methods, which are discussed in more detail in the following subsections, are shown in the following program listing:.

  // getter and setter methods
  public String getIsbn() {return isbn;}
  public void setIsbn( String isbn) {this.isbn = isbn;}
  public String getTitle() {return title;}
  public void setTitle( String title) {this.title = title;}
  public int getYear() {return year;}
  public void setYear( int year) {this.year = year;}
  // CRUD data management methods
  public static void create(...) {...}
  public static List<Book> retrieveAll(...) {...}
  public static Book retrieve(...) {...}
  public static void update(...) {...}
  public static void delete(...) {...}
  public static void clearData(...) {...}
  public static void generateTestData(...) {...}

The JPA architecture for data management and object-to-storage mapping is based on the concept of an entity manager, which provides the data management methods persist for saving a newly created or updated entity, find for retrieving an entity, and remove for deleting an entity.

Since the database access operations of an entity manager are executed in the context of a transaction, our data management methods have a parameter ut of type UserTransaction. Before the entity manager can invoke the database write method persist, a transaction needs to be started with ut.begin(). After all write (and state change) operations have been performed, the transaction is completed (and all changes are committed) with ut.commit().

3.1. Storing Book objects in a database table books

The instances of our entity class Book are Java objects representing "entities" (or business objects), which can be serialized, or, in other words, converted to records (or rows) of a database table, as shown in Table 4.1.

The data storage technology used in our example app is MySQL, and the SQL code used to create the schema for the database table books is the following:

CREATE TABLE IF NOT EXISTS books (
  isbn VARCHAR(10) NOT NULL PRIMARY KEY,
  title VARCHAR(128),
  year SMALLINT
);

While it is also possible to create the database schema manually (with the help of CREATE TABLE statements such as the one above), we show below how the database schema can be automatically generated by JPA. In both cases, the database setup, including a user account and the associated rights (create, update, etc), must be done manually before the JPA application can connect to it.

3.2. Creating a new Book instance and storing it

The Book.create method takes care of creating a new Book instance and saving it to a database with the help of an 'entity manager':

public static void create( EntityManager em, UserTransaction ut, 
    String isbn, String title, int year) throws Exception {
  ut.begin();
  Book book = new Book( isbn, title, year);
  em.persist( book);
  ut.commit();
}

To store the new object, the persist method of the given 'entity manager' is invoked. It is responsible for creating the corresponding SQL INSERT statement and executing it.

3.3. Retrieving all Book instances

The instances of an entity class, such as Book, are retrieved from the database with the help of a corresponding query expressed in the Java Persistence Query Language (JPQL). These queries are similar to SQL queries. They use class names Instead of table names, property names instead of column names, and object variables instead of row variables.

In the Book.retrieveAll method, first a query asking for all Book instances is created, and then this query is executed with query.getResultList() assigning its answer set to the list variable books:

public static List<Book> retrieveAll( EntityManager em) {
  Query query = em.createQuery( "SELECT b FROM Book b", Book.class);
  List<Book> books = query.getResultList();
  return books;
}

3.4. Updating a Book instance

To update an existing Book instance, first we need to retrieve it from the database, by using em.find, and then set those attributes the value of which has changed:

public static void update( EntityManager em, 
    UserTransaction ut, String isbn, String title, 
    int year) throws Exception {
  ut.begin();
  Book book = em.find( Book.class, isbn);
  if (!title.equals( book.getTitle())) book.setTitle( title);
  if (year != book.getYear()) book.setYear( year);
  ut.commit();
}

Notice that, when invoking the find method for retrieving an entity, the first argument must be a reference to the entity class concerned (here: Book.class), so the JPA runtime environment can identify the database table from which to retrieve the entity's data. The second argument must be the value of the entity's primary key.

Notice that in the update case, we do not have to use persist for saving the changes. Saving is automatically managed by the JPA runtime environment when we complete the transaction with ut.commit().

3.5. Deleting a Book instance

A book entity can be deleted from the database as shown in the following example code:

public static void delete( EntityManager em, 
    UserTransaction ut, String isbn) throws Exception {
  ut.begin();
  Book book = em.find( Book.class, isbn);
  em.remove( book);
  ut.commit();
}

To delete an entity from the database, we first need to retrieve it with the help of the find method as in the update case. Then, the remove method has to be invoked by the 'entity manager', and finally the transaction is completed with ut.commit().

3.6. Creating test data

For being able to test our code, we may create some test data and save it in our database. We can use the following procedure for this:

public static void generateTestData( EntityManager em, 
    UserTransaction ut) throws Exception {
  Book book = null;
  Book.clearData( em, ut);  // first clear the books table
  ut.begin();
  book = new Book("006251587X","Weaving the Web", 2000);
  em.persist( book);
  book = new Book("0465026567","Gödel, Escher, Bach", 1999);
  em.persist( book);
  book = new Book("0465030793","I Am A Strange Loop", 2008);
  em.persist( book);
  ut.commit();
}

After clearing the database, we successively create 3 instances of the Book entity class and save them with the help of persist.

3.7. Clearing all data

The following procedure clears our database by deleting all rows:

public static void clearData( EntityManager em, 
    UserTransaction ut) throws Exception {
  ut.begin();
  Query deleteStatement = em.createQuery( "DELETE FROM Book");
  deleteStatement.executeUpdate();
  ut.commit();
}

JPA does not provide a direct method to drop the entire population of a specific class from the database. However, this can be easily obtained by using a JPQL statement as shown in the above code. The JPQL code can be read as: delete all rows from the database table associated with the entity class Book.