2. 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 3.1 above, there is only one class, representing the object type Book. So, we create a file Book.java in the folder src/pl/model with the following code:

@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
  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 add(...) {...}
  public static List<Book> retrieveAll(...) {...}
  public static Book retrieve(...) {...}
  public static void update(...) {...}
  public static void destroy(...) {...}
  public static void clearData(...) {...}
  public static void createTestData(...) {...}
}

Notice that the model class Book is encoded 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.add 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.destroy for deleting a Book instance.

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

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

These methods are discussed in the following sections.

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 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().

2.1. Storing Book objects in a database table books

The instances of our entity class Book are special Java objects representing "entities" (or business objects), which can be serialized, or, in other words, turned into database records, or rows of a database table. Consequently, they can be shown as a table like in Table 3.2.

Table 3.2. Book objects represented as a table

ISBN Title Year
006251587X Weaving the Web 2000
0465026567 Gödel, Escher, Bach 1999
0465030793 I Am A Strange Loop 2008

The data storage technology used in our example app is MySQL, and the (My)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'll show later in this tutorial 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.

2.2. Creating a new Book instance and storing it

The Book.add 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 add( 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();
}

For storing 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.

2.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;
}

2.4. Updating a Book instance

For updating an existing Book instance we first retrieve it from the database with 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().

2.5. Deleting a Book instance

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

public static void destroy( 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().

2.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 createTestData( 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.

2.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.