$Author: bastafidli $
$Date: 2007/03/11 06:30:45 $
$Revision: 1.25 $
$RCSfile: tutorial_datamodel.html,v $
One of the first steps when developing new subsystems or application should be to decide what the data model looks like. A good understanding of data that the application works with is critical prerequisite for sucessful implementation. Open Core provides several interfaces and classes that speed up implementation of application data model.
We will decide what data entities our data model consists of. We will decide what are the default behavioral characteristics of our data entities, such as:
For each proposed data entity we will choose base class provided by Open Core that fits its characteristics the best. Thanks to the inheritance, the data entities will instantly get many attributes and operations we would have to otherwise design and code manually. The only thing left is to add to the proposed data entities any attributes specific to our application.
DataObject
- base class for all data entities regardless of how they
are persisted and what other behavioral characteristics
they may have. Data object implements
Transfer Object
pattern. The main goal is to encapsulate set of related
attributes as one object so that they can be easily passed
between different modules and tiers of the application. It
supports an architectural
assumption that each data object has a single
unique identifier
called id. It also provides cousin of standard
equals() method called isSame(),
which compares two data objects based only on their domain
attributes. Two data objects are considered the same if they
differ only in id and other similar environment dependent
attributes such as creation or modification timestamps.
BasicDataObject
- base class for data objects that are never modified. It
is derived from
DataObject
and actually implements the id attribute and creation
timestamp. It also implements
architectural
assumption that each data object belongs to some
partition
called domain.
ModifiableDataObject
- base class for data objects that can be modified. It is
derived from
BasicDataObject
and adds modification timestamps used for
optimistic locking.
GenericData
- interface used to expose information about a data object
that can be easily used to identify the data object in the
user interface.
The data model of OpenChronicle is quite simple. It consists
of only two entities.
Blog
represents the chronicle and
Entry
represents the chronicle entry. There can be zero or more blogs
in the system. Blog consists of zero or more entries.
We want to allow user to modify either one of these entities
and therefore both classes will be derived from
ModifiableDataObject.
This way they will also inherit attributes provided by Open
Core that we would have to otherwise implement manually:
Blog
has three additional attributes:
Entry
has four additional attributes:
These attributes are implemented as a ordinary member variables within each class with appropriate getters and setters. Most IDEs (such as Eclipse) can generate the getters and setters for these attributes with just a few mouse clicks. This makes the implementation of the data objects very fast. For example, caption attribute is implemented as follows:
protected String m_strCaption;
public String getComments(
)
{
return m_strComments;
}
Both data objects contain several attributes (for example caption and comments) that can hold variable amount of data. The data objects may have set limits how much data they accept into such fields. But where do the limits come from? Either they are constrained by the business logic or by the limitations set by the persistence store (such as sizes of database columns). Because both of these scenarios are external to the data object, the data object only provides static methods that can be used by the business logic or by the persistence layer to set such limits, for example
protected static int s_iCommentsMaxLength;
public static void setCommentsMaxLength(
int iCommentsMaxLength
)
{
s_iCommentsMaxLength = iCommentsMaxLength;
}
public static int getCaptionMaxLengthStatic(
)
{
return s_iCaptionMaxLength;
}
We will use these methods later to set appropriate limits.
The last step Open Core requires us to do is to implement
method isSame. This method is similar to the
standard Java method equals and it is in fact
called from the default implementation of equals
in the DataObject derived base classes (therefore you do not
have to override equals if you provide isSame. The difference
is, that the isSame method should compare two
data objects only based on the attributes, that are semantically
important and do not depend on an environment. This means that
the attributes, which are dictated by Open Core and are
generated (e.g. id, creation and modification timestamps) do
not need to be compared. It allows us to compare an in-memory
object and object coming from the database and determine if
they contain the same data ignoring the differences caused by
the objects persistence. For Blog data object the
isSame method needs to compare only folder,
caption and comments:
public boolean isSame(
Object oObject
)
{
boolean bReturn = false;
Blog data;
if (oObject == this)
{
bReturn = true;
}
else
{
if (oObject != null && oObject instanceof Blog)
{
data = (Blog) oObject;
bReturn = ((data.getFolder() == null && m_strFolder == null)
|| data.getFolder().equals(m_strFolder))
&& ((data.getCaption() == null && m_strCaption == null)
|| data.getCaption().equals(m_strCaption))
&& ((data.getComments() == null && m_strComments == null)
|| data.getComments().equals(m_strComments));
}
}
return bReturn;
}
Now, when we have a clear idea how our data model looks like we can start implementation of the persistence layer.
Next:
Implementing the persistence layer
Previous:
Overview