Home > Articles > Software Development & Management

  • Print
  • + Share This
  • 💬 Discuss
From the book Solution

Solution

Use a Data Access Object to abstract and encapsulate all access to the persistent store. The Data Access Object manages the connection with the data source to obtain and store data.

The Data Access Object (also known simply as DAO) implements the access mechanism required to work with the data source. Regardless of what type of data source is used, the DAO always provides a uniform API to its clients. The business component that needs data access uses the simpler interface exposed by the DAO for its clients. The DAO completely hides the data source implementation details from its clients. Because the interface exposed by the DAO to clients does not change when the underlying data source implementation changes, this allows you to change a DAO implementation without changing the DAO client’s implementation. Essentially, the DAO acts as an adapter between the component and the data source.

The DAO is implemented as a stateless. It does not cache the results of any query execution, or any data that the client might need at a later point. This makes the DAOs lightweight objects and avoids potential threading and concurrency issues. The DAO encapsulates the details of the underlying persistence API. For example, when an application uses JDBC, the DAO encapsulates all the JDBC usage inside the data access layer and does not expose any exception, data structure, object, or interface that belongs to the java.sql.* or javax.sql.* packages to the clients outside the data access layer.

Structure

The diagram in Figure 8.1 shows the structure of the Data Access Object pattern.

Figure 8.1 Data Access Object Class Diagram

Participants and Responsibilities

Figure 8.2 Data Access Object Sequence Diagram (part 1)

The diagram in Figure 8.2 shows the interaction of the various participants of the Data Access Object pattern for a basic operation to get data from a data source. Figure 8.3 shows part two of the diagram.

Figure 8.3 Data Access Object Sequence Diagram (part 2)

Client

The Client is an object that requires access to the data source to obtain and store data. The Client can be a Business Object, a Session Façde, an Application Services (357), a Value List Handler (444), a Transfer Object Assembler (433), or any other helper object that needs access to persistent data.

DataAccessObject

The DataAccessObject is the primary role object of this pattern. The DataAccessObject abstracts the underlying data access implementation for the Client to enable transparent access to the data source. The DataAccessObject implements create (insert), find (load), update (store), and delete operations.

DataSource

The DataSource represents a data source implementation. A DataSource could be a database, such as an RDBMS, OODBMS, XML repository, flat file system, and so on. A DataSource can also be another system (legacy/mainframe), service (B2B service or credit card bureau), or some kind of repository (LDAP).

ResultSet

The ResultSet represents the results of a query execution. For an RDMBS DataSource, when an application is using JDBC API, this role is fulfilled by an instance of the java.sql.ResultSet.

Data

The Data represents a transfer object used as a data carrier. The DataAccessObject can use a Transfer Object (415) to return data to the client. The DataAccessObject could also receive the data from the client as a Transfer Object (415) to update the data in the data source.

Strategies

Custom Data Access Object Strategy

When you decide to write your own data access layer, you can use the Data Access Object and the various strategies described in this section. Note that most of these strategies are complementary, and can be combined to make your Data Access Objects (462) more powerful and flexible. However, some of these strategies might add too much complexity to your implementation, so use them judiciously.

In its most basic form, a Data Access Object provides the operations to create, delete, update, and find data in a database. A sample implementation for this is shown in Example 8.2. The Data Access Object typically exchanges data with its clients using Transfer Objects (415), as shown in Example 8.1. Furthermore, a Data Access Object also provides other methods to find multiple results (finder methods) to return a List or other Collection objects.

The sample code in Example 8.1 shows a Transfer Object (415) for customer data used by the CustomerDAO. The CustomerDAO sample implementation is shown in Example 8.2 with the methods to create, update, find, and delete a customer record from the database.

Example 8.1 CustomerTO.java: Transfer Object Used by CustomerDAO and Its Clients

1	package com.corej2eepatterns.to;
2	
3	public class CustomerTO implements java.io.Serializable {
4		private String id;
5		private String name;
6		private String address;
7		. . .
8	
9		public String getId(){ return id; }
10		public void setId(String id){ this.id = id; }
11		public String getName(){ return name; }
12		public void setName(String name){ this.name = name; }
13		public String getAddress(){ return address; }
14		public void setAddress(String address){
15			this.address = address;
16		}
17		// other getters and setters
18		. . . 
19	}

Example 8.2 CustomerDAO.java: Data Access Object

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	public class CustomerDAO {
6		protected static final String FIELDS_INSERT = 
7				"customer_name, customer_address, " + 
8				"customer_contact, customer_phone, customer_email";
9	
10		protected static final String FIELDS_RETURN = 
11				"customer_id, " + FIELDS_INSERT;
12	
13		protected static String INSERT_SQL =
14				"insert into customer ( " + FIELDS_INSERT +
15				" ) " + "values ( ?, ?, ?, ?, ?)";
16	
17		protected static String SELECT_SQL = "select " +
18				FIELDS_RETURN +
19				" from customer where customer_id = ? ";
20	
21		protected static String UPDATE_SQL =
22				"update customer set customer_name = ?, " +
23				"customer_address = ?, customer_contact = ?, " +
24				"customer_phone = ?, customer_email = ? " +
25				"where customer_id = ? ";
26	
27		protected static String DELETE_SQL =
28				"delete from Customer where customer_id = ? ";
29	
30		// the data source used to connect to the back-end database
31		private DataSource datasource;
32	
33		public CustomerDAO() throws DAOException {
34			try {
35				// Shown here for clarity only. Typically, looking
36				// up a data source is done by a Service Locator
37				// and the DAO just uses the Service Locator to
38				// obtain a data source.
39				InitialContext initialContext =
40						new InitialContext();
41				datasource = (DataSource) initialContext.lookup(
42						OracleDAOFactory.DATASOURCE_DB_NAME);
43			} catch (NamingException e) {
44				throw new DAOException (
45						"Cannot locate data source at " + 
46						DAOFactory.DATASOURCE_DB_NAME, e);
47			}
48		}
49	
50		public String create(CustomerTO cust) throws DAOException {
51			// initialize variables
52			Connection con = getConnection();
53			String customerId = null;
54	
55			PreparedStatement prepStmt = null;
56			try {
57				// create and setup statement
58				prepStmt = con.prepareStatement(INSERT_SQL);
59				int i = 1;
60				prepStmt.setString(i++, cust.getName());
61				prepStmt.setString(i++, cust.getAddress());
62				. . .
63	
64				// execute the statement
65				prepStmt.executeUpdate();
66	
67				// obtain the newly created customer id value
68				. . .
69	
70			} catch (Exception e) {
71				// handle exception
72			} finally {
73				// close connections
74			}
75	
76			// return the newly created customer id value
77			return customerId;
78		}
79	
80		public CustomerTO find(String customerId)
81			throws DAOException {
82			// initialize variables
83			CustomerTO cust = null;
84			Connection con = getConnection();
85			PreparedStatement prepStmt = null;
86			ResultSet rs = null;
87	
88			try {
89				// setup statement and retrieve results
90				prepStmt = con.prepareStatement(SELECT_SQL);
91				prepStmt.setString(1, customerId);
92				rs = prepStmt.executeQuery();
93				if (rs.next()) {
94					//create the transfer object using data from rs
95					cust = new CustomerTO();
96					cust.setId(rs.getString(1));
97					cust.setName(rs.getString(2));
98					. . . 
99				}
100			} catch (Exception e) {
101				// handle exception
102			} finally {
103				// close connections
104			}
105			return cust;
106		}
107	
108		public void update(CustomerTO cust) throws DAOException {
109			Connection con = null;
110			PreparedStatement prepStmt = null;
111			try {
112				// prepare statement
113				con = getConnection();
114	
115				prepStmt = con.prepareStatement(UPDATE_SQL);
116				int i = 1;
117	
118				// add fields first
119				prepStmt.setString(i++, cust.getName());
120				prepStmt.setString(i++, cust.getAddress());
121				. . .
122	
123				// now add where parameters
124				prepStmt.setString(i++, cust.getId());
125				int rowCount = prepStmt.executeUpdate();
126				prepStmt.close();
127				if (rowCount == 0) {
128					throw new DAOException(
129						"Update Error:Customer Id:" + cust.getId());
130				}
131			} catch (Exception e) {
132				// handle exception
133			} finally {
134				// close connections
135			}
136		}
137	
138		public void delete(String customerId) throws Exception {
139			// setup variables
140			Connection con = getConnection();
141			PreparedStatement prepStmt = null;
142	
143			try {
144				// execute database update
145				prepStmt = con.prepareStatement(DELETE_SQL);
146				prepStmt.setString(1, customerId);
147				prepStmt.executeUpdate();
148			} catch (Exception e) {
149				// handle exception
150			} finally {
151				// close connections
152			}
153		}
154	
155		// other methods for finders, etc.
156		. . .
157	}

Data Access Object Factory Strategies

You can make Data Access Object creation highly flexible by adopting the Abstract Factory [GoF] and the Factory Method [GoF] patterns.

When applications use a single type of persistent store (such as Oracle RDBMS), and there is no need to change the underlying storage from one implementation to another, implement the DAO Factory Method strategy to produce a variety of DAOs needed by the application.

Example 8.3 OracleDAOFactory.java: Data Access Object Strategy Using Factory Method [GoF]

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	public class OracleDAOFactory extends DAOFactory {
5	
6		// package level constant used look up the 
7		// DataSource name using JNDI
8		static String DATASOURCE_DB_NAME =
9				"java:comp/env/jdbc/CJPOraDB";
10	
11		public static CustomerDAO getCustomerDAO() 
12				throws DAOException {
13			return (CustomerDAO) createDAO(CustomerDAO.class);
14		}
15	
16		public static EmployeeDAO getEmployeeDAO() 
17				throws DAOException {
18			return (EmployeeDAO) createDAO(EmployeeDAO.class);
19		}
20	
21		// create other DAO instances
22		. . .
23	
24		// method to create a DAO instance. Can be optimized to 
25		// cache the DAO Class instead of creating it everytime.
26		private Object createDAO(Class classObj) 
27				throws DAOException {
28			// create a new DAO using classObj.newInstance() or 
29			// obtain it from a cache and return the DAO instance
30		}
31	}

While the DAO Factory Method strategy is most commonly used, you can further extend the flexibility of the factory implementation by adopting the Abstract Factory pattern. This need for further flexibility arises if you’re likely to change persistent stores frequently or if you use more than one. To accomplish this, you must encapsulate multiple types of data sources, such that each Data Access Object factory provides the DAO pattern implementation for one type of persistent store. While most applications typically use a single type of data source (such as Oracle RDBMS), and therefore the DAO Factory Method strategy might be adequate, more sophisticated and commercial frameworks can benefit from the added flexibility of the DAO Abstract Factory strategy.

The sample code for a DAO Abstract Factory strategy is shown in Example 8.4.

Example 8.4 DAOFactory.java: Data Access Object Factory Strategy Using Abstract Factory [GoF]

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	// Abstract class DAO Factory
6	public abstract class DAOFactory {
7	
8		// List of DAO types supported by the factory
9		public static final int CLOUDSCAPE = 1;
10		public static final int ORACLE = 2;
11		public static final int SYBASE = 3;
12		. . .
13	
14		// There will be a method for each DAO that can be 
15		// created. The concrete factories will have to 
16		// implement these methods.
17		public abstract CustomerDAO getCustomerDAO()
18				throws DAOException;
19		public abstract EmployeeDAO getEmployeeDAO()
20				throws DAOException;
21		. . .
22	
23		public static DAOFactory getDAOFactory(int whichFactory) {
24			switch (whichFactory) {
25			case CLOUDSCAPE:
26				return new CloudscapeDAOFactory();
27	 		case ORACLE:
28				return new OracleDAOFactory();
29			case SYBASE:
30				return new SybaseDAOFactory();
31			. . .
32			default: 
33				return null;
34			}
35		}
36	}

Transfer Object Collection Strategy

In addition to the basic functions of create, find, update, and delete, most application clients also need a number of methods that can find multiple results based on a search criteria. The Data Access Object can implement a number of finder methods that accept a search criteria and return a specified number of rows with the results. The Data Access Object creates an SQL statement using the transfer object criteria, and executes a query against the data source to obtain a ResultSet. The Data Access Object then processes the ResultSet to retrieve as many matching result rows as requested by the calling client. For each row, the Data Access Object creates a Transfer Object (415) and adds it to a collection that is returned to the client.

This strategy is suitable when the client needs to obtain a small set of results as transfer objects. If the result set obtained from the query execution is large, then this strategy can be greatly resource intensive because it must create a transfer object instance for data in each row. If you need larger results sets, consider using strategies such as CachedRowSet strategy, Read Only RowSet strategy, or RowSet Wrapper List strategy.

A sample implementation of the Transfer Object Collection strategy is shown in Figure 8.5.

Example 8.5 CustomerDAO.java: Transfer Object Collection Strategy

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	public class CustomerDAO {
6	. . .  
7	
8		// Create a list of Transfer Objects and return it
9		public List findCustomers(CustomerTO criteria)    
10				throws DAOException {
11	
12			Connection con = getConnection();
13			ResultSet rs = null;
14			ArrayList custList = new ArrayList();
15			String searchSQLString = getSearchSQLString(criteria);
16	
17			try {
18				con = getConnection();
19				java.sql.Statement stmt =
20						con.createStatement(. . . );.
21				rs = stmt.executeQuery(searchSQLString);
22				while(rs.next()) {
23					//create the transfer object using data from rs
24					cust = new CustomerTO();
25					cust.setId(rs.getString(1));
26					cust.setName(rs.getString(2));
27					. . . 
28	
29					// add the TO to the list
30					custList.add(cust);
31				}
32			} catch (Exception e) {
33				// handle exception
34			} finally {
35				// close connections
36			}
37			return custList;
38		}
39	
40		. . .  
41	
42	}

Cached RowSet Strategy

When performing an operation, such as a search, that returns multiple elements, the Data Access Object certainly has the option of creating a collection of transfer objects and returning it to the client. If the search operation returns a large list, this strategy can become quite expensive with all those transfer object creations. To avoid that performance overhead, you can leverage the JDBC RowSet interface by using a disconnected, cached row set implementation. Row sets can be used for read and updates, and you can choose from a variety of publicly available implementations. Sun’s implementation is CachedRowSet, JdbcRowSet and WebRowSet [SUNRS]; other vendors such as Oracle provide their own RowSet implementations [ORARS]. You can use one of these public implementations if it meets your needs.

Exercise caution when using RowSets, connected or disconnected, with implementations that support the update functionality. If all you need is a results list that you use for read-only purposes (for instance, to display the results in a web page), a better alternative is to use the Read Only RowSet strategy or RowSet Wrapper List strategy.

  • JdbcRowSet - A connected RowSet providing JavaBean semantics.
  • CachedRowSet - A disconnected RowSet, providing JavaBean semantics and robust synchronization mechanisms.
  • WebRowSet - A disconnected RowSet provided synchronization mechanism that interact with XML data sources.
  • FilteredRowSet - A disconnected RowSet that permits a filtered inbound and outbound view on the RowSet data.
  • JoinRowSet - A disconnected RowSet that allows SQL JOIN relationships to be established between multiple CachedRowSets.

A sample DAO that uses a Cached RowSet implementation (sun.jdbc.rowset.CachedRowSet) is shown in Example 8.6.

Example 8.6 CustomerDAO.java: Cached RowSet Strategy

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	public class CustomerDAO {
6		. . .  
7	
8		// Create the CachedRowSet using the 
9		// ResultSet from query execution    
10		public RowSet findCustomersRS(CustomerTO criteria)    
11				throws DAOException {
12	
13			Connection con = getConnection();
14			javax.sql.RowSet rowSet = null;
15			String searchSQLString = getSearchSQLString(criteria);
16			try {
17				con = getConnection();
18				java.sql.Statement stmt =
19						con.createStatement(. . . );.
20				java.sql.ResultSet rs =
21						stmt.executeQuery(searchSQLString);
22				rowSet = new CachedRowSet();
23				rowSet.populate(rs); 
24			} catch (SQLException anException) {
25				// handle exceptionノ
26			} finally {
27				con.close();
28			}
29			return rowSet;
30		}
31	
32		. . .  
33	
34	}

Read Only RowSet Strategy

While the Cached RowSet strategy is useful for creating any kind of RowSet, most J2EE applications need only the read-only functionality of a RowSet. Instead of using an updateable RowSet implementation, you can implement your own RowSet that caters only to read-only functionality. This strategy provides an example of such a RowSet implementation called Read Only RowSet. The class diagram in Figure 8.4 shows the structure of the Read Only RowSet strategy.

The Client asks the DataAccessObject to perform a search. The DataAccessObject typically fulfills the role of the Client itself, as explained in the Rowset Wrapper List strategy.

Figure 8.4 Read Only Rowset Strategy Class Diagram

The sequence diagram in Figure 8.5 shows how the Read Only RowSet strategy works.

Figure 8.5 Read Only RowSet Strategy Sequence Diagram

The Client uses a DataAccessObject to execute a search query. The DataAccessObject executes the query and obtains a ResultSet, which it uses to create an instance of ReadOnlyRowSet. The ReadOnlyRowSet extracts values from the given ResultSet and stores them in its own array shown as DataRows.

As the DataRows list is being populated, the ReadOnlyRowSet extracts only as many rows from the ResultSet as the Client requested. Once the rows are extracted and the DataRows list is populated with the requested number of rows, the connection is closed and the ResultSet is no longer needed. Hence, the ReadOnlyRowSet described in this strategy is a disconnected cached RowSet implementation.

The following code example shows the implementation of the Read Only RowSet strategy. The sample code in Example 8.7, a Data Access Object, creates a ReadOnlyRowSet instance obtaining a ResultSet instance after a query execution. The client of the CustomerDAO Data Access Object invokes the findCustomersRORS() method and passes a CustomerTO transfer object as the search criteria, indicating the maximum number of rows to return using the howManyRows argument.

Example 8.7 CustomerDAO.java: Creating the ReadOnlyRowSet

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	public class CustomerDAO {
6		. . .  
7	
8		// Create the ReadOnlyRowSet using the 
9		// ResultSet from query execution
10		public RowSet findCustomersRORS(CustomerTO criteria, 
11				int startAtRow, int howManyRows)
12				throws DAOException {
13	
14			Connection con = getConnection();
15			javax.sql.RowSet rowSet = null;
16			String searchSQLString = getSearchSQLString(criteria);
17	
18			try {
19				con = getConnection();
20				java.sql.Statement stmt = con.createStatement(. . .);
21				java.sql.ResultSet rs =
22						stmt.executeQuery(searchSQLString);
23				rowSet = new ReadOnlyRowSet();
24				rowSet.populate(rs, startAtRow, howManyRows);
25			} catch (SQLException anException) {
26				// handle exceptionノ
27			} finally {
28				con.close();
29			}
30			return rowSet;
31		}
32	
33		. . .  
34	}

Example 8.8 ReadOnlyRowSet.java: Populating the RowSet

1	package com.corej2eepatterns.dao.rowset;
2	
3	// imports
4	
5	public class ReadOnlyRowSet implements RowSet, Serializable {
6	
7		. . . 
8	
9		private Object[] dataRows;
10	
11		. . .  
12	
13		/** this is a read only row set */
14		public boolean isReadOnly() {
15			return true;
16		}
17	
18		public void setReadOnly(boolean flag) throws SQLException {
19			throw new SQLException(
20					“ReadOnlyRowSet: Method not supported”);
21		}
22	
23		// Populates the rowset without the first startRow rows 
24		// of the ResultSet and with a maximum number
25		// of rows specified by howManyRows 
26		public void populate(ResultSet resultSet, 
27				int startRow, int howManyRows)
28				throws SQLException {
29	
30			// miscellaneous code not shown for brevityノ
31	
32			// Create a list to hold the row values
33			List dataRows = . . . ;
34	
35			// determine the number of columns from the mete data
36			int numberOfColumns =
37					resultSet.getMetaData().getColumnCount();
38	
39			// Discard initial rows if beginAtRow was specified
40			setStartPosition(startAtRow, resultSet);
41	
42			// if number of rows is unspecified, 
43			// get all rows from resultset
44			if (howManyRows <= 0) {
45				howManyRows = Integer.MAX_VALUE;
46			}
47			int processedRows = 0;
48			while ((resultSet.next()) &&
49					(processedRows++ < howManyRows)) {
50				Object[] values = new Object[numberOfColumns];
51	
52				// Read values for current row and save
53				// them in the values array
54				for (int i=0; i<numberOfColumns; i++) {
55					Object columnValue =
56							this.getColumnValue(resultSet, i);
57					values[i] = columnValue;
58				}
59	
60				// Add the array of values to the linked list
61				dataRows.add(values);
62			}
63	
64		} // end of row set constructor
65	
66		. . .
67	
68		// sets the result set to start at the given row number
69		private void setStartPosition(
70				int startAtRow, ResultSet resultSet) 
71				throws SQLException {
72			if (startAtRow > 0) {
73				if (resultSet.getType() !=
74						ResultSet.TYPE_FORWARD_ONLY) {
75					// Move the cursor using JDBC 2.0 API
76					if (!resultSet.absolute(startAtRow)) {
77						resultSet.last();
78					}
79				} else {
80					// If the result set does not support JDBC 2.0
81					// skip the first beginAtRow rows
82					for (int i=0; i< startAtRow; i++) {
83						if (!resultSet.next()) {
84							resultSet.last();
85							break;
86						}
87					}
88				}
89			}
90		}
91	
92		// Reads a column value for the current row and
93		// create an appropriate java object to hold it.
94		// Return null if error reading value or for SQL null.
95		private Object getColumnValue(
96				ResultSet resultSet, int columnIndex) {
97	
98			. . .
99	
100		}
101	
102		// implement the RowSet and ResultInterface methods
103		. . .
104	
105	}

RowSet Wrapper List Strategy

While the Transfer Object Collection strategy is one way to implement your finder methods, it might prove expensive if the query returns a large set of results and you end up creating a large collection of transfer objects, which are used sparingly by the client. Clients typically search and use the first few result items and discard the rest. When an application is executing a query that returns a large set of results, the RowSet Wrapper List strategy might be more efficient though it requires additional work and adds more complexity.

You do not want to expose the RowSet strategies to external clients of the Data Access Object, regardless of what RowSet implementation you choose to use. It is better to encapsulate all usage of any objects that originate in the java.sql and javax.sql packages inside the Data Access Object. Such encapsulation ensures that the client is not dependent on low-level JDBC interfaces, such as ResultSet or RowSet interfaces. The RowSet Wrapper List strategy internally uses the Read Only RowSet strategy to obtain the query results and wraps the RowSet instance to expose the results in an object-oriented way. The wrapping is done by a custom List implementation that provides the traversal and caching functions to manipulate the RowSet in an object-oriented manner by exposing the data in the rowset as objects.

The advantage of the RowSet Wrapper List strategy is that you create and return Transfer Objects (415) only if necessary; that is, if and when the client needs it. On the other hand, for the Transfer Object Collection strategy, the Data Access Object must create all the Transfer Objects (415) before it returns a Collection to the client.

The class diagram in Figure 8.6 shows the interactions of the RowSet Wrapper List strategy.

Figure 8.6 Rowset Wrapper List Strategy Class Diagram

The sequence diagram in Figure 8.7 shows the interactions of the RowSet Wrapper List strategy.

The DataAccessObject, having obtained a ReadOnlyRowSet using the Read Only RowSet strategy, proceeds to create a RowSetWrapperList by passing the ReadOnlyRowSet instance. The DataAccessObject returns the RowSetWrapperList to the Client, which uses that list to traverse the query results.

The RowSetWrapperList implements the List interface to provide the traversal function for the data in the ReadOnlyRowSet. The RowSetWrapperList encapsulates all data access layer functionality away from the Client, which does not have to know anything about RowSet, ResultSet, or any other classes in the java.sql or javax.sql packages. The RowSetWrapperList returns a Transfer Object (415) when the client wants to access the previous or next row in the results.

Figure 8.7 RowSet Wrapper List Strategy Sequence Diagram

The following code examples show the implementation of the RowSet Wrapper List strategy. The sample code in Example 8.9 shows the CustomerDAO creating a RowSetWrapperList instance using the ReadOnlyRowSet obtained in the previous example.

A custom list iterator, DataRowListIterator, is implemented as an inner class of RowSetWrapperList, as shown in Example 8.11.

Finally, a simple DAO client CustomerDAOClient that uses CustomerDAO and the RowSetWrapperList is shown in Example 8.13.

Example 8.9 CustomerDAO.java: Creating the RowSetWrapperList

1	package com.corej2eepatterns.dao;
2	
3	// imports
4	
5	public class CustomerDAO {
6		. . .  
7	
8		public List findCustomersRL(
9				CustomerTO cust, int startAtRow, int howManyRows) 
10				throws Exception{
11			// create the search SQL string 
12			String searchSQLString = getSearchSQLString(cust);
13	
14			// execute the search
15			return executeSearch(searchSQLString,    
16					StartAtRow, howManyRows);
17		}
18	
19		private List executeSearch(
20				String searchSQLString, int startAtRow,
21				int howManyRows)
22				throws Exception {
23	
24			RowSetWrapperList results = null;
25			try {
26				RowSet rowSet = getRORowSet(searchSQLString, 
27						int startAtRow,
28						int howManyRows);
29	
30				results = new RowSetWrapperList (rowSet);
31			} catch (Exception ex) {
32				throw new Exception(ex);
33			} 
34	
35			return results;
36		}
37	
38		private String getSearchSQLString(CustomerTO cust) {
39			// convenience method to create and return
40			// a “SELECT” SQL statement as a String, incorporating
41			// non-null values in the supplied CustomerTO transfer
42			// object into the WHERE clause
43		}
44	
45		. . .
46	
47	}

Example 8.10 RowSetWrapperList.java

1	package com.corej2eepatterns.to.list;
2	
3	// imports 
4	
5	// Read only implementation of the List interface. 
6	// It supports iterators and absolute positioning.
7	
8	public class RowSetWrapperList 
9	implements List, Serializable {
10	
11		// variable to hold a RowSet instance
12		private RowSet rowSet;
13	
14	
15		public RowSetWrapperList(RowSet rowSet) {
16			this.rowSet = rowSet;
17			. . .
18		}
19	 
20		// return the current row as a transfer object
21		public Object get(int index) {
22			try {
23				rowSet.absolute(index);
24			} catch (SQLException anException) {
25				// handle exception
26			}
27			// create a new transfer object and return
28			return
29					TORowMapper.createCustomerTO(this);
30		}
31		. . .
32	
33		// Returns a Sub List of the current list.
34		public List subList(int fromIndex, int toIndex) {
35			// Create a new RowSet with the required rows
36			ReadOnlyRowSet roRowSet = new ReadOnlyRowSet();
37			roRowSet.populate(this.rowSet, fromIndex, toIndex);
38	
39			// Create a new RowSetWrapperList instance and
40			// return it
41			return
42					new RowSetWrapperList(roRowSet);
43		}
44	
45		// Returns an iterator over the elements in this list in 
46		// proper sequence. It is possible to define multiple 
47		// independent iterators for the same RowSetWrapperList
48		// object.
49	
50		public Iterator iterator() {
51			try {
52				rowSet.beforeFirst();
53			} catch (SQLException anException) {
54				System.out.println(
55						"Error moving RowSet before first row." + 
56						anException);
57			}
58	
59			return this.listIterator();
60		}
61	
62		// Create a List Iterator that can iterate over the
63		// rowset 
64		public ListIterator listIterator() {
65			// ListResultIterator is implemented as an inner class 
66			return new DataRowListIterator();
67		}
68	
69		// implement the List interface methods
70	
71		. . .
72	
73	}

Example 8.11 RowSetWrapperList.java: Inner class DataRowListIterator

1	package com.corej2eepatterns.to.lists;
2	
3	// imports 
4	
5	public class RowSetWrapperList 
6	implements List, Serializable {
7	
8		. . .
9	
10		private class DataRowListIterator implements ListIterator {
11			int currentRow=0;
12			// sets the rowset cursor to next and returns
13			// the Transfer object
14			public Object next() {
15			// get transfer object for next row of RowSetWrapperList
16			currentRow++; 
17			return this.get(currentRow);  
18		}
19	
20		public Object previous() {
21			// get transfer object for previous row of
22			// RowSetWrapperList  
23			currentRow--; 
24			return this.get(currentRow);  
25	
26		}
27	
28		// Implement the List Iterator interface
29		public boolean hasNext() {
30			// Check the cursor position in the rowSet using
31			// isLast, isAfterLast, isEmpty methods and
32			// return true or false accordingly
33		}
34	
35		public boolean hasPrevious() {
36			// Check the cursor position in the rowSet using
37			// isFirst, isBeforeFirst, isEmpty methods and
38			// return true or false accordingly
39		}
40	
41		// implement other ListIterator methods
42	
43		public int nextIndex() {
44			. . .
45		}
46	
47		public int previousIndex() {
48			. . .
49		}
50	
51		// optional methods not implemented throw
52		// UnsupportedException
53		public void set(Object o) {
54			throw new UnsupportedOperationException();
55		}
56	
57		public void add(Object o) {
58			throw new UnsupportedOperationException();
59		}
60		. . .
61	}

Example 8.12 TORowMapper.java: A Sample Implementation

1	package com.corej2eepatterns.util;
2	
3	// imports
4	public class TORowMapper {
5	
6		// create Customer TO
7		public CustomerTO createCustomerTO(RowSet rowSet) {
8			CustomerTO to = new CustomerTO();
9			to.setId(getString(rowSet), 0);
10			to.setName(getString(rowSet), 1);
11			. . .
12		}
13	
14		// create other TOs
15		. . .
16	
17		// implement primitive methods used by create methods
18		protected boolean wasNull(RowSet rowSet) {
19			try {
20				return rowSet.wasNull();
21			} catch (SQLException e) {
22				throw new RuntimeException(e.getMessage());
23			}
24		}
25	
26		protected String getString(RowSet rowSet, int columnIndex) {
27			try {
28				return rowSet.getString(columnIndex);
29			} catch (SQLException e) {
30				throw new RuntimeException(e.getMessage());
31			}
32		}
33	
34		protected boolean getBoolean(
35				RowSet rowSet, int columnIndex) {
36			try {
37				return rowSet.getBoolean(columnIndex);
38			} catch (SQLException e) {
39				throw new RuntimeException(e.getMessage());
40			}
41		}
42	
43		protected java.util.Date getDate(
44				RowSet rowSet, int columnIndex) {
45			try {
46				return rowSet.getDate(columnIndex);
47			} catch (SQLException e) {
48				throw new RuntimeException(e.getMessage());
49			}
50		}
51	
52		// Other primitive getXXX methods for all required
53		// data types
54		. . . 
55	
56	}

Example 8.13 A Sample DAO Client: Using the RowSetWrapperList

1	package com.corej2eepatterns.rowset;
2	
3	// imports
4	
5	public class CustomerDAOClient {
6	
7		. . . 
8	
9		public void search() {
10			try {
11				CustomerDAO dao = new CustomerDAO();
12				CustomerTO criteria = new CustomerTO();
13	
14				criteria.setZip("94539");
15	
16				// search for all customers with Zip code 94539
17				// results to contain a maximum of 1000 matching rows
18				List searchResults = 
19						dao.findCustomersRL(criteria, 0, 999);
20	
21				int resultSize = searchResults.size();
22				for (int rowNum=0; rowNum < resultSize; rowNum++) {
23					CustomerTO customerTO = 
24							(CustomerTO)searchResults.get(rowNum);
25					System.out.println("Customer Row #" +
26							rowNum + " has ");
27					System.out.println("Customer Id = " +
28							customerTO.getId());
29					System.out.println("Name = " +
30							customerTO.getName());
31					. . .
32				}
33	
34				. . .
35	
36				// get a ListIterator
37				ListIterator iterator = searchResults.listIterator();
38	
39				// use the iterator to traverse to next or
40				// previous row
41				. . .
42	
43			} catch (Exception e) {
44				// handle exception
45			}
46		}
47		. . .
48	}
  • + Share This
  • 🔖 Save To Your Account

Discussions

comments powered by Disqus