Home  >  

Anatomy of an Enterprise Flex RIA Part 9: Nouns and Entity Beans

Author photo
AddThis Social Bookmark Button
riaseries_part9.jpg
In the last installment we looked at our development lifecycle and the dependencies of our persistence layer. Now we're going to look at the actual code that makes up the persistence layer, Actionscript and Java.

A very simple way to pick out the data we need to model and persist in the database is to look over our use cases and pick out the nouns. Nouns are something we can say something about, such as “this book has a certain title.” Verbs are actions that we need to allow our users to perform. Nouns become database tables and classes that map to those tables, and verbs become code that manipulates and acts on instances of the classes; simple, right? (Let’s not get into adjectives and adverbs just right now.)

A quick look at our use cases reveals five important nouns:

  • Books
  • Subjects
  • Authors
  • Users
  • Reservations
For our purposes, let’s treat Authors and Users roughly the same, and call them People. That leaves us with the following:
  • Books
  • Subjects
  • People
  • Reservations

So, Figure 10 shows our database schema based on those nouns.

anatomy_fig10.jpg
Figure 10. Database schema based on the nouns Books, Subjects, People, and Reservations

Now let’s create the Java classes which will be persisted into these tables. I like to follow the Ruby on Rails convention of naming the database tables as the plural of the noun and the object corresponding to the table in the singular.

Here’s the syntax for mapping a class to a table:

@Entity
@Table(name="books")
public class Book {
 ...


Inside src is a directory for the main source of the project, called main. At the same level is a folder for test code, called test. The code goes in the main and test directories and is organized by the type of language—in this case, a java directory holds the Java code in the data project directory, and a java and flex directory in the web project.

This type of class is called an entity, and it’s basically the same thing as a noun (something that has a name). This is one of the three bean types from EJB 3.0. We’ll run into the others, session beans and message-driven beans, soon. Notice that the class doesn’t have to extend anything, which is a change from previous versions of EJB. There are two annotations: one that deals with the at (@) signs next to them, and the @Table annotation which tells EJB to which table this class maps.

After mapping a class to a table, each column needs to be mapped to a set of “accessor methods.” Java convention has a name for a class with one or more of these sets of methods: a bean. A class that follows a “bean pattern” has one or more “properties,” which are private variables, and a “getter” and “setter” method to allow the class to protect access to that property. For instance, here’s some more code from the Book class:

package lcds.examples.bookie.entity;
...
@Entity
@Table(name="books")
public class Book {
 
     private String title;
     
 ...
     @Column(name="title")
     public String getTitle() {
           return title;
      }
     
     public void setTitle(String title) {
           this.title = title;
      }
 ...

The book has a private property, title, and two public methods, getTitle and setTitle. This is the idea of an object in object-oriented programming: data and methods to get at the data, which allow the object to protect the data from outside access. There’s one more @ sign here: the @Column annotation tells EJB that the getTitle and setTitle methods are the accessor methods for a property that goes in a column called “Title”.

One more step I’m going to take is to generalize some of the common parts of the entities into a BaseEntity class. A nice bit of agility we can achieve here is to have an EJB listener put the created and updated timestamps on each record for us. We just have to make sure each table has a “created_on” and “updated_on” column. Now, by setting up BaseEntity with an EntityListener annotation, we’ll get an instance of that object called whenever an EJB life cycle event happens:

package lcds.examples.bookie.entity.listeners;
...
@MappedSuperclass
@EntityListeners({CreateUpdateTimestampEventListener.class})
public abstract class BaseEntity implements Serializable {
     
     private Integer id;
     private Calendar createdOn;
     private Calendar updatedOn;
 
     @Id
     @GeneratedValue(strategy=GenerationType.AUTO)
     @Column(name="id")
     public Integer getId() {
           return id;
      }
 
     public void setId(Integer id) {
           this.id = id;
      }
     
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name="created_on")
     public Calendar getCreatedOn() {
           return createdOn;
      }
 
     public void setCreatedOn(Calendar createdOn) {
           this.createdOn = createdOn;
      }
 
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name="updated_on")
     public Calendar getUpdatedOn() {
           return updatedOn;
      }
 
     public void setUpdatedOn(Calendar updatedOn) {
           this.updatedOn = updatedOn;
      }
 ...

The benefit of creating this BaseEntity is that we can put common code such as the id property here. Every class that extends BaseEntity gets an id for free. Just remember that it needs to be in the table for that class too.

Notice that instead of an @Entity, BaseEntity is annotated as an @MappedSuperclass, which means that it doesn’t go directly in the database, but the fields in it will when they become part of another object, most likely when that object extends the mapped superclass.

As I mentioned, we can put the timestamp code here too. Each table we map to an entity should have a “created_on” and “updated_on” column. If the entity extends BaseEntity, those columns will be populated automatically, which is pretty useful. Notice the new annotation, @Temporal, which tells EJB to map the data in a certain date-type property to a date-type column in the database.

How does the automatic part happen? By using an entity listener. Notice the @EntityListeners annotation, which contains an array of listeners. Entity listeners have methods that EJB calls automatically at different points in the entity’s life cycle—for instance, when the object is created, when it’s updated, or when it’s removed. Table 6 lists and describes the possible life cycle event annotations.

Table 6: life cycle event annotations
life cycle event annotationDESCRIPTION
@PrePersistMethod called before the entity is written to the database
@PostPersistMethod called directly after the entity is written to the database
@PreUpdateMethod called before the entity is updated in the database
@PostUpdateMethod called after the entity is updated in the database
@PreRemove Method called before the entity is removed
@PostRemoveMethod called after the entity is removed

And here’s a look at our entity listener:

public class CreateUpdateTimestampEventListener {
     
     @PrePersist
     public void setCreateDateTime(Object entity) 
               throws IllegalArgumentException {
           try {
                 BaseEntity stampable = (BaseEntity)entity;
                 stampable.setCreatedOn(new GregorianCalendar());
            } catch (Exception e) {
                 ...
            }
      }
     
     @PreUpdate
     public void setUpdateDateTime(Object entity)
               throws IllegalArgumentException {
           try {
                 BaseEntity stampable = (BaseEntity)entity;
                 stampable.setUpdatedOn(new GregorianCalendar());
            } catch (Exception e) {
                 ...
            }
      }
     
}

Figure 11 shows all the mapped entities in the data project in the lcds.examples.bookie.entity package.

anatomy_fig11.jpg
Figure 11. Mapped entities in the data project

Now let’s look at modeling the entities in ActionScript. Here’s the ActionScript class corresponding to the Book entity:

package lcds.examples.bookie.entity {
     
     import lcds.examples.bookie.entity.BaseEntity;
     import lcds.examples.bookie.entity.Subject;
 
     [Managed]
     [RemoteClass(alias="lcds.examples.bookie.entity.Book")]
     public class Book extends BaseEntity {
  
           public var subject:Subject;
           public var title:String;
           public var isbn:String;
           public var author:Person;
  
      }
}

Notice how the package is the same as the Java package, to minimize confusion. The ActionScript Book also extends a BaseEntity, which we’ll see in a second. Notice the [RemoteClass] metadata. Its alias property tells LCDS which Java class it maps to. This is a compiler directive to map that string to this class. Then, whenever the LCDS code in the Flash Player gets an instance of a java class with that qualified name, it knows to look up this class and create an instance of it. No messy or fragile XML encoding and decoding!

The [Managed] metadata tag tells Flex to watch objects of this type for changes which, depending on settings, may automatically make their way back to LCDS.

One last thing: Flex doesn’t have the same concept of a “bean pattern” that Java does (it does have a nicer getter/setter pattern, though), so any properties in ActionScript beans that map to JavaBeans should be public properties. No biggie; just treat the data in these objects nicely and everything will be fine.

The ActionScript entities are in the lcds.examples.bookie.entity package in the bookie-ui project (see Figure 12).

anatomy_fig12.jpg
Figure 12. ActionScript entities

Now that the entities are modeled on both the Flex and the Java side, we’ll be able to call services on the Java side and LCDS will be able to translate ActionScript objects in the call to Java objects. Java will do what it needs to do, and if necessary, Java will translate those objects from Java back to ActionScript. This is only a basic example of what LCDS provides us. We’ll see more of LCDS in action in the coming sections of this shortcut.

Next time we'll look at the service layer from the Java point of view. You can always find the entire series here.

Read more from Tony Hillerson. Tony Hillerson's Atom feed thillerson on Twitter

Comments

1 Comments

Jennifer Lech said:

none

Leave a comment


Tag Cloud

Poll: Mobile Features

What feature do you use most on your mobile phone?

Vote | View Poll Results | Read Related Blog Entry

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.