Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Write once, persist anywhere

Implement a Data Access Object pattern framework

  • Print
  • Feedback

Page 5 of 5

query = select distinct? [object_name]:identifier in
[domain_alias]:identifier where [where_clause]:disjunction;
disjunction = {single} conjunction |
              {multiple} disjunction or conjunction;
conjunction = {single} relational |
              {parenthetical} left_paren disjunction right_paren |
              {multiple} conjunction and relational;
relational = {binary} [left]:value relational_operator [right]:value |
             {between} [value]:value between [lower_bound]:value and
[upper_bound]:value;
relational_operator = {gt} greater_than | 
                      {lt} less_than | 
                      {gte} greater_than_or_equal | 
                      {lte} less_than_or_equal |
                      {eq} equal |
                      {neq} not_equal;       
value = {field} [object_name]:identifier dot [field_name]:identifier |
        {literal} literal;
literal = {double} double | {long} long | {string} string;


This grammar supports query strings of the following formats:

select p in Person where p.firstName = 'James' and p.lastName = 'Carman'
select p in Person where p.lastName between 'Carman' and 'Cartman'
select p in Person where p.lastName >= 'Carman'


Don't worry if you don't understand the grammar; I've left out quite a bit, like definitions of such things as the not_equal token. The grammar is limited, but it should provide an adequate example and support some simple queries. Now, based on this grammar, SableCC generates classes corresponding to each production rule. So, for the between relational production rule (the relational alternative with {between} in front of it), we have the ABetweenRelational class, which looks like:

public final class ABetweenRelational extends PRelational
{
    public PValue getValue();
    public TBetween getBetween();
    public PValue getLowerBound();
    public TAnd getAnd();
    public PValue getUpperBound();
    public void apply( Switch sw );
}


The types starting with T represent tokens in the grammar. The Switch interface defines methods, such as caseABetweenRelational(ABetweenRelational node) that correspond to each type the framework generates. The framework supplies a DepthFirstAdapter class that implements the Switch interface using a "depth-first" (when a node's children are traversed before it is) tree traversal algorithm. In our case, we just need to override the case methods for the concerned types, such as the ABetweenRelational class, as follows:

class QueryProcessor extends DepthFirstAdapter
{
    private final StringBuffer m_SqlBuffer = new StringBuffer();
    String getSqlString()
    {
        return m_SqlBuffer.toString();
    }
    public void caseABetweenRelational(ABetweenRelational node)
    {
        node.getValue().apply( this );
        m_SqlBuffer.append( " BETWEEN " );
        node.getLowerBound().apply( this );
        m_SqlBuffer.append( " AND " );
        node.getUpperBound().apply( this );
    }
}


The QueryProcessor class translates the DAOQL query string into a SQL query string, storing it in the enclosed java.lang.StringBuffer object. Completely translating the DAOQL query string into a SQL string only involves overriding a few more methods of the DepthFirstAdapter class. Using this framework, parsing a DAOQL query string proves fairly straightforward:

QueryProcessor processor = new QueryProcessor();
Lexer lexer = new Lexer( new PushbackReader( new StringReader( queryString )
));
Parser parser = new Parser( lexer );
parser.parse().apply( processor );
String sql  = processor.getSqlString();


The SableCC framework also generates the Lexer and Parser classes. In fact, the code generated by the framework is entirely self-contained. There are no jar files to include on the classpath. Using the generated SQL string to query the database and map the results is somewhat trivial, so I won't include it here.

Future enhancements

While designing and implementing the DAO framework, I discovered some of its limitations. The following sections outline some of those limitations and describe how I may address them in future versions.

Named DaoFactory instances

Many business applications deal with data from multiple data sources. Since the DaoFactory currently implements the Singleton design pattern, business objects can access only one data source. A solution to this problem would provide a variation to the Singleton design pattern by adding the following method to the DaoFactory class:

public static final DaoFactory getInstance( String dataSource ) throws
DaoException;


Adding this method allows one DaoFactory instance per data source.

Explicit transaction management

Although the framework supports transactions via the javax.transaction.UserTransaction interface or container-managed transactions, these services are available only within a J2EE container's context. Situations may arise that mandate transactional support outside a J2EE container's context (such as unit testing, two-tier applications, and so on). Adding the following methods to the Dao interface would allow explicit transaction management without J2EE container overhead:

public void beginTransaction() throws DaoException;
public void commitTransaction() throws DaoException;
public void rollbackTransaction() throws DaoException;


Prepared DAOQL queries

As the framework stands, it doesn't lend itself to reusing commonly used queries, similar to the java.sql.PreparedStatement approach. Adding such functionality to the framework wouldn't be a challenge, but it also wouldn't allow the DAO provider to utilize its data store's performance benefits (such as using java.sql.PreparedStatements). However, by adding a DaoQuery interface and modifying the Dao interface as follows, you might overcome this limitation:

public interface Dao
{
    public void create( Object o ) throws DaoException;
    public DaoQuery retrieve( String queryString ) throws DaoException;
    public void update( Object o ) throws DaoException;
    public void delete( Object o ) throws DaoException;
    public void close() throws DaoException;
}
public interface DaoQuery
{
    public Collection execute() throws DaoException;
    public void setParameter( int parameterIndex, Object parameterValue ) 
throws DaoException;
    public void close() throws DaoException;
}


The DAO of Java

The generic DAO framework outlined here provides a simplistic, pattern-driven, and powerful approach to object persistence. Business objects no longer must worry about object type-specific interfaces. They can use a generic API and let the DAO provider worry about which types are persisted or queried.

About the author

James Carman, an independent consultant, has been working with Java since 1997. He is a Sun Certified Developer for the Java 2 Platform, Sun Certified Web Component Developer for J2EE, and a Sun Certified Architect for Java Technology. He serves as codirector of the Cincinnati Java Users Group, where he frequently gives talks about various Java topics (J2EE architecture, JavaSpaces, JMS (Java Message Service), and Java security, to name a few).

Read more about Enterprise Java in JavaWorld's Enterprise Java section.

  • Print
  • Feedback

Resources