Row Data Gateway Pattern

This pattern belongs to Data Source Architectural Patterns Catalog and this Catalog belongs to Patterns of Enterprise Application Architecture.

Intent

An object that acts as a Gateway to a single record in a data source. There is one instance per row.
Here Gateway means an object that encapsulates access to an external system or resource.
This object does not contain domain logic methods. If you introduce other methods (in particular domain logic) the object becomes an Active Record Pattern.

How It Works

A Row Data Gateway acts as an object that exactly mimics a single record, such as one database row.  For example, Person class has an id,firstName and lastName fields. 
public class Person{
    private int id;
    private String firstName;
    private String lastName;
    // Getter and Setter methods
}
Person table for Person Java class.
idfirstNamelastName
1RameshFadatare
This pattern holds the data about a row so that a client can then access the Row Data Gateway directly. The gateway acts as a good interface for each row of data. This approach works particularly well for Transaction Scripts.

Explanation

Embedding database access code in in-memory objects can leave you with a few disadvantages. For a start, if your in-memory objects have business logic of their own, adding the database manipulation code increases complexity. Testing is awkward too since if your in-memory objects are tied to a database, tests are slower to run because of all the database access. You may have to access multiple databases with all those annoying little variations on their SQL.
A Row Data Gateway gives you objects that look exactly like the record in your record structure but can be accessed with the regular mechanisms of your programming language. All details of data source access are hidden behind this interface.

When to Use It

This Row Data Gateway widely used with Transaction Script pattern. In this case, it nicely factors out the database access code and allows it to be reused easily by different Transaction Scripts.

Sample Code

Let's create a Class Diagram for sample code of PersonGateway to demonstrate this pattern.

Here's an example of Row Data Gateway. It's a simple person table.
create table people (ID int primary key, lastname varchar, 
                     firstname varchar, number_of_dependents int)
PersonGateway is a gateway for the table. It starts with data fields and accessors.
class PersonGateway... 

   private String lastName;
   private String firstName;
   private int numberOfDependents;
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public String getFirstName() {
      return firstName;
   }
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
   public int getNumberOfDependents() {
      return numberOfDependents;
   }
   public void setNumberOfDependents(int numberOfDependents) {
      this.numberOfDependents = numberOfDependents;
   }
The gateway class itself can handle updates and inserts.
class PersonGateway... 

   private static final String updateStatementString =
         "UPDATE people " +
         "  set lastname = ?, firstname = ?, number_of_dependents = ? " +
         "  where id = ?";
   public void update() {
      PreparedStatement updateStatement = null;
      try {
         updateStatement = DB.prepare(updateStatementString);
         updateStatement.setString(1, lastName);
         updateStatement.setString(2, firstName);
         updateStatement.setInt(3, numberOfDependents);
         updateStatement.setInt(4, getID().intValue());
         updateStatement.execute();
      } catch (Exception e) {
         throw new ApplicationException(e);
      } finally {DB.cleanUp(updateStatement);
      }
   }
   private static final String insertStatementString =
         "INSERT INTO people VALUES (?, ?, ?, ?)";
   public Long insert() {
      PreparedStatement insertStatement = null;
      try {
         insertStatement = DB.prepare(insertStatementString);
         setID(findNextDatabaseId());
         insertStatement.setInt(1, getID().intValue());
         insertStatement.setString(2, lastName);
         insertStatement.setString(3, firstName);
         insertStatement.setInt(4, numberOfDependents);
         insertStatement.execute();
         Registry.addPerson(this);
         return getID();
      } catch (SQLException e) {
         throw new ApplicationException(e);
      } finally {DB.cleanUp(insertStatement);
      }
   }
To pull person rows out of the database, we have a separate PersonFinder. This works with the gateway to create new gateway objects.
class PersonFinder... 

   private final static String findStatementString =
         "SELECT id, lastname, firstname, number_of_dependents " +
         "  from people " +
         "  WHERE id = ?";
   public PersonGateway find(Long id) {
      PersonGateway result = (PersonGateway) Registry.getPerson(id);
      if (result != null) return result;
      PreparedStatement findStatement = null;
      ResultSet rs = null;
      try {
         findStatement = DB.prepare(findStatementString);
         findStatement.setLong(1, id.longValue());
         rs = findStatement.executeQuery();
         rs.next();
         result = PersonGateway.load(rs);
         return result;
      } catch (SQLException e) {
         throw new ApplicationException(e);
      } finally {DB.cleanUp(findStatement, rs);
      }
   }
   public PersonGateway find(long id) {
      return find(new Long(id));
   }
class PersonGateway...

   public static PersonGateway load(ResultSet rs) throws SQLException {
         Long id = new Long(rs.getLong(1));
         PersonGateway result = (PersonGateway) Registry.getPerson(id);
         if (result != null) return result;
         String lastNameArg = rs.getString(2);
         String firstNameArg = rs.getString(3);
         int numDependentsArg = rs.getInt(4);
         result = new PersonGateway(id, lastNameArg, firstNameArg, numDependentsArg);
         Registry.addPerson(result);
         return result;
   }
To find more than one person according to some criteria we can provide a suitable finder method.
class PersonFinder... 

   private static final String findResponsibleStatement =
         "SELECT id, lastname, firstname, number_of_dependents " +
         "  from people " +
         "  WHERE number_of_dependents > 0";
   public List findResponsibles() {
      List result = new ArrayList();
      PreparedStatement stmt = null;
      ResultSet rs = null;
      try {
         stmt = DB.prepare(findResponsibleStatement);
         rs = stmt.executeQuery();
         while (rs.next()) {
            result.add(PersonGateway.load(rs));
         }
         return result;
      } catch (SQLException e) {
         throw new ApplicationException(e);
      } finally {DB.cleanUp(stmt, rs);
      }
   }
The finder uses a Registry Pattern to hold Identity Maps .
We can now use the gateways from a Transaction Script Pattern
PersonFinder finder = new PersonFinder(); 
Iterator people = finder.findResponsibles().iterator();
StringBuffer result = new StringBuffer();
while (people.hasNext()) {
   PersonGateway each = (PersonGateway) people.next();
   result.append(each.getLastName());
   result.append("  ");
   result.append(each.getFirstName());
   result.append("  ");
   result.append(String.valueOf(each.getNumberOfDependents()));
   result.append("

}
return result.toString();

References


Comments