Shept » Development Cycle

Development Cycle

Last modified by Andreas Hahn on 2011/06/09 10:23

Building your data models

Whether you start from the scratch or you need to embed an existing relational database schema the SHEPT toolset supports both approaches. When you need to adapt an existing schema it is a consideration if you are allowed to modify the schema. In general its preferable to have the freedom to enhance or modify a schema as some prerequisites will help the object relational layer to operate efficiently. If you start from the scratch without a legacy datamodel to embed you can skip the following paragraph.

Preparing your existing database schema

  • Each database table shall have a primary key

While this is common sense for every database developer you won't get any good results without primary keys as these are a key aspect for efficient data storage and access. Check your existing schemas beforehand and apply prime keys on tables which haven't one. If you can't identify a column with unique data in each row consider adding a surrogate row as identifer. Any of todays relational databases support key generation and Hibernate as our object relational layer offers a couple of options for that.
 

  • Each database table should have a column for versioning

This is more a recommendation than a prerequisite as it depends on your database. With Oracle this is not necessary on the database level but if using Postgres as recommended you should apply a version column either as an Integer or TimeStamp (with milliseconds). The reason for this is that it helps to establish an efficient locking strategy. If different user sessions are working on the same database row once performing an update on a row will check the version column to ensure that the row wasn't modified in the meantime. Technically speaking the generated statement containing the update will look something like

update table set col1=data ... where id=theId and version=savedVersion
On each update the savedVersion will be incremented so the update will fail if the row was modified by another session. We will see later that the Shept toolset provides a smart handling of such error conditions.

Creating your first entity models

Entity models often also referred to as business models are the core objects to keep your data. During the execution of your application these entity models will follow a lifecycle from transient to persistent and usually detached when they are not bound to a database session between subsequent requests of a user's browsing session.
The details are mostly covered behind the scenes by the SHEPT toolset but it doesn't hurt having heard some of the basic terminology either.

In this tutorial we will focus on the practical aspects of howto do it with the Shept toolset and illustrate some examples. With the Eclipse IDE lets first create a package in our project containing all our data models.

  • Create an entity class
  • Create instance variables
  • applying getters and setters
  • applying meta information with annotations
  • applying #hash and #equals
     

Create an entity class

Let's start defining a 'person' entity. From Eclipse menu select
File->New->Other->Class push Next and type the entites name in the dialogs Name field.
The resulting dialog should look like this.
Press Finish to create the entity object.

Create instance variables

Now that we have created the entity model class we will define a couple of common instance variables.
We will declare each variable as private meaning that no direct access other than within the objects class is possible. Note the 'id' and 'version' variables. We have already prepared our model for efficient object relational transformation as noted before.

Applying accessor methods

We need to provide accessors to our instance variables. These accessors will be used by the OO-layer when populating our objects from the database as well when reading and instanciating objects.

No need to type a lot of stuff. The Eclipse IDE will do all the dumb typing for us. With the focus on the Person.java class editor press Alt+Shift+S or right click the mouse over the editor. From the pup-up dialog select Source ...* and then Genrerate Getters and Setters. Select all of the shown instance variables and code generation starts once you press Ok.

Applying ORM Metadata

The Object Relational Mapping needs additonal information to provide the right mappings of the tables columns to our entity models instance variables. This information is provided through Annotations. We could annotate either the instance variables or the getter methods but we stick with the getter methods here because this is the way the Hibernate model generation works when we create a model from an existing database schema.

These are the most important annotations:

  • Mark the class as an entity covered by the ORM layer
    • @Entity marks the class as an entity to be covered by the ORM layer
    • @Table(name="person") tells the orm layer that the effective table name is 'person'.
  • Mark the getter methods for column specific information
    • @Id marks the getter as prime key column (you may have multi column prime keys of cause)
    • @GeneratedValue(strategy=GenerationType.IDENTITY) usually used with @Id to perform key generation (with Postgres it is a sequence but this will be different for another database)
    • @Version marks the getter as a version column
    • @Column(name=YourColumnName) can be ommitted if the column name shall be the same as the getters method name (without the get-Prefix)
    • @Temporal(TemporalType.TIMESTAMP) maps a date or calendar instance variable to the various date representations of the database
    • @Transitional Mark a getter to be not mapped to a database column.

Read more about the full feature set of JPA / Hibernate annotations.

/**
 *
 */

package org.shept.apps.tutorial.demo1.orm;

import java.io.Serializable;
import java.util.Calendar;

/**
 * @version $$Id: $$
 *
 * @author Andi
 *
 */

@Entity
public class Person implements Serializable {

private Integer id;

private Integer version;

private String name;

private String firstName;

private Boolean male;

private Calendar birthdate;

/**
  * @return the id
  */

@Id
public Integer getId() {
 return id;
}

/**
  * @param id the id to set
  */

public void setId(Integer id) {
 this.id = id;
}

/**
  * @return the version
  */

@Version
public Integer getVersion() {
 return version;
}

/**
  * @param version the version to set
  */

public void setVersion(Integer version) {
 this.version = version;
}

/**
  * @return the name
  */

public String getName() {
 return name;
}

/**
  * @param name the name to set
  */

public void setName(String name) {
 this.name = name;
}

/**
  * @return the firstName
  */

public String getFirstName() {
 return firstName;
}

/**
  * @param firstName the firstName to set
  */

public void setFirstName(String firstName) {
 this.firstName = firstName;
}

/**
  * @return the male
  */

public Boolean getMale() {
 return male;
}

/**
  * @param male the male to set
  */

public void setMale(Boolean male) {
 this.male = male;
}

/**
  * @return the birthdate
  */

@Temporal
public Calendar getBirthdate() {
 return birthdate;
}

/**
  * @param birthdate the birthdate to set
  */

public void setBirthdate(Calendar birthdate) {
 this.birthdate = birthdate;
}

}

Applying hash and equals

Remember that in the Java world the root Object class implements equals() and hash() methods which should be overridden by individual subclasses. Equals() shall return true if 2 objects are equal by definition. The default implementation in class Object implements equals() for object identity which is almost always not what you want for your entity models.

To complete our entity model creation we should override the #hash() and #equals() methods inherited from class Object. When do we consider 2 instances of our person entity to be equal by definition ? Its important to analyze your requirements at this point because when our entity objects are stored within collections this is a key decision which information may be duplicate. For the person entity it may be ok to allow all instance variables to be duplicated although its very unlikely to have the same person twice with the same name and birthdate. But we'll stick with a simple approch here to just defining 2 objects with the same prime key to be equal.

To avoid typing we use the build-in wizard in Eclipse to generate #equals() and #hash() methods. Right-click over the Person.class editor and select Source or press Alt+Shift+S. In the pop up menu select Generate hashCode() and equals(). In the pop up dialog we just select the id and press ok to let Eclipse generate both methods.

For a full compliance of your internal object representation with the database integrity enforcement capabilities it is also a good idea to mark any of the (getter-)methods of the instance variables you've selected for hash() and equals() with a @NaturalId annotation. This will advise Hibernate to create a compound unique index on all the @NaturalId annotated columns which in fact will enforce the same policy on your relational database as with your object model.

Read a backgrounder about the business key and hashcode() and equals()  

Tags:
Created by Andreas Hahn on 2010/07/05 09:54

© 2011 shept.org - Andreas Hahn