Domain Model Generic Superclass

09 April 2014

There is a common JavaEE pattern for enterprise domain modeling called Domain Generic Superclass which is one specific case of the Design Patter Template:

"With the Template method pattern this happens at compile-time by subclassing the template. Each subclass provides a different concrete algorithm by implementing the template's abstract methods. When a client invokes methods of the template's external interface the template calls its abstract methods (its internal interface) as required to invoke the algorithm."

One of the core pieces of your appplication lays on the business domain and the objects which it is composed. Since many classes share similar fields and attributes, for example ID, and considering their Database relational model, you might want to build an initial generic superclass in order to avoid duplicating boilerplate code on several classes, and being able to enhance reusability and modularization:

DomainEntity Interface:

package com.guilhebl.testapp.domain.template;

import java.io.Serializable;

public interface DomainEntity<ID extends Serializable> { public ID getId(); public void setId(ID id); }

NamedDomainEntity interface, for objects which have a name attribute or NAME column:

package com.guilhebl.testapp.domain.template;

import java.io.Serializable;

public interface NamedDomainEntity<ID extends Serializable> extends DomainEntity<ID> { public String getName(); public void setName(String name); }

Base generic implementations:

package com.guilhebl.testapp.domain.template;

import java.io.Serializable;

import javax.persistence.MappedSuperclass; import javax.persistence.Transient;

@MappedSuperclass public abstract class BaseDomainEntity<ID extends Serializable> implements NamedDomainEntity<ID>, Comparable, Serializable {

<span class="kd">private</span> <span class="n">ID</span> <span class="n">id</span><span class="o">;</span>

<span class="kd">protected</span> <span class="nf">BaseDomainEntity</span><span class="o">()</span> <span class="o">{</span>

<span class="o">}</span>

<span class="nd">@Transient</span>
<span class="kd">public</span> <span class="n">ID</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setId</span><span class="o">(</span><span class="n">ID</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">()</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="kt">int</span> <span class="n">prime</span> <span class="o">=</span> <span class="mi">31</span><span class="o">;</span>
    <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">prime</span> <span class="o">*</span> <span class="n">result</span> <span class="o">+</span> <span class="o">((</span><span class="n">id</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">id</span><span class="o">.</span><span class="na">hashCode</span><span class="o">());</span>
    <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">equals</span><span class="o">(</span><span class="n">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="k">this</span> <span class="o">==</span> <span class="n">obj</span><span class="o">)</span>
        <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">obj</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(!</span><span class="k">this</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">isAssignableFrom</span><span class="o">(</span><span class="n">obj</span><span class="o">.</span><span class="na">getClass</span><span class="o">()))</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="n">BaseDomainEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;</span> <span class="n">other</span> <span class="o">=</span> <span class="o">(</span><span class="n">BaseDomainEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;)</span> <span class="n">obj</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">id</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="k">this</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">other</span><span class="o">.</span><span class="na">getId</span><span class="o">()))</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>    

<span class="nd">@Override</span>
<span class="nd">@Transient</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setName</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">compareTo</span><span class="o">(</span><span class="n">Object</span> <span class="n">arg0</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="k">this</span> <span class="o">==</span> <span class="n">arg0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>            
    <span class="n">BaseDomainEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;</span> <span class="n">other</span> <span class="o">=</span> <span class="o">(</span><span class="n">BaseDomainEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;)</span> <span class="n">arg0</span><span class="o">;</span>                
    <span class="k">return</span> <span class="nf">getName</span><span class="o">().</span><span class="na">compareTo</span><span class="o">(</span><span class="n">other</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
<span class="o">}</span>

}

Where DomainEntity is a generic domain entity superclass and ID a generic Id type that can be serializable, like String, Integer and others.

package com.guilhebl.testapp.domain.template;

import java.io.Serializable;

import javax.persistence.MappedSuperclass; import javax.persistence.Transient;

@MappedSuperclass public abstract class BaseNamedEntity<ID extends Serializable> implements BaseNamedDomainEntity<ID>, Comparable, Serializable {

<span class="kd">private</span> <span class="n">ID</span> <span class="n">id</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">name</span><span class="o">;</span>

<span class="kd">protected</span> <span class="nf">BaseNamedEntity</span><span class="o">()</span> <span class="o">{}</span>

<span class="kd">public</span> <span class="n">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setName</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Transient</span>
<span class="kd">public</span> <span class="n">ID</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setId</span><span class="o">(</span><span class="n">ID</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">toString</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">compareTo</span><span class="o">(</span><span class="n">Object</span> <span class="n">arg0</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="k">this</span> <span class="o">==</span> <span class="n">arg0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>            
    <span class="n">BaseNamedEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;</span> <span class="n">other</span> <span class="o">=</span> <span class="o">(</span><span class="n">BaseNamedEntity</span><span class="o">&lt;</span><span class="n">ID</span><span class="o">&gt;)</span> <span class="n">arg0</span><span class="o">;</span>              
    <span class="k">return</span> <span class="nf">getName</span><span class="o">().</span><span class="na">compareTo</span><span class="o">(</span><span class="n">other</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
<span class="o">}</span>

}

Now any Domain Entity which has a column called "NAME" and a valid ID can extend from BaseNamedEntity,

Example:

FileReference domain entity,

TABLE:

  CREATE TABLE  TB_FILE_REFERENCE
 (
    ID_FILE_REFERENCE VARCHAR(255) NOT NULL,
    NAME VARCHAR(128) NOT NULL,
    TYPE VARCHAR(10) NOT NULL,
    CONSTRAINT TB_FILE_REFERENCE_PK PRIMARY KEY (ID_FILE_REFERENCE)
 );

Class:

package com.guilhebl.testapp.domain;

import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.TableGenerator;

import com.guilhebl.testapp.domain.template.BaseNamedEntity;

@Entity @Table(name = "TB_FILE_REFERENCE") public class FileReference extends BaseNamedEntity<String> {

<span class="kd">private</span> <span class="n">String</span> <span class="n">type</span><span class="o">;</span>

<span class="kd">public</span> <span class="nf">FileReference</span><span class="o">()</span> <span class="o">{}</span>

<span class="nd">@Id</span>
<span class="nd">@TableGenerator</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"genFileReference"</span><span class="o">,</span> <span class="n">table</span><span class="o">=</span><span class="s">"TB_ID_GENERATOR"</span><span class="o">,</span>
        <span class="n">pkColumnName</span><span class="o">=</span><span class="s">"ID_NAME"</span><span class="o">,</span> <span class="n">valueColumnName</span><span class="o">=</span><span class="s">"ID_VAL"</span><span class="o">,</span>
        <span class="n">pkColumnValue</span><span class="o">=</span><span class="s">"TB_FILE_REFERENCE"</span><span class="o">,</span> <span class="n">allocationSize</span><span class="o">=</span><span class="mi">1</span><span class="o">)</span>
<span class="nd">@GeneratedValue</span><span class="o">(</span><span class="n">strategy</span><span class="o">=</span><span class="n">GenerationType</span><span class="o">.</span><span class="na">TABLE</span><span class="o">,</span> <span class="n">generator</span><span class="o">=</span><span class="s">"genFileReference"</span><span class="o">)</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"ID_FILE_REFERENCE"</span><span class="o">,</span> <span class="n">unique</span> <span class="o">=</span> <span class="kc">true</span><span class="o">,</span> <span class="n">nullable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">getId</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setId</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">setId</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
<span class="o">}</span>

<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"TYPE"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getType</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">type</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setType</span><span class="o">(</span><span class="n">String</span> <span class="n">type</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">type</span> <span class="o">=</span> <span class="n">type</span><span class="o">;</span>
<span class="o">}</span>

}

The class has a column called "NAME" which is mapped by it's supperclass, all similar classes which has a name attribute can extend BaseNamedEntity class, enabling extension points in your applitcation for defining common methods and mapping common fields, for domain objects that have common columns in DB or common behavior.


An example of a "thicker" Generic Domain Entity Superclass:

In this example there is specific group of entities where might need to hold version, creation date and other common attributes.

package com.guilhebl.testapp.domain.template;

import java.io.Serializable; import java.util.Date;

import javax.persistence.MappedSuperclass; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version;

@MappedSuperclass public abstract class BaseSemanticDomainEntity<ID extends Serializable> extends BaseNamedEntity<ID> {

<span class="kd">private</span> <span class="n">String</span> <span class="n">semanticName</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">Date</span> <span class="n">created</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">Date</span> <span class="n">lastUpdated</span><span class="o">;</span>
    <span class="kd">private</span> <span class="n">Long</span> <span class="n">version</span><span class="o">;</span>

<span class="kd">protected</span> <span class="nf">BaseDomainEntity</span><span class="o">()</span> <span class="o">{}</span>

<span class="nd">@Version</span>
<span class="kd">public</span> <span class="n">Long</span> <span class="nf">getVersion</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">version</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setVersion</span><span class="o">(</span><span class="n">Long</span> <span class="n">version</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">version</span> <span class="o">=</span> <span class="n">version</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"SEMANTIC_NAME"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getSemanticName</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">semanticName</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setSemanticName</span><span class="o">(</span><span class="n">String</span> <span class="n">semanticName</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">semanticName</span> <span class="o">=</span> <span class="n">semanticName</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Temporal</span><span class="o">(</span><span class="n">TemporalType</span><span class="o">.</span><span class="na">DATE</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">Date</span> <span class="nf">getCreated</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">created</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Temporal</span><span class="o">(</span><span class="n">TemporalType</span><span class="o">.</span><span class="na">DATE</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">Date</span> <span class="nf">getLastUpdated</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">lastUpdated</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCreated</span><span class="o">(</span><span class="n">Date</span> <span class="n">created</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">created</span> <span class="o">=</span> <span class="n">created</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setLastUpdated</span><span class="o">(</span><span class="n">Date</span> <span class="n">lastUpdated</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">lastUpdated</span> <span class="o">=</span> <span class="n">lastUpdated</span><span class="o">;</span>
<span class="o">}</span>

}

Created and Last Updated are common columns to track when this record was created and last time it was changed, semantic name is a common column to store the SEO friendly semantic name of this entity, so URLs based on this entity might be properly displayed in an SEO friendly URI.

Using the @Version enables JPA based automatic versioning, which provides optimistic lock to ensure no confilct occurs at runtime, for example if 2 users are writing the same record, and one of the users have stale data (an old and invalid version of that Record). this will ensure that the record can be changed by only one user at a time, avoiding confilcts. The @Version field is immutable so you might not want to change the version field on any of your domain model instances in order to avoid unpredictable behaviors. Let the underlying JPA PersistenceContext handle the versioning and extend this class for any domain class that might need versioning.

An example of mapping a concrete realization of the above generic superclass models in the database:

 CREATE TABLE  TB_FILE_REFERENCE
 (
    ID_FILE_REFERENCE VARCHAR(255) NOT NULL,
    NAME VARCHAR(128) NOT NULL,
    SEMANTICNAME VARCHAR(128) NOT NULL,
    TYPE VARCHAR(10) NOT NULL,
    CREATED DATE,
    LASTUPDATED DATE;
    VERSION BIGINT;
    CONSTRAINT TB_FILE_REFERENCE_PK PRIMARY KEY (ID_FILE_REFERENCE)
 );
 

comments powered by Disqus