DAO Consumer's Guide v1.0.11

67
Data Access Object (DAO) Framework A Consumer’s Guide to Using the DAO Framework Date: 5/28/2010 Author: Kevin Scholz Revision: 1.0.11

Transcript of DAO Consumer's Guide v1.0.11

Page 1: DAO Consumer's Guide v1.0.11

Data Access Object (DAO) Framework

A Consumer’s Guide to Using the DAO Framework

Date: 5/28/2010 Author: Kevin Scholz

Revision: 1.0.11

Page 2: DAO Consumer's Guide v1.0.11

Table of Contents

DAO Framework Page 2 of 67

Table of Contents 1  Introduction ...................................................................................................................................................... 3 

1.1  Overview .................................................................................................................................................... 3 1.2  DAO Features & Benefits ............................................................................................................................. 3 1.3  APPID Usage in an Application ..................................................................................................................... 3 1.4  Framework Overview .................................................................................................................................. 4 

2  Inputs to the DAO ............................................................................................................................................. 5 2.1  Contract ..................................................................................................................................................... 5 2.2  Inputs ........................................................................................................................................................ 6 2.3  Selector ..................................................................................................................................................... 7 2.4  NVStream ................................................................................................................................................... 7 2.5  Qualifiers.................................................................................................................................................... 8 2.6  EntryNVStream ........................................................................................................................................... 8 2.7  Targets ...................................................................................................................................................... 9 2.8  ColumnMaps ............................................................................................................................................. 10 

3  BasicDAO ........................................................................................................................................................ 12 3.1  By Row .................................................................................................................................................... 12 3.2  By Object ................................................................................................................................................. 13 3.3  Insert ....................................................................................................................................................... 14 3.4  Update ..................................................................................................................................................... 15 3.5  Delete ...................................................................................................................................................... 16 

4  Results ............................................................................................................................................................ 18 4.1  Calls to wasNull() ...................................................................................................................................... 19 4.2  I R(I) R(R(I)) Notation ...................................................................................................................... 19 

5  Streams .......................................................................................................................................................... 20 5.1  MapStream ............................................................................................................................................... 20 5.2  JavaBeanStream ....................................................................................................................................... 21 5.3  DelegateNVStream .................................................................................................................................... 23 5.4  NVStream Custom Implementation ............................................................................................................. 24 

6  Exceptions ...................................................................................................................................................... 25 6.1  QueryException ........................................................................................................................................ 25 6.2  PropertyNotFoundException ....................................................................................................................... 25 6.3  InsertException ........................................................................................................................................ 25 6.4  UpdateException ....................................................................................................................................... 25 6.5  SecurityException ..................................................................................................................................... 25 6.6  ValidationException ................................................................................................................................... 25 

7  DAO Framework Detailed Flow .......................................................................................................................... 26 7.1  QueryManager .......................................................................................................................................... 27 7.2  DAOConnection ........................................................................................................................................ 27 7.3  QueryHandler ........................................................................................................................................... 27 7.4  The UniversalConnectionHandler ................................................................................................................ 29 7.5  Nesting Connection Handlers ..................................................................................................................... 36 7.6  Nesting Query Handlers ............................................................................................................................. 36 7.7  Controls ................................................................................................................................................... 43 

8  Best Practice Design Patterns ........................................................................................................................... 44 8.1  First Design Pattern .................................................................................................................................. 44 8.2  Using Two Contracts ................................................................................................................................. 50 8.3  Second Desing Pattern .............................................................................................................................. 54 

9  Debugging ...................................................................................................................................................... 61 9.1  Calls to iterator() ...................................................................................................................................... 61 9.2  Calls to Debug.printStream() ..................................................................................................................... 61 9.3  DAO Runtime Debugging ........................................................................................................................... 62 9.4  DAO Logs ................................................................................................................................................. 65 9.5  DAO Monitor ............................................................................................................................................. 67 

Page 3: DAO Consumer's Guide v1.0.11

Introduction Overview

DAO Framework Page 3 of 67

1 Introduction

1.1 Overview This document will cover the Data Access Object (DAO) Framework from a consumer’s viewpoint. A consumer is defined as a developer who will be accessing pre-built DAO libraries to assemble a program for business use. The DAO Framework’s purpose is to access data. This data is primarily in a database like Informix or DB2 but can also be in other locations. The DAO will also be used to isolate the business logic from any type of pure data access regardless of the source (e.g. RPC, Flat Files, XAct, HTTP calls, 3rd Party packages, etc.). 1.2 DAO Features & Benefits The principal advantage to the DAO Framework is that it decouples the Business Logic user from the programming mechanism used to accesses the underlying data. The DAO exposes the same interface to its clients regardless of the underlying API it uses to access the data at its source. Most types of changes to the data will not impact the consumer of the data. This means that the user’s programming model does not have to change when the access mechanism changes or there are modifications to the data’s form or location. In the event of a planned major change to the data’s structure, the DAO component can be versioned to encapsulate these changes into the agreement between the consumer and access of the data. 1.3 APPID Usage in an Application Before using the DAO Framework to access data, the JNDI API was used directly by developers to obtain a connection to the database by looking up a DataSource using a text-based reference (APPID) similar to java:comp/env/MM.SV.PROD-SVC-CATLG.001. The information needed to access the database like the driver class name, userid, password, hostname, port number, instance name, etc. is removed from the application and kept in the JNDI resource. These JNDI resources are still used, but they are handled internally by the DAO Framework. Consumers will no longer have to program the calls to access the data sources.

Page 4: DAO Consumer's Guide v1.0.11

Introduction Framework Overview

DAO Framework Page 4 of 67

1.4 Framework Overview The diagram below is a good starting point for looking at the DAO Framework:

This diagram shows the use of three DAO components, shown in blue, the Contract, Inputs and a Results object. A Contract and Inputs object drive the request into the framework to retrieve data for consumption. A set of Results is given back to the consumer on completion of the request. Your application then takes control and uses the data as required. In the upcoming sections we will cover the two input constructs and results in full detail. Later in section 7 of this document we will dive deeper under the covers of the DAO framework box, shown in green, and see all the pieces inside. For now we’ll use this view to get started.

Page 5: DAO Consumer's Guide v1.0.11

Inputs to the DAO Contract

DAO Framework Page 5 of 67

2 Inputs to the DAO From the diagram shown in section 1.4, there are two DAO objects required to be passed in when calling a DAO query, a Contract and Inputs object. The Contract is used to select the business domain area for data access and the Inputs are used to direct the request. For ease of calling the DAO APIs, these two objects are typically created by using helper classes to reduce the number of discrete objects the consumer application would need to create to get to the data being requested. The DAO Contract and Inputs constructs are the way consumers identify the data required to build their application. They are the heart of the loosely coupled approach used by the DAO Framework. Because of this, it is rich in possible options to account for any type of data amalgamation that may need to be requested. The section below will show the various ways a Contract and Inputs object can be constructed. 2.1 Contract First, lets discuss the DAO Contract which is the highest level identifier used for classifying the data being requested. Its purpose is to organize data into specific business domains. These domains will be tied closely to the Retail Process Maps and Activities being assembled within THD. The definition of a Contract is shown below:

public interface Contract { /** The name of the contract */ public String getContractName(); /** The version id of the contract */ public int getVersionID(); /** The business use of the contract - used for authentication and tracing */ public BusinessUse getBusinessUse(); /** The priority requested for this contract */ public Priority getPriority() throws QueryException; }

Let’s take a look at the interface definition piece by piece. First, let’s look at the first three methods:

public String getContractName(); public int getVersionID(); public BusinessUse getBusinessUse();

The first method, getContractName(), is used to retrieve the Contract’s name. The name is the unique identifier used to differentiate between different business domains. The second method, getVersionID(), is used to identify the specific version of the contract being used. The third method, getBusinessUse(), defines the unique identifier assigned to the caller of the DAO. The BusinessUse class is essentially an integer based identifier that is used for security and logging purposes. It helps track the usage of a DAO component back to an application in the event of failures or performance problems. Continuing with the example, let’s look at the remaining method:

public Priority getPriority() throws QueryException;

The getPriority() method will return the details about the priority assigned to a contract’s execution. For the DAO’s initial implementation, the Priority is stubbed in but not used. How to create a new Contract is shown below:

Contract contract = new BasicContract( "Customer", 5, 1 );

Page 6: DAO Consumer's Guide v1.0.11

Inputs to the DAO Inputs

DAO Framework Page 6 of 67

The BasicContract class is an implementation of the Contract Interface. The “Customer” value is the name of the contract being requested. The number 5 is the business use ID and the number 1 is the contract’s version. There are several constructors available for use, but the one shown above is typical one that is used. Not much more about a Contract needs to be stated. In short, the Contract’s name and version, along with the business use identifier, will be provided to the consumer to be able to call the DAO Framework. Please look to the DAO team for this information. 2.2 Inputs A DAO Inputs object is used to fully specify the data being passed into the DAO Framework to invoke an operation. Below is the definition of an Inputs object:

/** * Defines a Series of Input discriminators used to select and execute a query. * This is the main mechanism that is used to direct a query's action from the BL layer. */ public interface Inputs { /** Gets the Selector */ public Selector getSelector() throws QueryException; /** Adds a Qualifier */ public Object addQualifier( String optionName, Object value ) throws QueryException; /** Gets the input stream of properties */ public NVStream getNVStream() throws QueryException; /** Gets any defines target Object */ public Object getTarget() throws QueryException; /** Gets the input ColumnMap */ public ColumnMap getInputColumnMap() throws QueryException; /** Sets the input ColumnMap */ public void setInputColumnMap( ColumnMap columnMap ) throws QueryException; }

As you can see, the Inputs object is a hub for a series of detailed items like a Selector, Qualifiers, Name/Value pairs, a Target, and an input ColumnMap. The combination of these items defines the Inputs for a call into the DAO framework. Let’s look at Inputs more graphically:

The Inputs container is a composite of several components. The Selector and the NVStream are required and the other items shown with dashed lines are optional. Since Inputs is an interface, many concrete classes in the DAO Framework can be used as Inputs. Earlier, we also made reference to helper classes – Inputs are where the helper classes are most commonly used. In the upcoming sections, each of the Inputs’ components will be detailed along with the helper classes. Let’s start by looking at the Selector component.

Page 7: DAO Consumer's Guide v1.0.11

Inputs to the DAO Selector

DAO Framework Page 7 of 67

2.3 Selector A DAO Selector is used to further refine the choice of action or function to be performed against the data. The selector can be more closely compared to a method name in standard object oriented languages. Let’s take a look at the definition for a Selector:

public interface Selector { public String getSelectorName() throws QueryException; public EntryNVStream getQualifiers() throws QueryException; }

The first method, getSelectorName(), is used to retrieve the Selector’s name. The name is the unique identifier for a specific operation to be performed. The name must be unique within the Contract, not null, and not an empty string. For the second method, getQualifiers(), please see the Qualifiers section 2.5 below for more details. Selectors are most commonly created as part of a helper class. Section 2.6 below will show a selector being created as part of an NVStream. First, let’s look at the definition of an NVStream. 2.4 NVStream The second required component in an Inputs object is an NVStream. It contains a set of read only Name/Value pairs. The NV in the name is an abbreviation for Name/Value. The Stream term is used to signify that the properties flow from the Business Logic to the DAO and back. The Name part is always a String identifier which must be unique within each NVStream, not null, and not an empty string. The Value part is a Java type that includes built-in language primitives, Collections, SQL classes, and generic holders of data (i.e. java.lang.Object). Listed below are the supported types in an NVStream:

# Type Extended Meaning 1. java.sql.Array 2. java.io.InputStream AsciiStream 3. java.math.BigDecimal 4. java.io.InputStream BinaryStream 5. java.sql.Blob 6. boolean 7. byte 8. byte[] 9. char

10. java.io.Reader CharacterStream 11. java.sql.Clob 12. java.sql.Date 13. double 14. float 15. int 16. java.util.List<?> 17. long 18. java.util.Map<?, ?> 19. Object Any Java class 20. java.util.Set<?> 21. short 22. String 23. java.sql.Time 24. java.sql.Timestamp 25. java.net.URL

Page 8: DAO Consumer's Guide v1.0.11

Inputs to the DAO Qualifiers

DAO Framework Page 8 of 67

The overall purpose is to contain the input and output values to and from the DAO Framework. You can see there is a rich set of value types to choose from. If the one you need isn’t listed, the Object (#19) allows for any possible type to be used. We’ll see how to create an NVStream in the upcoming EntryNVStream section 2.6 below. For now let’s continue to look at the NVStream itself. The NVStream has a few additional methods that can be used. Please see the list below:

public boolean containsName(String name) throws QueryException;

public boolean wasNull(String name) throws PropertyNotFoundException; public Iterator<String> iterator() throws QueryException; /** * Inner Class used to get the details about a Stream (usually for printing/debugging

* purposes).<p> By default, all the property names are printed. Optionally the property * values can be printed as well.

*/ public static class Debug { public static String printStream( NVStream stream ) throws QueryException public static String printStream( NVStream stream, boolean printDetails )

throws QueryException }

The containsName() method is used to see if a given property name exists in the list of properties. This allows the user to check for existence without generating an exception. The wasNull() method can be used to see if the value of a named property contains a null value. More details on this later on in this document.

Note: The following two methods should be used for debugging purposes only. They can have negative performance implications if used heavily during runtime. This will depend on the underlying implementation of the Stream class being used.

The iterator() method is used to get an java.util.Iterator of all property names in the stream. The Debug.printStream() method(s) can be used to return a printable String representation of the properties in a stream. Example use of this is discussed in section 9.2 below. 2.5 Qualifiers Qualifiers are another set of Name/Value pairs that are contained in the Selector. They are always optional. Qualifiers are used to direct the organization of the data being returned. Examples of qualifier uses would be for things like sorting, grouping, retrieving the first 100 rows or a range of rows like 101-200. Qualifiers are an extended type of NVStream class called an EntryNVStream. Let’s look at that now. 2.6 EntryNVStream In the Selector section above, we learned that qualifiers are an EntryNVStream. This is an extension of the base NVStream class that allows for setting property values into the stream. The definition is shown below:

public interface EntryNVStream extends NVStream { /** Adds a name value pair to the stream of properties */ public Object put( String name, Object value ) throws QueryException; /** Adds a name value pair to the stream of properties */ public Object putAllowNull( String name, Object value ) throws QueryException; }

Page 9: DAO Consumer's Guide v1.0.11

Inputs to the DAO Targets

DAO Framework Page 9 of 67

Remembering back to the NVStream section, we noted that an NVStream is a read only interface. This new class is an extension that adds two additional methods to allow for the construction of a stream. The first method, put(name, value), is used to add a Name/Value property into the stream. If the put() operation replaces an existing property, the method returns the previous value to the caller. If there was no previous value, the method will return null. With the put() operation, the value passed in cannot be null. The second method, putAllowNull(name, value), works just like the put() call but it will allow the value portion of the property to be null. In both methods, the name parameter cannot be null or an empty string. An example of building an EntryNVStream is shown below:

MapStream inputs = new MapStream( "findCustomers" ); inputs.put( "customerName", "Smith"); inputs.put( "zipcode", 90210 ); inputs.addQualifier( "sort", "ascending" );

The MapStream class is used. A MapStream is an Inputs helper class. It implements both the EntryNVStream and the Inputs interfaces. Since it is an EntryNVStream class, it allows us to “put” values into the stream. Let’s review the first few lines of code:

MapStream inputs = new MapStream( "findCustomers" ); inputs.put( "customerName", "Smith"); inputs.put( "zipcode", 90210 );

In the first line above, the “findCustomers” string in the constructor call is setting the selector name. The last two lines are adding Name/Value properties into the stream. Note how the value types differ from line to line. The first property, “customerName”, contains a String value of “Smith” and the second property, “zipcode”, contains an int value of 90210. An NVStream can contain many different types so the put() method call will allow you the same flexibility. Let’s look at the next line:

inputs.addQualifier( "sort", "ascending" );

The addQualifer() call is a helper method that is used to add data organization options to this Inputs object. Remember that qualifiers are ultimately stored within the Selector so this helper method makes them easier to add. As many “put” and “addQualifier” lines as needed can be used and they can vary as to the types of value objects they are adding. Also note that the return values of the “put” call are being ignored since our example code doesn’t care if the values are being replaced. 2.7 Targets Target objects are used to identify a specific object as the “Target” or “Focus” object for a query action. Using a target is optional and will vary based upon the DAO query implementation being used. A Target can be used for many purposes. There are two typically uses cases:

1. To return a primary key object from an insert operation. 2. To identify an object for an Insert, Update or Delete operation without the use of NVStream specific

properties. This document will show an example using Target’s in some of the upcoming sections.

Page 10: DAO Consumer's Guide v1.0.11

Inputs to the DAO ColumnMaps

DAO Framework Page 10 of 67

2.8 ColumnMaps A ColumnMap is used to translate between what the consumer’s application may have used for property names and what the DAO Framework requires as property names. They are used primarily to translate from the underlying database column names into the corresponding business names used when reading from a set of Results. In the DAO Framework there are two places they can be specified, as either an input or output ColumnMap. The structure of the ColumnMap class used is identical only the direction of the translation varies. Input ColumnMaps translate user defined property names into DAO specific names and output ColumnMaps translate DAO property names, like database column names, into consumer business names. When defining an Inputs object you can add a ColumnMap by calling the setInputColumnMap() method. After the assignment is made, the map will be used automatically when properties are read from the Inputs’ NVStream. The other method available is the getInputColumnMap() call which will return the assigned ColumnMap. Let’s take a look at the ColumnMap interface:

public interface ColumnMap { /** Returns the mapped name given an input name */ public String mapName( String inputName ) throws PropertyNotFoundException; /** * Returns an Iterator to visit all the property names in the current ColumpMap */ public Iterator<String> iterator() throws QueryException; /** * Returns a new ColumnMap that is reverse of the current map */ public ColumnMap getReverse() throws QueryException; }

Any ColumnMap implementation must provide these three methods. The first method, mapName(), is used to do the translation from the input property name to the output name. The second method, iterator(), is used to get a list of all of the translations defined in the ColumnMap instance. The last method, getReverse(), is used to return a ColumnMap instance with the translation reversed. It will not reverse the existing map in place. There are several types of implementations the consumer application can choose from including the following:

1. HashColumnMap – uses an underlying java.util.Map 2. EntryColumnMap – allows the map to be built outside of the constructor 3. PassThruColumnMap – passes the values through without any mapping

If you choose to not do any mappings, the ColumnMap can be left as a null instance by not calling the set method shown above. Below is an example use of an input ColumnMap used on a set of Inputs:

public class UsesAnInputColumnMap { private final static int businessUseID = 5; public List getHosts() throws QueryException { MapStream inputs = new MapStream( "readHosts" ); inputs.addQualifier( "ascending", true ); inputs.put( "aHost", "ccaita6s.homedepot.com" ); EntryColumnMap inputMap = new EntryColumnMap(); inputMap.addMapping( "hostName", "aHost" ); inputs.setInputColumnMap( inputMap ); return (List)BasicDAO.getObject( "placementHosts", businessUseID, inputs ); } }

Page 11: DAO Consumer's Guide v1.0.11

Inputs to the DAO ColumnMaps

DAO Framework Page 11 of 67

In the code above, we have highlighted the code used for the input ColumnMap. First an EntryColumnMap is created and then a single mapping is added by calling the addMapping() method. You can add as many mappings as you need by repeatedly calling the addMapping() method. Finally, the mapping was installed onto the set of Inputs by calling the setInputColumnMap() method. Now when the DAO Framework asks for the “hostName” property, the “aHost” property from the NVStream will be used. The reading of the property did not know that the “aHost” property was actually used. The input ColumnMap provided the automatic translation.

Page 12: DAO Consumer's Guide v1.0.11

BasicDAO By Row

DAO Framework Page 12 of 67

3 BasicDAO The BasicDAO class is the DAO Framework’s most often used helper class. It creates all the required items to call the DAO Framework for a single operation. Since all the methods on the class are static, no instance of the BasicDAO class is required. An example BasicDAO call would look something like the following:

return (List)BasicDAO.getObject( "Customer", 1, new MapStream( "readCustomerTypes" ) );

So now let’s look at this class in more detail. The BasicDAO supports the following operations:

# Return Type Method Callback Handler Uses a Transaction

1 void getResult(. . .) ResultsReader - 2 Object getObject(. . .) - - 3 void getObject(. . .) ObjectReader - 4 int insertObject(. . .) - 5 void insertObject(. . .) InsertNotifier 6 int updateObject(. . .) - 7 void updateObject(. . .) UpdateNotifier 8 int deleteObject(. . .) - 9 void deleteObject(. . .) DeleteNotifier

Table 3-1 Basic DAO Methods

The table above illustrates the five types of operations available for DAO Framework calls. The first two types named getResult() and getObject() are used for “Reading” data and vary only on how the caller needs to handle reading the results from the framework. The last three types, named insertObject(), udpateObject(), and deleteObject(), handle the remaining CRUD operations for creating, updating, and deleting data. The next few sections will cover all the individual methods shown in the table above. 3.1 By Row

The first method (#1), getResult() handles reading data from the DAO Framework in a row-by-row fashion. Let’s look at the complete method signature and review the inputs and outputs:

public static void getResult( String contractName, int businessUseID, final Inputs inputs, ResultsReader reader ) throws QueryException

The first thing of note is that the method is public and that it returns void (i.e. no value). So you may be asking yourself – how is the data read row-by-row if nothing is returned? The method “returns” the value(s) to the caller via a ResultsReader that is specified as the fourth parameter. We will get to that shortly.

The first three parameters should already be familiar to you. The Contract’s name, the integer business use ID, and an Inputs object like we just created in the above section. These are used to select and execute the DAO call needed to retrieve the data. The final parameter, ResultsReader is the new item. Most all of the methods in the DAO Framework that allow the user to take control make use of the callback handler pattern. This allows for a hand off of control with a guarantee that the originator is returned to after the “handler” has completed it’s work. This type of pattern allows for the DAO Query Writer to close any and all resources used to access the data after the consumer has read it. Any Java class can simply implement the ResultsReader interface to become a handler.

Page 13: DAO Consumer's Guide v1.0.11

BasicDAO By Object

DAO Framework Page 13 of 67

A ResultsReader handler is used to read data in a row-by-row fashion. The ResultsReader interface has the following definition:

public interface ResultsReader { /** * Read the results row-by-row * @param results the results to be read * @param query the query being executed * @param inputs the inputs to the query * @throws QueryException thrown when something goes wrong */ public void readResults( Results results, Query query, Inputs inputs ) throws QueryException; }

As you can see there is only one method to implement. The implementer can process through the Results parameter that is passed to them to read the data. The Query and Inputs parameters are typically used for reference purposes. The Query parameter represents the class that is executing the DAO details and the Inputs parameter is a handle to the Inputs object sent in to the original call. Below is an example of readResults() call in a simple Java class:

public class SampleReadResults { private final static int businessUseID = 1; public List<String> getCustomerList() throws QueryException { final List<String> list = new ArrayList<String>(); MapStream inputs = new MapStream( "findCustomers" ); BasicDAO.getResult( "Customer", businessUseID, inputs, new ResultsReader() { public void readResults( Results results, Query query, Inputs inputs )

throws QueryException { while ( results.next() ) { list.add( results.getString( "lastName" ) ); } } }); return list; } }

In the above code you can see a call to the BasicDAO.getResult() method. The fourth parameter, the ResultsReader, is created as an anonymous inner class. The readResults() method is implemented and it will be called when the data is ready to be processed. A while loop does the processing of the Results and it reads the “lastName” property value. It then adds each name to the list, and after all the rows are complete, the list is returned. More details about how to read from Results will be shown in the Results section 4 below. 3.2 By Object Items #2 and #3 in Table 3-1 above are for the getting data from the DAO Framework as an Object return value. Let’s look at the complete method signatures and review the inputs and outputs:

#2 - public static Object getObject( String contractName, int businessUseID, final Inputs inputs ) throws QueryException

#3 - public static void getObject( String contractName,

int businessUseID, final Inputs inputs, ObjectReader reader ) throws QueryException

Page 14: DAO Consumer's Guide v1.0.11

BasicDAO Insert

DAO Framework Page 14 of 67

You can note similarities and differences between these two items shown above. Getting an Object from the DAO Framework can be handled by a direct return of type Object from the method (i.e. #2 above) and through the callback handler (i.e. #3 above) just like in the row-by-row section above. Let’s cover them in order. First, in #2 above, the standard set of parameters: Contract’s name, the integer business use ID, and an Inputs object are required. By now you should be familiar seeing these values. The output of the method is handled via the return of an Object. This requires you to cast the Object to some known type based on the specific DAO Technical specifications for a Contract/Selector or use Java introspection. This method isn’t too different from any standard Java method we write all the time.

In #3 above, you’ll notice a similar pattern to that used in the row-by-row example in section 3.1 above. Let’s take a look at the ObjectReader definition:

public interface ObjectReader { /** * Read an object * @param target the object to be read * @param query the query that was executed * @param inputs the inputs to the query * @throws QueryException thrown when something goes wrong */ public void readObject( Object target, Query query, Inputs inputs ) throws QueryException; }

As you can see there is only one method to implement. The implementer simply processes the target parameter that is passed to it. Again, the Query and Inputs parameters are typically used for reference purposes. Below is an example of a very simple readObject() method:

Object returnValue = null; public void readObject(Object target, Query query, Inputs input) throws QueryException { returnValue = target; }

In this example, the target object is stored in a member variable and used later in the application.

Important Note – Large amounts of work other than reading the value from the DAO Framework are to be discouraged inside of the readObject() method. There are other places that an application typically will add their business logic details that will be shown later in this document.

3.3 Insert Items #4 and #5 in Table 3-1 above are used for inserting data via the DAO Framework. These calls always happen inside a transaction. Let’s look at the complete method signatures and review the inputs and outputs: #4 - public static int insertObject( String contractName,

int businessUseID, final Inputs inputs ) throws QueryException

#5 - public static void insertObject( String contractName, int businessUseID, final Inputs inputs, InsertNotifier notifier ) throws QueryException

Inserting via the DAO Framework can be handled by a direct call returning just the count of rows inserted from the method call (i.e. #4 above) and through the callback handler (i.e. #5 above) that can return more information about the event. In #4 above, the output of the method is handled via the return of an int that is the count of rows inserted.

Page 15: DAO Consumer's Guide v1.0.11

BasicDAO Update

DAO Framework Page 15 of 67

In #5 above, you’ll notice a similar pattern to that used in the callback handler examples above. Let’s take a look at the InsertNotifier definition:

public interface InsertNotifier { /** * Notification about an insertion operation * @param target the target of the insertion * @param success true if succeeded, false otherwise * @param count the number of rows inserted * @param query the query that was executed * @param inputs the inputs to the query * @throws QueryException if anything goes wrong when talking to the database */ public void notifyInsert( Object target,

boolean success, int count, Query query, Inputs inputs ) throws QueryException;

}

As you can see there is only one method to implement. Like in the previous examples, the Query and Inputs parameters are typically used for reference purposes. There are three items of interest. First, the target object can be any item that is defined by the Contract / Selector specification for a given query. It is typically used to pass information like a primary key back to the caller after the insert completes. Seconds, the success flag is used to denote a successful completion of the insert that is separate from the count. In some cases a zero count doesn’t denote an error. And third, the count parameter is the number of rows inserted. It is the same value that is returned in item #4 above. Below is an example of a very simple notifyInsert() method:

Object primaryKey; public void notifyInsert(Object target, boolean success, int count, Query query, Inputs inputs)

throws QueryException { if ( success && count > 0 ) {

primaryKey = target; } }

As you can see, the handling of an insert is different from the read methods shown above. Because of this, there is a special type of Exception that can be caught by InsertNotifiers if it is implemented in the DAO specification of the selector being used. It is a subclass of QueryException called InsertException. This can be used to denote that an existing or duplicate row was in the database and an updateObject() call could be used to change the data. This type of usage will vary by application and may not be applicable but it is available to handle these types of situations. Please see more details in the Exceptions section 6 shown below. 3.4 Update Items #6 and #7 in Table 3-1 above are used for the updating data via the DAO Framework. This call always happens inside of a transaction. Let’s look at the complete method signatures and review the inputs and outputs: #6 - public static int updateObject( String contractName,

int businessUseID, final Inputs inputs ) throws QueryException

#7 - public static void updateObject( String contractName, int businessUseID, final Inputs inputs, UpdateNotifier notifier ) throws QueryException

Updating via the DAO Framework can be handled by a direct call returning just the count of rows updated from the method call (i.e. #6 above) and through the callback handler (i.e. #7 above) that can return more information about the event.

Page 16: DAO Consumer's Guide v1.0.11

BasicDAO Delete

DAO Framework Page 16 of 67

The output of the method is handled via the return of an int that is the count of rows updated. In #7 above, you’ll notice a similar pattern to that used in the previous callback handler examples above. Let’s take a look at the UpdateNotifier definition:

public interface InsertNotifier { /** * Notification about an update operation * @param target the target of the update * @param success true if succeeded, false otherwise * @param count the number of rows updated * @param query the query that was executed * @param inputs the inputs to the query * @throws QueryException if anything goes wrong when talking to the database */ public void notifyUpdate( Object target,

boolean success, int count, Query query, Inputs inputs ) throws QueryException;

}

As you can see there is only one method to implement. First, like in the other examples above, the Query and Inputs parameters are typically used for reference purposes. There are three items of interest. First, the target object can be any item that is defined by the Contract / Selector specification for a given query. It is typically used to pass information back to the caller after the update completes. Seconds, the success flag is used to denote a successful completion of the update that is separate from the count. In some cases a zero count doesn’t denote an error. And third, the count parameter is the number of rows updated. It is the same value that is return in item #6 above. Below is an example of a very simple notifyUpdate() method:

boolean targetUpdated; public void notifyUpdate(Object target, boolean success, int count, Query query, Inputs inputs)

throws QueryException {

targetUpdated = success; }

Just like with inserts, there is a special type of Exception that can be caught by updateNotifiers if it is implemented in the DAO specification of the selector being used. It is a subclass of QueryException called UpdateException. This can be used to denote that a row was not already in the database and an insertObject() call could be used to insertion the data. This type of usage will vary by application and may not be applicable but it is available to handle these types of situations. Please see more in the Exceptions section 6 shown below.

3.5 Delete Items #8 and #9 in Table 3-1 above are used for the deleting data via the DAO Framework. This call always happens inside of a transaction. Let’s look at the complete method signatures and review the inputs and outputs: #8 - public static int deleteObject( String contractName,

int businessUseID, final Inputs inputs ) throws QueryException

#9 - public static void deleteObject( String contractName, int businessUseID, final Inputs inputs, DeleteNotifier notifier ) throws QueryException

Deleting via the DAO Framework can be handled by a direct call returning just the count of rows deleted from the method call (i.e. #8 above) and through the callback handler (i.e. #9 above) that can return more information about the event.

Page 17: DAO Consumer's Guide v1.0.11

BasicDAO Delete

DAO Framework Page 17 of 67

In #8 above, the output of the method is handled via the return of an int that is the count of rows deleted. In #9 above, you’ll notice a similar pattern to that used in the previous callback handler examples above. Let’s take a look at the DeleteNotifier definition:

public interface DeleteNotifier { /** * Notification about a deletion operation * @param target the target of the deletion * @param success true if succeeded, false otherwise * @param count the number of rows deleted * @param query the query that was executed * @param inputs the inputs to the query * @throws QueryException if anything goes wrong when talking to the database */ public void notifyDelete( Object target,

boolean success, int count, Query query, Inputs inputs ) throws QueryException;

}

As you can see there is only one method to implement. Like in the other examples above, the Query and Inputs parameters are typically used for reference purposes. The target object can be any item that is defined by the Contract / Selector specification for a given query. It is typically used to pass information back to the caller after the deletion completes. The success flag is used to denote a successful completion of the deletion that is separate from the count. In some cases a zero count doesn’t denote an error. The count parameter is the number of rows deleted. It is the same value that is return in item #8 above. Below is an example of a very simple notifyDelete() method:

boolean targetRemoved; public void notifyDelete(Object target, boolean success, int count, Query query, Inputs inputs)

throws QueryException {

targetRemoved = success; }

The method follows the same pattern as shown in the update section above. There is no special Exception used by delete operations.

Page 18: DAO Consumer's Guide v1.0.11

Results Delete

DAO Framework Page 18 of 67

4 Results

Results objects are the way the DAO Framework passes information back to the consumer when reading data row-by-row. The BasicDAO section 3.1 above described the pattern for reading row-by-row in some detail, now we’ll go into more examples for using Results objects. First, let’s look at the Results interface definition:

public interface Results extends NVStream, Inputs { /** * The inputs to the Query that produced these results * @return the list of inputs */ public Inputs getInputs() throws QueryException; /** * Get the next() row in the stream. Must be called to access the first row of data. * @return true if a next row exists, false otherwise */ public boolean next() throws QueryException; /** * Get the count of rows processed in the stream from calling next() * @return the count of rows processed in the stream from calling next() * @throws QueryException */ public long getRowsProcessed() throws QueryException; /** * Gets the output ColumnMap * @return the output ColumnMap */ public ColumnMap getOutputColumnMap() throws QueryException; /** * Sets the output ColumnMap * @param outputColumnmap the output ColumnMap */ public void setOutputColumnMap(ColumnMap outputColumnmap) throws QueryException; /** * Maps a input column name to and output business name * @param inputName the column name to map * @return the business name */ public String mapOutputName(String inputName) throws QueryException; }

First thing of note is that the Results implements two interfaces, NVStream and Inputs. This means that Results are a stream of properties accessed by the methods described in NVStream section 2.4 above. This will make it easier to use since we have already become somewhat familiar with the NVStream itself. Next you will see that Results also implements the Inputs interface. What this means is that any Results object can be used as the Inputs to another call to the DAO Framework, and can directly provide the properties to drive that call. How this works is that the current row that is being read from the Results is the set of properties for the Inputs. This allows you to easily nest calls to the DAO framework to any level required. This is an extremely powerful construct that permits the consumer to build a complex relationship between individual DAO calls. Nesting calls like this will be covered later in this document. Since Results can be Inputs, you may want to get the reference to the Inputs object that was used to drive the current set of Results you are reading. To do this use the getInputs() method is available. You should already be familiar with how Inputs objects work, so you can access the attributes as needed. The next() method is at the heart of Results object processing. It follows the same usage pattern as java.util.Iterator or java.sql.ResultsSet object. Basically, you are required to call next() to see if a row of data exists. The method returns true or false to indicate if there are properties to be read. This is required for the first row of data as well.

Page 19: DAO Consumer's Guide v1.0.11

Results Calls to wasNull()

DAO Framework Page 19 of 67

Important Note – Results object are typically read directly from the underlying database, using the Results objects for long term access is not allowed. Results are designed to be read and then released. You should not store a Results object into a List or Map structure to be accessed later in your application.

The getRowsProcessed() method is a helper method that keeps count of how many rows were read by calling next(). The counter is updated as part of each next() method call. The remaining methods are for output ColumnMaps. Any output ColumnMap required will be put in place by the DAO writer and any call to retrieve a property will automatically use it. No additional calls or modifications to your code are required. Please refer to the ColumnMaps section 2.8 above for more details on ColumnMaps. 4.1 Calls to wasNull() In the early discussion about NVStreams it was mentioned that there is a call to check if a property wasNull(). This method is very useful when used with Results. Some objects in a database may have default values. This method can be called to check if the underlying value read was null at the source. There is virtually no performance impact to using this method. 4.2 I R(I) R(R(I)) Notation What does the I R(I) R(R(I)) notation mean? Earlier you saw that the getInputs() method on the Results object can return you the set of Inputs directly. Although this is a handy way to read the Inputs values, there is a simpler way to access the properties. What may not be evident when reading property values from a Results stream is that the Inputs property values to the DAO call are already available directly within the same stream. No call to any other method is needed. The notation and details below describe how this works. So let’s explain this notation used to describe this process. The I above represents a set of Inputs and the “ “ represents a call to the DAO Framework. And when the call completes we receive the Results object to read row-by-row, we will call that R. So then the notation R(I) shows that the Results we receive wrap the Inputs used to run the query. Both are available in the same stream of properties. A single request to read a property’s value would check the Results first, and if it’s not found then it will check the Inputs. No additional calls or Exception handling is required. Then, if you were to nest another call to the DAO Framework using the current Results [i.e. R(I)] as Inputs to you would end up with a stream represented by R(R(I)). The latest Results wrap the Inputs like in a first call. Since the Inputs were a set of Results, the whole structure is wrapped. So now a single request for a property’s value would check the outer Results first and if not found then it will check the Inputs. Since the Inputs are Results, they would be checked and then finally the original Inputs would be checked as needed. This shows that all the previous values are in the stream and the precedence is from the outside in. This construct can eliminate the need to build discrete Inputs to each DAO call and easily allow you to use the current row in one set of Results as the Inputs to another call. More details and examples of this feature in action will be shown in the Nesting Query Handlers section 7.7 below.

Page 20: DAO Consumer's Guide v1.0.11

Streams MapStream

DAO Framework Page 20 of 67

5 Streams

Since we have defined all the interactions with the DAO Framework as Java Interfaces, we can combine them in many ways to reduce the number of object we need to create. Steam classes make heavy use of this concept, they are typically composite classes that implement multiple interfaces and can play many roles within the DAO Framework. To see all the interfaces used and roles each stream class plays, please refer to the DAO Framework Javadoc. You have already seen a brief example of both a MapStream and Results object in action. This section will go into more details about the streams classes that are available for use. At the root of all stream classes is the NVStream which was presented in the Inputs to the DAO section 2.4 above. What you should recall is the NVStream is an interface that describes what a stream implementer must provide. There are several concrete implementations of stream classes that you will use heavily and the MapStream is one of them. 5.1 MapStream At the root of an NVStream is the Name/Value constructs which is equivalent to a Key/Value pair. Java already has several generic collections classes that are used heavily for this purpose such as HashMap, SortedMap, TreeMap, LinkedHashMap, etc. So it shouldn’t be too surprising that a DAO MapStream is a concrete implementation of an NVStream that uses a Java LinkedHashMap. In Java, Maps are very generic containers that allow for any Object to be the key and / or value. Our implementation places DAO specific restrictions on top of this generic construct. We will not go into the coding of the MapStream class itself, but let’s look at its definition to understand what it can do within the framework.

public class MapStream implements EntryNVStream, Inputs

So a MapStream is an EntryNVStream, meaning it can have “put” calls made to load it with values. It is also an Inputs object. We have seen this fact already. Note, that since a MapStream is an Inputs object, all the methods available for Inputs are available on the MapStream class directly. Please review section 2.2 above for more details on the Inputs interface. Let’s review the available constructors on a MapStream: /** * Creates a new empty MapStream with the selector name specified. * @param selectorName the selector name to run */

public MapStream( String selectorName ) /** * Create a new empty MapStream */

public MapStream() /** * Creates a new map stream using the map provided * @param map the map containing the name/value-pair properties */

public MapStream( Map<String, Object> map ) /** * Creates a new map stream using the map provided. Also sets the target * to the object specified. * @param map the map containing the name/value-pair properties * @param target the target object */

public MapStream( Map<String, Object> map, Object target ) /** * Creates a new map stream using the map provided. Also sets the selector name as specified. * @param selectorName the selector name to run * @param map the map containing the name/value-pair properties */

public MapStream( String selectorName, Map<String, Object> map )

Page 21: DAO Consumer's Guide v1.0.11

Streams JavaBeanStream

DAO Framework Page 21 of 67

/** * Creates a new map stream using the map provided. Also sets the selector name as specified. * Also sets the target to the object specified. * @param selectorName the selector name to run * @param map the map containing the name/value-pair properties * @param target the target object */

public MapStream( String selectorName, Map<String, Object> map, Object target ) /** * Creates a new map stream using the map provided. It sets the selector name as specified. * It sets the target to the object specified. Also sets the input ColumnMap to the

* object specified. * @param selectorName the selector name to run * @param map the map containing the name/value-pair properties * @param target the target object */

public MapStream( String selectorName, Map<String, Object> map, Object target, ColumnMap columnMap )

The first constructor is the most commonly used. It creates an empty MapStream and sets the Inputs’ selector name to the value provided. This helps you to quickly create a new stream from scratch. The second constructor creates a new Empty MapStream and the order that the properties are added to the stream is maintained. No selector name is set so it must be defined later on. The third version takes a Map as an input. This allows the consumer to construct a new Stream from an already existing java.util.Map instance. This method isn’t used that often. All the rules about the name and values explained above still apply. The fourth version takes both a Map and a target Object. This constructor works like the third, but also set the target object in the Inputs. The remaining versions are variations of the above parameters. In conclusion, as we discussed in the Inputs to the DAO section 2.6 above, MapStreams are an EntryNVStream implementation using a Java Map class. They allow the two defined put() operations. MapStreams also contain a rich set of setter/getter methods for retrieving properties. A MapStream is also an Inputs object. Please review the DAO JavaDoc for more detailed information about the MapStream class. 5.2 JavaBeanStream Now that you are beginning to understand how streams work, there is another concrete implementation of a stream that can also be used. Again, a little background is called for. When building standard Java applications a Data Transfer Object (DTO) pattern is often used. This standard pattern creates a custom Java object with properties that represent some domain based construct like a Customer, SKU, or Vendor among many other possible types. These custom classes contain member variables for all the properties that the object represents. These objects, also known as Java Beans, are constructed with setter / getter methods to expose their properties. If your application already has one or more of these classes that contains the values your program is using, it doesn’t always make sense to have to copy the properties from that object over to a MapStream or other container just to call the DAO API. This is where the JavaBeanStream comes into play. It allows your DTO or Java Bean class to become the stream. Basically, the DAO framework will look for a getter method that matches the property name specified. This way no extra objects are created and your existing class plays the role of the stream. Let’s take a look at the definition and a then an example:

public class JavaBeanStream implements NVStream, Inputs

So a JavaBeanStream is an NVStream, meaning it does not have “put” calls, just “read” calls. You should use any existing setter methods or constructors you have built to create and populate your Java Bean objects directly. It is also an Inputs object. Note that since it is an Inputs object, all the methods available for Inputs are available on the JavaBeanStream class. Let’s review the available constructors on a JavaBeanStream:

Page 22: DAO Consumer's Guide v1.0.11

Streams JavaBeanStream

DAO Framework Page 22 of 67

public JavaBeanStream( String selectorName, Object javaBean ) public JavaBeanStream( String selectorName, Object javaBean, Object target ) public JavaBeanStream( String selectorName, Object javaBean, Object target, ColumnMap columnMap )

The first constructor is the most commonly used version. It creates a new JavaBeanStream and sets the Inputs’ selector name to the value supplied. It also sets the javaBean instance to use to pull properties from. The second version takes the selector and a target Object. This constructor works like the first, but also set the target object in the Inputs. The Third constructor builds on the second and lets the user set the ColumnMap. So let’s look at an example that uses a JavaBeanStream. First let’s create a simple Java Bean class to use:

public class SimpleCustomer { private int zipCode; private String customerName; public int getZipCode() { return zipCode; } public void setZipCode( int zipCode ) { this.zipCode = zipCode; } public String getCustomerName() { return customerName; } public void setCustomerName( String customerName ) { this.customerName = customerName; } }

As you can see, this is a very simple DTO style Java Bean object that contains two properties exposed with setter and getter methods. In the Inputs to the DAO section 2.6 above, we created a MapStream. Let’s show that code again for reference:

MapStream inputs = new MapStream( "findCustomers" ); inputs.addQualifier( "sort", "ascending" ); inputs.put( "customerName", "Smith"); inputs.put( "zipcode", 90210 );

So now let’s modify that version to use our new stream type to do the same thing. Note that this example is using an existing Business Logic (BL) Java Bean to give the same properties values to the DAO Framework, see below:

// Some Business Logic class already created an object, shown here for completeness SimpleCustomer simpleCustomer = new SimpleCustomer(); simpleCustomer.setCustomerName( "Smith" ); simpleCustomer.setZipCode( 90210 ); // . . . the call to the DAO Framework uses the existing BL object JavaBeanStream inputs = new JavaBeanStream( "findCustomers", simpleCustomer ); inputs.addQualifier( "sort", "ascending" );

Page 23: DAO Consumer's Guide v1.0.11

Streams DelegateNVStream

DAO Framework Page 23 of 67

As you can see in the first 3 lines, business logic code has previously created a simple Java Bean used inside an application. To call the DAO Framework, this object can be used to provide the properties. Notice we did not have to put (or copy) any values into the MapStream like before. The specified Java Bean already has two getter methods that give public access to the properties. The JavaBeanStream will find and call getCustomerName() and getZipCode() methods to retrieve the values when the stream asks to read the values. The details below show the standard mappings from Java Bean property names to methods, this may help you understand how this type of plumbing works:

Property Name Method Name "customerName" getCustomerName(); "zipCode" getZipCode();

Note that we still set the qualifier “sort” just like in the previous example. Finally, if your Java Bean’s getter method names do not exactly match the DAO specified property names to make the call, you can use an input ColumnMap to translate the property names. Please review the ColumnMaps section 2.8 above for more details. This should give you a lot of flexibility to re-use any existing Java Bean classes. 5.3 DelegateNVStream Another way to form a stream of property values may involve using a combination of other streams to make up one composite stream for a single call to the DAO Framework. This is supported through the DelegateNVStream implementation. A DelegateNVStream class is used to elect a primary (or main) stream and a delegate (or secondary) to use to read properties. The way it works is to read from the primary first, and if the property is not found there, then it will read from the secondary stream. An example of this new type of stream is shown below:

MapStream stream1 = new MapStream( "findCustomers" ); stream1.addQualifier( "sort", "ascending" ); stream1.put( "customerName", "Smith"); // Some Business Logic class already created an object, shown here for completeness SimpleCustomer simpleCustomer = new SimpleCustomer(); simpleCustomer.setCustomerName("Smith" ); simpleCustomer.setZipCode( 90210 ); JavaBeanStream stream2 = new JavaBeanStream( simpleCustomer ); stream2.addQualifier( "sort", "ascending" ); DelegateNVStream inputs = new DelegateNVStream( stream1, stream2 );

What this code does first is create a MapStream with just a “customerName” property. Note that the “zipCode” value is no longer present in this code example but our call the DAO Framework still requires it as an input value. So next we created a JavaBeanStream, it has two property values including the “zipCode” value we need. Finally, the last line of code in this example creates a DelegateNVStream with the MapStream as the main stream and the JavaBeanStream as the delegate stream. Now, when the property values are read from the stream the main stream is always checked first so the “customerName” property will be found there. The “zideCode” is not present in the main stream so its value will come from the delegate stream. See the illustration below for more details:

Page 24: DAO Consumer's Guide v1.0.11

Streams NVStream Custom Implementation

DAO Framework Page 24 of 67

This drawing shows how these two streams were composited into one. Since this new type of stream can check in two places for values, we gain some options for how to call the DAO Framework. What may not be obvious at this point is that either or both of the streams in the DelegateStream shown above could also have been another DelegateNVStream that also looks in two places. Now you would have a composite stream that can look in three places for values. See the illustration below for more details:

The delegate construct is highly useful for building these composite structures. Although this document doesn’t go into the details, the Delegate construct is also available for Inputs, Results, and ColumnMap classes. See the DAO Framework Javadoc for more details. 5.4 NVStream Custom Implementation There are a good number of out of the box implementations that should cover the vast majority of a typical program’s requirements for streams but if you need something else you can always create your own. Since the NVStream is an interface definition, any class that implements its methods can become a stream. Although it should be rare, if you need to create one an Adapter class exists to help with the construction. The NVAdapter class is an implementation of a stream that always throws a PropertyNotFoundException. What you would need to do is override the methods you want to implement within your custom class and you’ll be ready to go. Please see the DAO Framework Javadoc for more information.

Page 25: DAO Consumer's Guide v1.0.11

Exceptions QueryException

DAO Framework Page 25 of 67

6 Exceptions

Exceptions are part of all Java applications. The DAO Framework is very consistent in its use of Exceptions to minimize the number of catch-blocks that a consumer program needs to handle from the DAO. Below we will review the exception classes available and their specific uses. 6.1 QueryException The QueryException is the root of all DAO Framework exceptions. All the other exception will always extend this class. This will allow the consumer’s application to only have to catch a single type. The QueryException is capable of wrapping other non-DAO exceptions. This is a typical Java pattern that the DAO Framework also uses. This is useful way to get to the (possible) underlying exception that caused the Framework to see a failure. Use the getCause() method to retrieve any wrapped exception as appropriate. There are five other types of specific DAO exceptions that can be generated from the framework. The DAO Framework consumer application can catch these specific types of exceptions if they can be handled by the caller. Please see the sections below for more details on these exception types. 6.2 PropertyNotFoundException A PropertyNotFoundException is specific to NVStream classes. It is thrown when a property value is attempted to be read from a Stream but it does not exist. There may be cases where this type of exception would cause the consumer to alter their call to the DAO Framework without failing. The PropertyNotFoundException has a method called getPropertyName() that will return the property “string” name that could not be found in the Stream. 6.3 InsertException The InsertException is a specialty type of QueryException that is used to indicate that an insert operation failed due to a duplicate row condition on the underlying data store. There are cases where the business logic can try an update operation instead given their particular business rules. 6.4 UpdateException The UpdateException is a specialty type of QueryException that is used to indicate that an update operation failed due to a no rows found condition on the underlying data store. There are cases where the business logic can try an insert operation instead given their particular business rules. 6.5 SecurityException This specialty type of QueryException is used to indicate that the caller does not have permissions required to execute the operation being requested. There may be cases where the business logic can handle this type of exception based upon their particular business rules. The SecurityException has three specialty methods called getContractName(), getSelectorName(), and getBusinessUseID() that will return the property values that caused the exception to occur. 6.6 ValidationException This specialty type of QueryException is used to when a property validation rule is violated. Typically this type of exception would cause the consumer to display a UI message to have their user correct the input. The ValidationException has a method called getPropertyName() that will return the property “string” name that caused the violation. The getMessage() method contains the details about of the violation that can be used for display.

Page 26: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow ValidationException

DAO Framework Page 26 of 67

7 DAO Framework Detailed Flow

Back at the beginning of this document we showed you the simplified view of the DAO Framework and concentrated on the Inputs and Results components. The drawing showed the details of the DAO Framework as a big green box with more details to come. So now it is time to look inside of the box to see the other DAO components and how you can build more complex interactions. Up to this point we have made use of the BasicDAO class to run our requests. As we described in that section, the BasicDAO’s purpose is as a helper to run a single request and construct all the needed stages to make a call to the DAO. This class is very useful, but for more complex interactions like a series of operations the entire DAO flow needs to be shown. The interactions steps are broken down into stages shown by the blue boxes with text below to briefly describe what occurs at each stage. Please see the diagram below and the details inside of the green box for the full DAO Framework flow:

You should be familiar by now with the Contract, Inputs and Results stages at the beginning and the end of the flow as they have been discussed in detail in the preceding sections of this document. As we have progressed through our journey so far, we addressed these three stages by examining their Java interfaces, and reviewed some example implementations. In the figure above, all the blue boxes represent Java interfaces for each stage’s functionality. As you have learned, interfaces allow us to create multiple ways to implement these items as we did with the streams. For the new stages above, we will address them in a similar fashion by describing the purpose for the stage and then showing some concrete implementations that are available. Let’s start by looking at the QueryManager stage.

Page 27: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow QueryManager

DAO Framework Page 27 of 67

7.1 QueryManager The QueryManager, like its name, is used to manage query interactions requested from the business logic consumers. Contracts and Selectors are the inputs needed to start the request. The QueryManager is the first class used to initiate an interaction with the DAO Framework. The QueryManager’s role covers several areas as the orchestrator of the interaction. The QueryManager does the following major functions:

1. Loads the query implementations for contracts 2. Caches results 3. Manages security 4. Manages prioritization 5. Logs details of the query interactions 6. Provides detailed monitoring 7. Manages debugging

The first step is to get hold of the QueryManager. An instance of the QueryManager is obtained by passing in a Contract object. A QueryManager instance is returned. The code would look something like below:

Contract contract = new BasicContract( contractName, businessUseID ); QueryManager queryManager = QueryManager.getInstance( contract );

What this section of code is doing is creating a Contract instance. Back in section 2.1 we described the details of a Contract. The BasicContract class shown here is a concrete implementation of the Contract interface. The second line of code gets us a QueryManager instance to use. The QueryManager can be re-used many times to initiate interactions. It is not required for an application to hold a static member variable to the QueryManager since obtaining one on demand has extremely low overhead. 7.2 DAOConnection Now that we have a QueryManager instance to talk with, the next stage in the flow is to request a connection for the interaction. The connection construct in the DAO Framework is called a DAOConnection. It is defined as the connection point for a consumer to interact with the DAO Framework. It can be thought of as a logical connection to the DAO Framework not unlike a connection to a database that we might use for traditional JDBC. It contains Controls to handle a business logic initiated rollback or committing of work. Note: rollback and commits will be shown in details shortly. So how do we get a DAOConnection? Great question – see below:

DAOConnection daoConn = queryManager.getDAOConnection( contract );

This line of code is relatively simple. We take the query manager and request a connection given a Contract object. That’s it. A DAOConnection is designed to be obtained each time a request or grouping of work is needed, not unlike what you would do for a JDBC connection object. OK, so now we have gotten further down the stages and our next step is to get the QueryHandler, let’s take a look at that in the next section. 7.3 QueryHandler A QueryHandler is used to gain access to one of the five access methods available in the DAO Framework. A QueryHandler is used as a callback mechanism for an individual query framework interaction. A query is a single Contract-Selector operation that is executing from one of the five types supported. If you look back at the BasicDAO section 3 above, you’ll see the five types of interaction with the data – two ways to read and also the insert, update, and delete operations. A QueryHandler models these five types of interactions. Let’s take a look at the definition of a QueryHandler shown below:

Page 28: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow QueryHandler

DAO Framework Page 28 of 67

public interface QueryHandler { /** * Handles row-by-row reading */ public void handleResults( Results results, Query query, Inputs inputs ) throws QueryException; /** * Handles by-Object reading */ public void handleObject( Object target, Query query, Inputs inputs ) throws QueryException; /** * Notifies about an update being completed */ public void updateComplete( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException; /** * Notifies about an insert being completed */ public void insertComplete( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException; /** * Notifies about a delete being completed */ public void deleteComplete( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException; /** * @return the elapsed time in milliseconds */ public long getElapsedTime() throws QueryException; }

As you can see there are six methods, two for reading and 3 for modifying data, and one for getting elapsed time. The first method called handleResults() is used for reading row-by-row. A Results object is returned along with the Query being executed and the Inputs that were used for the request. These values should be familiar since they are the same we used with a ResultsReader shown in the By Row section 3.1 example above. Next we have the other reading method called handleObject(). A target Object along with the Query being executed and the Inputs that were used for the request are returned. Hmmm. This again should ring familiar to you. Remember back to the ObjectReader shown in the By Object section 3.2 above. The same values are passed back. Well, by this point I hope you’re seeing a pattern emerging. The insert, update and delete methods return the same values as their respective Notifiers received in the Insert, Update, and Delete section 3.x above. So no further explanation should be needed. So, on to the final method called getElapsedTime(). This method will return the time in milliseconds the operation(s) took to complete. In review, the QueryHandler is a higher level callback handler that is used to receive multiple types of responses from a query caller. This type of handler is more useful when you are doing a series of operations rather than just one. Now let’s show how to create one, see below:

QueryHandler queryHandler = new MyQueryHandler(); There is not much to it. No other methods or staged items have to be involved. The creation of the MyQueryHandler class is done by someone who would write the code and use the methods required to have their data flow back from DAO calls, it is not a DAO class. You can customize the handlers to do what you like including possibly changing their constructors to take some of your business logic components or manage where you would like the data to be stored. Since we have the QueryHandler covered at this point, we can move onto the ConnectionHandler stage shown below.

Page 29: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 29 of 67

7.4 The UniversalConnectionHandler Connection handlers are the way a DAO consumer controls an interaction grouping with the DAO Framework. The concept for a connection handler is to be able to control a series of individual DAO selector operations which comprise a logical grouping of work. The recommended connection handler class to use is the UniveralConnectionHandler. This connection handler allows you to choose if you want to use a database level unit of work where you can do a series of calls and if they all work then you commit them all together and if anything goes wrong you roll them all back. If enabled, it uses a Transaction to talk to the database. If any exceptions are thrown the whole unit of work is rolled back otherwise it is committed. The calls to rollback and commit are done for you; there is no code to write outside of specifying the handler. Hopefully you are thinking – cool, writing all that code by hand is a big ole pain. That is the idea for using a connection handler. The common code is written once and you choose the one you want and all the low level work is done for you. Before diving head deep into a ConnectionHandler code example, let’s pause and look at a quick design pattern that better explains the roles of a ConnectionHandler and a QueryHandler. See below:

From this drawing you can see that the two classes play distinct roles. The ConnectionHandler is used to initiate the calls to the DAO layer, manage the mode of the call, and to provide access to the data being persisted. The QueryHandler is managing the responses back from the DAO. There are cases where the response from one call would be used to call the next. When the calls are sequential, it would be better to handle them all from the ConnectionHandler. The QueryHandler would set a value to be picked up by the ConnectionHandler for the next call. When the calls are nested, it is easier to handle the second call directly from the QueryHandler itself. So now on to a code example. Using a ConnectionHandler is simple. Choose the one you need first and instantiate it. Let’s look at an example:

ConnectionHandler connHandler = new UniversalConnectionHandler( false, queryHandler );

The UniversalConnectionHandler is a class is designed for both transactional and non-transaction interactions with the database. This false parameter above is selecting a non-transactional state which makes sense for most read operations. It also allows you to send in a query handler to call on the constructor. This is common with the connection handlers, they will manage the connection constructs and pass off the work to the defined query

Page 30: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 30 of 67

handler to read and/or update the data. Let’s take a look at the ConnectionHandler Java interface to see what ConnectionHandler’s must do:

public interface ConnectionHandler { /** * The work to be done with the connection */ public void execute() throws QueryException; /** * The user defined work to be done when the query is executed */ public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException; /** * The QueryHandler for this ConnectionHandler */ public QueryHandler getQueryHandler( Query query ) throws QueryException; /** * @return the elapsed time this handler has taken in milliseconds */ public long getElapsedTime() throws QueryException; }

There are four required methods. Most are handled by the implementation class chosen. The one that is usually left up to you is the handleQuery() method. This is where you would choose the low level operation(s) to call. An example would be something like below:

public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException

{ DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery();

query.getResult( daoConn, queryHandler, inputs ); }

This example uses the daoConnList to get the query implementation for a contract. It then calls the getResult() method to read the customer header. This code segment just defines the action(s) to take, but how do we tell it to run. That is what the execute() method is for on a ConnectionHandler. Calling execute() starts the whole event chain and runs the handler. So now, lets put everything we have just learned together to see a complete example. Please see the code segment below:

public static void getResult( String contractName, int businessUseID, int versionID, final Inputs inputs, ResultsReader reader ) throws QueryException { final Contract contract = new BasicContract( contractName, businessUseID, versionID ); QueryManager queryManager = QueryManager.getInstance( contract ); final DAOConnection daoConn = queryManager.getDAOConnection( contract ); QueryHandler queryHandler = new BasicResultsReader( reader ); ConnectionHandler connHandler = new UniversalConnectionHandler( false, queryHandler, daoConn ) { public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery(); query.getResult( daoConn, queryHandler, inputs ); } }; connHandler.execute(); }

Page 31: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 31 of 67

Most of this code you have just seen and now it is wrapped into a reusable method. Let’s review the new parts to see how this is composed:

public static void getResult( String contractName, int businessUseID, int versionID, final Inputs inputs, ResultsReader reader ) throws QueryException

This part should not be all too new. This is a method signature passing in the items we need to call a DAO query. It takes a contract name, business use ID, version ID, and the Inputs. Our new method also uses a ResultsReader to read row-by-row. The ResultsReader is passed in to allow any instance we decide to be able to read the data. You will see the getResult() method is being called inside of an anonymous inner class. This is an easy way to put all the code you are writing together by implementing the handleQuery() method. Remember a connection handler defines the interactions but it does not start the interaction. The inner class defines what to do but does not run it. To start it all we need to call execute() method on the connection handler. This is what the last line of code does. Something else is new – a BasicResultsReader. This class is used to make a call out to a ResultsReader instance when the data is ready to be read row-by-row. It is a specific type of QueryHandler that is designed to just do row-by-row reading. Since our method sent in a ResultsReader to be called, this class completes the plumbing needed to call the handler. Well, we have created an interaction with the DAO framework using all the discrete DAO stages and made a query call. Well, if you haven’t guessed yet, you have just seen the actual implementation of the getResults() method used inside of the BasicDAO class explained in the By Row section 3.1 above. Kind of sly aren’t we? But our example still only does a single operation. Let’s make it do more and see another way to create an interaction, please see below:

Page 32: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 32 of 67

/** * Loads the query details for the selected customer. * @param daoConn the DAO Connection * @param contract the contract to load * @param the stream of properties needed to read customer details. * @throws QueryException */ protected void readCustomerDetails( final DAOConnection daoConn, final Contract contract,

final MapStream ms ) throws QueryException { if ( daoConn == null ) { throw new QueryException( "DAOConnection cannot be null" ); } if ( contract == null ) { throw new QueryException( "Contract cannot be null" ); } QueryHandler queryHandler = new CustomerDetailsReader(); ConnectionHandler connHandler = new UniversalConnectionHandler( true, queryHandler, daoConn ) { @Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery(); ms.setSelectorName( "readCustomerHeader" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "readCustomerAddresses" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "readCustomerPhones" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "updateCustomerAccess" ); query.updateObject( daoConn, queryHandler, ms ); } }; connHandler.execute(); }

So let’s walk through this example and highlight the changes and additions compared to the earlier one. First let’s look at the method call definition:

protected void readCustomerDetails( final DAOConnection daoConn, final Contract contract, final MapStream ms ) throws QueryException { if ( daoConn == null ) { throw new QueryException( "DAOConnection cannot be null" ); } if ( contract == null ) { throw new QueryException( "Contract cannot be null" ); }

In our prior example we used all the DAO stages and created both a new Contract and a new DAOConnection that we would need. With this method signature you’ll note that these values are passed in. We do need to add proper parameter checking now, so the two null check if-blocks make sure the passed in values are not null. It is more common to pass these values into a method when handling a multiple interaction scenario. A higher level method would obtain or create the preliminary stages that are needed. This part has a small change. You will also note that the previous example passed in a ResultsReader. In this version we are just receiving the input stream to use to call the query. You may also note that we threw a QueryException ourselves in the if-blocks. This is allowed and will keep you from having to inject some other type of exception into your DAO classes.

Page 33: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 33 of 67

Let’s look at the QueryHandler line:

QueryHandler queryHandler = new CustomerDetailsReader();

This line looks close to the one before but there is no BasicResultsReader instance. We are creating this handler using a new custom class called CustomerDetailsReader to read the data. This is not a DAO Framework class. It is part of the example and will be shown here shortly. Next, the creation of the ConnectionHandler is basically identical except for what the body of the handleQuery() method is doing. Let’s take a look at that now:

public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery(); ms.setSelectorName( "readCustomerHeader" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "readCustomerAddresses" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "readCustomerPhones" ); query.getResult( daoConn, queryHandler, ms ); ms.setSelectorName( "updateCustomerAccess" ); query.updateObject( daoConn, queryHandler, ms ); }

There are four repeating sections of two lines of code. The first line of each repeating section is setting the selector name for the interaction. You can see that between the four sets of lines just the selector name is changing. For the first three sections, the second line of each is identical. They are calling the query to have the data read row-by-row. So here we have three sequential calls to the DAO reading three different sets of results – a header, addresses and phone records respectively. The fourth section is a bit different as it is calling to update the customer’s access. Note the name of the method to the “query” instance has changed to updateObject() from getResult(). When you build your own ConnectionHandler handleQuery() sequences, you can mix the five methods available in any order and in an quantity based on your design. Create a new UniversalConnectionHandler for each grouping interaction. Do not try to make one ConnectionHandler do all the work for your entire application. ConnectionHandler’s are easy to create. And soon you will see, in the following section, that they automatically composite themselves together to allowing them work in concert with each other. Now let’s look at the last line:

connHandler.execute();

This last line of the code example, the execute() call, is the same as before and is telling everything to run. How are all the different data sets being read and how is the notification about the update coming back to the consumer? Good question, we’ll need to take a look at the QueryHandler we said to use to find out that part. We told it to use a CustomerDetailsReader, let’s look at that code now:

public static class CustomerDetailsReader extends AnnotatedQueryHandler { @QuerySelector( name="readCustomerHeader", operation=QueryMethod.getResult ) public void processCustomerHeader( Results results, Query query, Inputs inputs)

throws QueryException { while( results.next() ) { // Reader customer header here } }

Page 34: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 34 of 67

@QuerySelector( name="readCustomerAddresses", operation=QueryMethod.getResult ) public void processCustomerAddresses( Results results, Query query, Inputs inputs)

throws QueryException { while( results.next() ) { // Reader customer addresses here } } @QuerySelector( name="readCustomerPhones", operation=QueryMethod.getResult ) public void processCustomerPhones( Results results, Query query, Inputs inputs) throws QueryException { while( results.next() ) { // Reader customer phones here } } @QuerySelector( name="updateCustomerAccess", operation=QueryMethod.updateObject ) public void accessAcknowledged( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Access update is complete } }

In this example, we are using an inner class. This code would appear inside the same Java class file that contains the readCustomerDetails() method shown above. Using inner classes keeps the code together and should make it easier to glue all the details together without having to look across source files. Now let’s take a look at our inner class definition:

public static class CustomerDetailsReader extends AnnotatedQueryHandler

It is an inner class definition like we mentioned we were going to use. The static identifier is there since we don’t need the containing parent’s details. Whether your inner classes are static or not is unimportant. Use the style that best fits your application, both static and parent-referencing inner classes are just fine. But something else new is used, a type of QueryHandler called AnnotatedQueryHandler. Some background first. The DAO Framework is built on top of Java 5.0 JDK. In Java 5, annotations were added to the language and allow you to add metadata information into your classes. If you are not familiar with Java 5 annotations, please read about them since we will not cover them in any detail here. The DAO Framework allows you to use custom DAO annotations to control the flow of calls within your classes based on selector names. In the previous example we used a QueryHandler called BasicResultsReader. This is a single purpose query handler that only handled reading of row-by-row results from one of the five possible types. In this example, we have switched to use a full octane QueryHandler that will allow us to use all five types in conjunction with each other. The specific implementation we are using is called AnnotatedQueryHandler. Since we have a series of calls, we could have created a number of individual custom Reader(s) and/or Notifier(s) classes and had a large if-ladder to control which one to call. That type of code can be problematic and difficult to read. Annotations provide a cleaner way to do this flow control. You may be wondering, where is that one line of code we are supposed to implement for a ResultsReader like we did before? Here’s the line:

public void readResults( Results results, Query query, Inputs inputs ) throws QueryException

Page 35: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow The UniversalConnectionHandler

DAO Framework Page 35 of 67

The AnnotatedQueryHandler class implements this method along with all the other QueryHandler methods. Since this type of handler does all five operation types, it will have the readResults() method along with the Object and Notifier method signatures. It will discover the annotations you added to the individual methods and use them to direct the control flow. The method names used are left up to you. As long as they are unique for each selector name, the class will find it and call it. The name of the method is free for you to create but the signature of the method must match the patterns used by typical QueryHandlers. Please look back at the QueryHandler section 7.3 above. The number and order of the parameters must be maintained. Looking back to our example, let’s examine the details for one of the methods we created, see below:

@QuerySelector( name="readCustomerHeader", operation=QueryMethod.getResult ) public void processCustomerHeader( Results results, Query query, Inputs inputs) throws QueryException {

The first line “@QuerySelector()” is the annotation for the method processCustomerHeader(). A QuerySelector annotation always contains two values. The first value is the “name” field. The name is set to the selector name you wish the method to handle. The second parameter is for the “operation” you want to use. Our operation is set to QueryMethod.getResult since we are reading row-by-row. The QueryMethod.getResult is a reference to the DAO Framework Java Enumeration class that defines the five types of query operations available for use. You must have exactly one method defined to handle each type you are using. You cannot have two methods with the same selector name and operation value. If you do, the behavior will be non-predictive and it will be based on the underlying vendor JVM implementation. This should be avoided. If we look at the method we defined in this case, processCustomerHeader(), we’ll see there are three arguments to the method. The parameters match the number and order required by a handleResults() call on a QueryHandler. In this case, when a “readCustomerHeader” selector needs to be read, the processCustomerHeader() method will be called. The body of the method would then process the Results just like any single copy of a handleResults() method would. We have left the results processing details as a //comment. Your application specifics would be added into the method at this point. The update call is next, let’s take a look:

@QuerySelector( name="updateCustomerAccess", operation=QueryMethod.updateObject ) public void accessAcknowledged( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException {

This time the “@QuerySelector()” has a different operation type. It has called out an updateObject enumeration reference. You will also note that the method signature is different and matches what is required for and update call. Hopefully you are beginning to see the pattern is pretty straight forward to create the annotation, match the method signature, and code the details you want to execute. Looking back at the CustomerDetailsReader implementation, you will see four custom methods with annotations defined before each one. The first difference in the annotation is the value of the “name” field. The name matches the four different selector names we referenced in the ConnectionHandler method. The second difference is the operation type. It is set to match one of the five types supported. When the query executes, the annotated method(s) will get called selector by selector as it runs. The methods in the example were listed in the exact order we used to make the calls. This was for example purposes only and is not required.

Page 36: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Connection Handlers

DAO Framework Page 36 of 67

7.5 Nesting Connection Handlers There are times when you may want to composite multiple ConnectionHandler(s) together for reuse without having to change or copy your code. UniversalConnectionHandlers do this behavior by default. If while executing one UniversalConnectionHandler you call execute() on another UniversalConenctionHandler, the interactions with the underlying database will share the connections to the database and any unit-of-work currently running. They will auto-composite into a single transaction based upon the settings provided. If a second handler is also set as transactional and an outer handler already started a transaction, the second one will participate in the same transaction scope without any code changes needed. Also, if an exception is thrown triggering a rollback(), all the nested queries will be rolled back together. If an second inner handler is the only transactional one, it will only affect itseld and any other handlers executed from within it. The outer handlers will be non-transactional and the inner ones will inherit the transaction scope as noted before. Likewise if you execute a non-transactional UniversalConnectionHandler from an outer UniversalConnectionHandler that already began a transaction, the code specified in the non-transactional handler will also participate in the transaction without any recoding of your classes. 7.6 Nesting Query Handlers We have spent a good amount of time talking about the UniversalConnectionHandlers. But what if we want to read from one set of Results and then use them and run another interaction with the DAO to get more information? This is when you would create nested QueryHandlers. So let’s change our focus to how to do just that. Let’s build a customer example that runs a DAO call to read back a list of customers. For each customer in that Results list, we want to read all the details about each customer through another nested interaction with the DAO. We do not want to store the customer list first and then make multiple calls to get each individual customer; we want to do it all at once.

Page 37: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 37 of 67

This example is a bit large to show within this document. Because of that, we will show it piece by piece. All of the pieces presented would be in the same Java class file. On to the first method, see below:

/** * Loads the customer details for all customers that match the input customer search criteria. * @param daoConn the DAO Connection to use * @param customerSearchCriteria the inputs that has the search criteria to find a set * of customers and load their detail records. */ protected List<Map<String, Object>> readCustomers( final DAOConnection daoConn,

final MapStream customerSearchCriteria ) throws QueryException

{ if ( daoConn == null ) { throw new QueryException( "DAOConnection cannot be null" ); } CustomerListReader customerListReader = new CustomerListReader( daoConn ); ConnectionHandler connHandler =

new UniversalConnectionHandler( false, customerListReader, daoConn ) { public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList, QueryHandler queryHandler ) throws QueryException { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery(); customerSearchCriteria.setSelectorName( "readCustomers" ); query.getResult( daoConn, queryHandler, customerSearchCriteria ); } }; connHandler.execute(); return customerListReader.getCustomers(); }

This code is similar to what we saw back in the ConnectionHandler section 7.4 above. Let’s look at some of the differences first:

protected List<Map<String, Object>> readCustomers( final DAOConnection daoConn, final MapStream customerSearchCriteria ) throws QueryException {

The method signature is similar but in this example we are returning a List to the caller. The type of return value is up to you when you write your application. We are using generic Java collections to help keep the example smaller. In our example the returned List is composed of series of entries, each of which is a Map of property names and values. Next, the method creates a custom QueryHandler and dispatches a single read row-by-row query request. This part is pretty simple. Then, the method calls execute() to run all the details. We do, however, have something new at the end of the method:

return customerListReader.getCustomers();

Since we are returning a value to the caller, we make a call to a custom method on our QueryHandler that we access through a variable called customerListReader. Given that, it is time to see that piece and understand the details, please see the next section of code:

Page 38: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 38 of 67

/** * Reads a list of customers and then retrieves their details. */ public static class CustomerListReader extends AnnotatedQueryHandler { // The connection we are work with protected DAOConnection daoConn; // Our Output (Answer) - a list of customers with their details protected List<Map<String, Object>> customers; public CustomerListReader( DAOConnection daoConn ) { this.daoConn = daoConn; } /** * @return the list of customers and their associated properties */ public List<Map<String, Object>> getCustomers() { return customers; } /** * Reset each usage so we can be used multiple times */ public void reset() throws QueryException { customers = new ArrayList<Map<String, Object>>(); } @QuerySelector( name="readCustomers", operation=QueryMethod.getResult ) public void processCustomerList( Results results, Query query, Inputs inputs )

throws QueryException { // Nested calls to this reader may occur, so return a new List for each call reset(); // Our nested Reader to retrieve the customer details CustomerDetailsReader detailsReader = new CustomerDetailsReader(); // Create an Inputs Object that will let use change the SelectorName MapStream ms = new MapStream(); // Composite this with our Results DelegateInputs newStream = new DelegateInputs( ms, results ); while( results.next() ) { ms.setSelectorName( "readCustomerHeader" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "readCustomerAddresses" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "readCustomerPhones" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "updateCustomerAccess" ); query.updateObject( daoConn, detailsReader, newStream ); customers.add( detailsReader.getCustomerDetails() ); } } }

This code is also somewhat similar to what we saw in the ConnectionHandler section 7.4 above, but there some difference here too. Let’s look some of the differences starting with the class definition and the member variables:

Page 39: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 39 of 67

public static class CustomerListReader extends AnnotatedQueryHandler { // The connection we are work with protected DAOConnection daoConn; // Our Output (Answer) - a list of customers with their details protected List<Map<String, Object>> customers; public CustomerListReader( DAOConnection daoConn ) { this.daoConn = daoConn; }

The inner class definition is similar to what we saw before. We are still using an AnnotaedQueryHandler. In this example, however, we have two member variables. The first one is used to keep a reference to the DAOConnection being used. If you look at the constructor, you will see that the DAOConnection value is passed in and then stored in the variable. The other variable that is defined is used to hold the output from our inner class. This is the value we returned in our method back a page or so ago. Let’s take a look at this custom method now:

/** * @return the list of customers and their associated properties */ public List<Map<String, Object>> getCustomers() { return customers; }

This method is a standard getter method we would write in most any Java application. The real work is how the List is built. We’ll take a detailed look at that in a moment, but first let’s look at another new method:

/** * Reset each usage so we can be used multiple times */ public void reset() throws QueryException { customers = new ArrayList<Map<String, Object>>(); }

This is a custom method that is used to create a new ArrayList instance to hold our output. The method is called reset() because it can be called multiple times. This pattern is used so the single instance of this QueryHandler can be reused as needed. You will see how it is called in this next section:

@QuerySelector( name="readCustomers", operation=QueryMethod.getResult ) public void processCustomerList( Results results, Query query, Inputs inputs ) throws QueryException { // Nested calls to this reader may occur, so return a new List for each call reset();

Since we are using an AnnotatedQueryHandler, you can tell that this method is called by a readCustomers() selector, and the operation is for reading row-by-row. The first thing the method is doing is calling the reset() method. This is reinitializing the output collection for another use. Not too bad so far. Onto the next new part:

// Our nested Reader to retrieve the customer details CustomerDetailsReader detailsReader = new CustomerDetailsReader();

If you read the comment you should have a clue what this variable is used for. It is our nested QueryHandler. So to use nested handlers, all we have to do is create them inside our first handler and delegate the work to them. But we said we wanted to take our Results from our customer List and send them down to it. That part is coming up next:

Page 40: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 40 of 67

// Create an Inputs Object that will let use change the SelectorName MapStream ms = new MapStream(); // Composite this with our Results DelegateInputs newStream = new DelegateInputs( ms, results );

The first line creates a new empty MapStream. Again, if you read the comment you can see that we have created this to allow us to create a group of calls to the DAO by changing the selector name. From our earlier example back in the ConnectionHandler section 7.4 above, we called to setSelectorName() several times within the readCustomerDetails() method to do this. That part is coming up, but first let’s discuss the DelegateInputs line. We haven’t directly shown the DelegateInputs class before. We made a quick reference at the end of the DelegateNVStream section 5.3 above that you could create DelegateInputs but didn’t show them. The good news is that they work in similar fashion to their stream counterparts. They take two Inputs on the constructor. The first one is the main Inputs, and the second is the delegate Inputs. The main is looked to first and the delegate is looked to after. When properties are accessed, they are automatically placed into a DelegateNVStream. That is what we want, so we are in business. We needed a MapStream so we can set the selector name. If you look at the code line you will see that the variable “ms” is the main Inputs and that the variable “results” is the delegate. By this simple construct, the results will be looked to if the properties are not found on the main stream, our MapStream. Since we have placed no properties on the stream our results will be used. Mission accomplished. Let’s take a look at the results processing part now:

while( results.next() ) { ms.setSelectorName( "readCustomerHeader" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "readCustomerAddresses" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "readCustomerPhones" ); query.getResult( daoConn, detailsReader, newStream ); ms.setSelectorName( "updateCustomerAccess" ); query.updateObject( daoConn, detailsReader, newStream ); customers.add( detailsReader.getCustomerDetails() ); }

The inner class we are looking at currently is a QueryHandler. It is reading all the customers that came back from the search call we made to the DAO. It is processing all the returned customers by retrieving their details. Now we have reached that part of the example to review. You will see that the while loop is processing all the records found on the search call. If no records were found, we have already called the reset() method so we would return an empty list. But, when we do have rows to read, each row will make a series of calls back to the DAO to read the customer details. The sequence of four calls is the same as we had back in section 7.4 above. One part that is new is which queryHandler we are telling the query.getResults() call to use. We told it to call to the detailsReader, this is our nested query handler. Also, the Inputs object we told it to use was the variable “newStream”, this is our DelegateInputs that we already setup to contain our results. When each of these run they will call down to the CustomerDetailsReader() instance. We will show that next, but before we do we also have a new line at the end of the while loop. This line is adding a record to our output List for customer. It is making a call to the nested query handler to get its data. This is not unlike what we did in our connection handler code to get the first query handler’s data. The nesting call is now in place, so now on to the nested query hander code:

Page 41: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 41 of 67

/** * A read for customer details. */ public static class CustomerDetailsReader extends AnnotatedQueryHandler { // Our Output (Answer) - a map of customer details for a given customer protected Map<String, Object> customerDetails; /** * @return the customer details */ public Map<String, Object> getCustomerDetails() { return customerDetails; } /** * Reset each usage so we can be used multiple times */ public void reset() throws QueryException { customerDetails = new HashMap<String, Object>(); } @QuerySelector( name="readCustomerHeader", operation=QueryMethod.getResult ) public void processCustomerHeader( Results results, Query query, Inputs inputs)

throws QueryException { reset(); if ( results.next() ) { customerDetails.put( "customerNumber", results.getInt( "customerNumber" ) ); customerDetails.put( "customerHeader", results.getMap( "customerHeader" ) ); } } @QuerySelector( name="readCustomerAddresses", operation=QueryMethod.getResult ) public void processCustomerAddresses( Results results, Query query, Inputs inputs )

throws QueryException { List<Map<String, Object>> addresses = new ArrayList<Map<String, Object>>(); while( results.next() ) { Map<String, Object> properties = new HashMap<String, Object>(); properties.put( "addressTypeCode", results.getInt("addressTypeCode") ); properties.put( "streetLine1", results.getString("streetLine1") ); properties.put( "streetLine2", results.getString("streetLine2") ); properties.put( "cityName", results.getString("cityName") ); properties.put( "stateName", results.getString("stateName") ); properties.put( "zipCode", results.getInt("zipCode") ); properties.put( "countryCode", results.getString("countryCode") ); addresses.add( properties ); } customerDetails.put( "addresses", addresses ); } @QuerySelector( name="readCustomerPhones", operation=QueryMethod.getResult ) public void processCustomerPhones( Results results, Query query, Inputs inputs )

throws QueryException { List<Map<String, Object>> phones = new ArrayList<Map<String, Object>>(); while( results.next() ) { Map<String, Object> properties = new HashMap<String, Object>(); properties.put( "phoneTypeCode", results.getInt("phoneTypeCode") ); properties.put( "countryCode", results.getInt("countryCode") ); properties.put( "areaCode", results.getString("areaCode") ); properties.put( "phoneNumber", results.getString("phoneNumber") ); phones.add( properties ); } customerDetails.put( "phones", phones ); }

Page 42: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Nesting Query Handlers

DAO Framework Page 42 of 67

@QuerySelector( name="updateCustomerAccess", operation=QueryMethod.updateObject ) public void accessAcknowledged( Object target, boolean success, int count, Query query,

Inputs inputs ) throws QueryException { // We have nothing to do here } }

We can see that this inner class is also an AnnotatedQueryHandler that is used to read customer details. Let’s take a look at the first part:

public static class CustomerDetailsReader extends AnnotatedQueryHandler { // Our Output (Answer) - a map of customer details for a given customer protected Map<String, Object> customerDetails; /** * @return the customer details */ public Map<String, Object> getCustomerDetails() { return customerDetails; } /** * Reset each usage so we can be used multiple times */ public void reset() throws QueryException { customerDetails = new HashMap<String, Object>(); }

Like in the CustomerListReader we were just looking at, there is a member variable. It is holding the details about a single customer. There is a getter method to read the Map of values. There is also a reset() method like we had before allowing the handler to be called multiple times. This is handy since we will be doing exactly that. Let’s look at the next method in the example:

@QuerySelector( name="readCustomerHeader", operation=QueryMethod.getResult ) public void processCustomerHeader( Results results, Query query, Inputs inputs)

throws QueryException { reset(); if ( results.next() ) { customerDetails.put( "customerNumber", results.getInt( "customerNumber" ) ); customerDetails.put( "customerHeader", results.getMap( "customerHeader" ) ); } }

This is an annotated method called for row-by-row reading when the selector name is "readCustomerHeader". Since this is the first method of the four in our example, it is calling reset() to re-initialize the collection container we are going to expose. Since this is a header reader, we have switched the while() loop over to an if() call. There is only one record in a header, so we do not need a while loop. Inside of the if-block are the calls to read the customerNumber and customerHeader values. These are read from the results object that was passed in. The other four annotated method manage their parts respectively to read the customer details. Since the addresses and phone numbers are Lists themselves, they are building a list and adding it to the output properties. You have now seen the entire nested example. You can nest another level deep as easily as we did these two. You are now armed with all the tools to construct a full featured application calling to the DAO in many different ways. Our next section will show you the debugging features available in the DAO to help you create your code. First, we have an extra credit exercise for you. What would the notation look like for the Results object passed into this nested query? Please refer back to the I R(I) R(R(I)) Notation section 4.2 above.

Page 43: DAO Consumer's Guide v1.0.11

DAO Framework Detailed Flow Controls

DAO Framework Page 43 of 67

7.7 Controls Another typical use case that arises occasionally is the need for a consumer’s application to control the isolation level of a DAO call or calls. This can be achieved in two ways. The first method is by using the DAO Metadata setting(s) to default the isolation level at a Contract or individual Selector level. This approach uses a non-API approach to controlling the interactions with the underlying datasource. Once setup, the change of isolation level occurs automatically by the DAO framework each time the interaction begins. If you feel your application needs this type of control, please contact the DAO team directly. The settings at the Metadata level are independent of the individual applications. Any application that uses a DAO library will inherit these Metadata settings automatically. The second method is and via programmatic selection by an individual application through DAO Framework API calls. Let’s take a look at an example below:

DAOConnection daoConn = queryManager.getDAOConnection( contract1 ); Controls controls = daoConn.getControls(); controls.setTransactionIsolation( contract1.getContractName(), contract1.getVersionID(), null,

IsolationLevel.REPEATABLE_READ );

The controls object on the DAOConnection is where this type of change is set. First, the DAOConnection is created as we have seen previously. Next, the Controls instance for the DAOConnection is obtained. Then a call is made to change the isolation level for a given set of parameters on the controls instance. The first three parameters in the call above are the contact name, the contract version, and the selector name. The first two entries are required to identify the contract. The selector name can be specified to limit the isolation level setting to just that selector. If it is null, then any selector(s) accessed in the contract interaction will use the new setting level, which is the last parameter. The isolation level is set for just this specific call. Once the call has completed, the isolation settings are cleared. The available setting levels come from the Controls$IsolationLevel enum class in the DAO Framework. Please see the DAO Javadoc for more details. Other available settings on the Controls class include methods to commit(), rollback(), create/release Savepoints, and rollback(savepoint) all driven by the consumer. Recall that the UniversalConnectionHandler with the transactional state set to true above will automatically call the commit or rollback for you. This should handle the majority if not all the cases for a typical application. But, these additional Control settings are provided for special cases where the Business User needs finer control to the transaction semantics. This is especially true with Java Batch applications that use the DAO with very large data sets. Please see the DAO Javadoc for more details about the calls available.

Page 44: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 44 of 67

8 Best Practice Design Patterns

In the previous chapter, we covered how you can use ConnectionHandlers and QueryHandlers to create more complex interactions with the DAO. But there are some other versatile design patterns which you can use to better organize these more complex interactions with the DAO which makes them highly re-usable within your application. Before looking at these patterns, let’s first explore how a typical application is composed. Most applications have one or more domain based objects that need to be managed. Some example domain objects would be for orders, customers, SKUs, taxes, deliveries, vendors, etc. Each of these high level items usually contain other domain entities like line items, addresses, amounts, sites, statuses, etc. Each of these application domain objects would be modeled as an individual data transfer object or DTO. 8.1 First Design Pattern Our first design pattern creates a custom ConnectionHandler and QueryHandler that manage a specific DTO. Please see the diagram below:

This pattern shows two business logic classes. One that contains the business logic rules and the second that represents the managed domain object as a DTO class. The DTO can be a coarse grained object all the way down to a very fine grained object. In this example, the DTO is for a Customer. The DTO would have all the data fields the make up the customer. Let’s take a look at an example DTO:

Page 45: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 45 of 67

public class CustomerDTO { private Integer customerNumber; private Integer typeCode; private Integer statCode; private String firstName; private String lastName; private String ssn; private CustomerPhoneDTO[] phoneNumbers; private CustomerAddressDTO[] addresses; public CustomerDTO(Integer customerNumber, Integer typeCode, Integer statCode, String firstName, String lastName, CustomerPhoneDTO[] phoneNumbers ) { setCustomerNumber( customerNumber ); setTypeCode( typeCode ); setStatCode( statCode ); setFirstName( firstName ); setLastName( lastName ); setPhoneNumbers( phoneNumbers ); } public Integer getCustomerNumber() { return customerNumber; } public void setCustomerNumber(Integer customerNumber) { this.customerNumber = customerNumber; } public Integer getTypeCode() { return typeCode; } public void setTypeCode(Integer typeCode) { this.typeCode = typeCode; } public Integer getStatCode() { return statCode; } public void setStatCode(Integer statCode) { this.statCode = statCode; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getSSN() { return ssn; }

Page 46: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 46 of 67

public void setSSN(String ssn) { this.ssn = ssn; } public CustomerPhoneDTO[] getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(CustomerPhoneDTO[] phoneNumbers) { this.phoneNumbers = phoneNumbers; } public CustomerAddressDTO[] getAddresses() { return addresses; } public void setAddresses(CustomerAddressDTO[] addr) { addresses = addr; } }

Looking at this DTO, you can see that there are eight properties that make up this customer domain model. Six single values and two array structures. The example code shown here is pretty typical, but for brevity’s sake in the example, items for validation or range checking are not shown. The setter and getter method names represent the business names. Now that we have a DTO, our pattern shows the use of a ConnectionHandler to persist the CustomerDTO via the DAO Framework. Let’s take a look at that code now:

public class SaveCustomerConnectionHandler extends UniversalConnectionHandler { protected static Contract contract = new BasicContract( "eCustomer", 1, 1 ); protected CustomerDTO customer; public SaveCustomerConnHandler( CustomerDTO customer ) throws QueryException {

super( true ); setCustomer( customer ); QueryManager queryManager = QueryManager.getInstance( contract ); DAOConnection daoConn = queryManager.getDAOConnection( contract ); addDAOConnection( daoConn ); setQueryHandler( new SaveCustomerQueryHandler() ); } public void setCustomer(CustomerDTO customer) { this.customer = customer; } public CustomerDTO getCustomer() { return customer; } @Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler ) throws QueryException { CustomerDTO customer = getCustomer(); if ( customer != null ) { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery(); JavaBeanStream inputs = new JavaBeanStream( customer ); //update the header inputs.setSelectorName( "updateHeader" ); query.updateObject( daoConn, queryHandler, inputs );

Page 47: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 47 of 67

//update the phone(s) inputs.setSelectorName( "updatePhone" ); for( CustomerPhoneDTO phone : customer.getPhoneNumbers() ) { if ( phone != null ) { inputs.setJavaBean( phone ); query.updateObject( daoConn, queryHandler, inputs ); } } //update the address(es) inputs.setSelectorName( "updateAddress" ); for( CustomerAddressDTO address : customer.getAddresses() ) { if ( address != null ) { inputs.setJavaBean( address ); query.updateObject( daoConn, queryHandler, inputs ); } } } } protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { @QuerySelector( name="updateHeader", operation=QueryMethod.updateObject ) public void handleHeaders( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Add logic as needed } @QuerySelector( name="updatePhone", operation=QueryMethod.updateObject ) public void handlePhones( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Add logic as needed } @QuerySelector( name="updateAddress", operation=QueryMethod.updateObject ) public void handleAddresses( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Add logic as needed } } }

This connection handler class is created a little differently from the previous examples in this document. Instead of using an inner class, a named class is created that extends the UniversalConnectionHandler. See below:

public class SaveCustomerConnectionHandler extends UniversalConnectionHandler

This class is now a direct extension of a UniversalConnectionHandler and it will be inside of a unit-of-work by calling super(true) inside of the constructor. Now, let’s look at the next few lines in the class:

protected static Contract contract = new BasicContract( "eCustomer", 1, 1 ); protected CustomerDTO customer;

A static member variable is created for the contract being used. If the class was using more than one Contract, each of them would be created as a static member variable. They are static because the contract doesn’t change often and should be virtually constant for most handlers you would write. Next, there is a member variable to hold the DTO object this class is managing, our CustomerDTO. Now let’s look at the constructor:

Page 48: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 48 of 67

public SaveCustomerConnHandler( CustomerDTO customer ) throws QueryException { super( true ); setCustomer( customer ); QueryManager queryManager = QueryManager.getInstance( contract ); DAOConnection daoConn = queryManager.getDAOConnection( contract ); addDAOConnection( daoConn ); setQueryHandler( new SaveCustomerQueryHandler() ); }

In the previous inner class examples, we never added a constructor to the ConnectionHandler class we extended, we just added the handleQuery() method. With this design pattern there is a constructor that takes the DTO class to be managed. In this example, it is sending in a CustomerDTO parameter. The first line of the constructor calls super( true ) setting this class to be transactional. The second line of the constructor, setCustomer( customer ) is a simple call to the setter method in the class that stores the passed in variable to the member variable for later use. The next three lines are setting up the ConnectionHandler relationship to the Contract via a DAOConnection. The first line is obtaining an instance to the QueryManager class. Once we have that instance, the next line gets a DAOConnection reference for the specific contract. Then, the third line adds the DAOConnection to the ConnectionHandler with a call to addDAOConnection( daoConn ). Finally, the last line in the constructor installs a custom QueryHandler that we want to use. A QueryHandler manages DAO responses from each individual DAO call. In our example, a QueryHandler called SaveCustomerQueryHandler() is instantiated and passed by calling to the setQueryHandler() method. Next, let’s look at the handleQuery() method details:

@Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler ) throws QueryException { CustomerDTO customer = getCustomer(); if ( customer != null ) { DAOConnection daoConn = daoConnList.get( contract ); Query query = daoConn.getQuery();

First, the signature of the method follows the standard for ConnectionHandlers and is marked with an @Override annotation. Inside the method, we make sure that the CustomerDTO is valid by calling the getter method and checking for null. If it is present, then we move inside the if-block and the next two lines gain access to the query implementation to run the series of DAO calls. The daoConnList is accessed with the contract instance to retrieve the DAOConnection. The daoConn.getQuery() method returns the query implementation to call to interact with the DAO Framework like in previous chapter examples. Let’s look at the first DAO call to update the customer header information, please see below:

JavaBeanStream inputs = new JavaBeanStream( customer ); //update the header inputs.setSelectorName( "updateHeader" ); query.updateObject( daoConn, queryHandler, inputs );

In this section of the code, we construct a JavaBeanSteam and pass in the DTO object. Recall from section 5.2 JavaBeanStream above that this type of stream reads directly from an object’s getter methods to obtain property values. This makes reading values from the DTO much simpler, especially if the names on the DTO are the business names. Using a JavaBeanStream means we do not have to copy all the values from the DTO object to a new MapStream to run a DAO selector call. This example shows the JavaBeanStream but you could also use the other stream types or column maps to provide access to the properties needed for the DAO call you are making. The last two lines are used to call a specific selector and run an update for the header. Continuing with the example, the DTO we created contains an array of phone numbers. Next we’ll show how to update this type of structure with DAO calls, please see below:

Page 49: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns First Design Pattern

DAO Framework Page 49 of 67

inputs.setSelectorName( "updatePhone" ); for( CustomerPhoneDTO phone : customer.getPhoneNumbers() ) { if ( phone != null ) { inputs.setJavaBean( phone ); query.updateObject( daoConn, queryHandler, inputs ); } }

The first line changes the selector name for updating customer phone records. Next, the code starts a for-loop. The loop is accessing each of the phone records from the array in the CustomerDTO. There is a null check to make sure the phone DTO object exists. If it does, the phone object is installed as the java bean to call the DAO framework. Finally, the call to an update DAO selector is made. This loop will access each record. Next let’s look at the customer addess logic:

inputs.setSelectorName( "updateAddress" ); for( CustomerAddressDTO address : customer.getAddresses() ) { if ( address != null ) { inputs.setJavaBean( address ); query.updateObject( daoConn, queryHandler, inputs ); } }

This code is nearly identical to the phone update logic we just reviewed. The differences are in the object type and selector names. The last part of the example is the custom QueryHandler. Let’s look at that part now:

protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { @QuerySelector( name="updateHeader", operation=QueryMethod.updateObject ) public void handleHeaders( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException { // Add logic as needed } @QuerySelector( name="updatePhone", operation=QueryMethod.updateObject ) public void handlePhones( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Add logic as needed } @QuerySelector( name="updateAddress", operation=QueryMethod.updateObject ) public void handleAddresses( Object target, boolean success, int count, Query query, Inputs inputs ) throws QueryException { // Add logic as needed } }

This code is similar to what you have seen in previous chapters. In this example, we did not add any logic in the methods, but you can add in any logic or methods you need to handle the responses from the DAO Framework. Also, this custom QueryHandler was done as an inner class; that is also not required. It could be a named class and be reused by several other classes. Now let’s look at the business rules class that is orchestrating the work, please see below:

Page 50: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Using Two Contracts

DAO Framework Page 50 of 67

package com.homedepot.ta.aa.example.bl; import com.homedepot.ta.aa.dao.exceptions.QueryException; import com.homedepot.ta.aa.example.services.CustomerDTO; public class SaveCustomer { public void doWork() throws QueryException { CustomerDTO customer = new CustomerDTO(); // add code to set the customerDTO values as appropriate // Create the handler and execute it. SaveCustomerConnHandler saveHandler = new SaveCustomerConnHandler( customer ); saveHandler.execute(); } }

As you can see, this is a simple business logic class that is creating a CustomerDTO domain object. Again, for brevity’s sake, the calls to setup the CustomerDTO object were left out. The CustomerDTO object would be populated based on UI and BL validations and may be done in many ways not germain to this document. Once the DTO object is ready to save, the SaveCustomerConnHandler is instantiated and the CustomerDTO is passed in as the parameter to the constructor. The only step left is to call the execute() method on the ConnectionHandler. The execute() method runs the ConnectionHandler code we just created and the CustomerDTO object will be saved to the database. 8.2 Using Two Contracts With the same design pattern in mind, let’s add some code to handle two contracts from the same custom ConnectionHandler. The CustomerDTO class requires no changes. We’ll use the same example SaveCustomerConnHandler class as before but with some new additions, please see below:

public class SaveCustomerConnHandler extends UniversalConnectionHandler { protected static Contract contract1 = new BasicContract( "eCustomer", 1, 1 ); protected static Contract contract2 = new BasicContract( "EPR", 1, 1 ); protected CustomerDTO customer; public SaveCustomerConnHandler( CustomerDTO customer ) throws QueryException { super( true ); setCustomer( customer ); QueryManager queryManager = QueryManager.getInstance( contract1 ); DAOConnection daoConn1 = queryManager.getDAOConnection( contract1 ); DAOConnection daoConn2 = daoConn1.shareControl( contract2, queryManager ); addDAOConnection( daoConn1 ); addDAOConnection( daoConn2 ); setQueryHandler( new SaveCustomerQueryHandler() ); } /** @return the customer */ public CustomerDTO getCustomer() { return customer; } /** @param customer the customer to set */ public void setCustomer(CustomerDTO customer) { this.customer = customer; } public SaveCustomerQueryHandler getSaveHandler() throws QueryException { return (SaveCustomerQueryHandler)getQueryHandler(); }

Page 51: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Using Two Contracts

DAO Framework Page 51 of 67

@Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler ) throws QueryException { CustomerDTO customer = getCustomer(); if ( customer != null ) { DAOConnection daoConn1 = daoConnList.get( contract1 ); Query query1 = daoConn1.getQuery(); JavaBeanStream inputs = new JavaBeanStream( customer ); //update the header inputs.setSelectorName( "updateHeader" ); query1.updateObject( daoConn1, queryHandler, inputs ); //update the phone(s) inputs.setSelectorName( "updatePhone" ); for( CustomerPhoneDTO phone : getCustomer().getPhoneNumbers() ) { if ( phone != null ) { inputs.setJavaBean( phone ); query1.updateObject( daoConn1, queryHandler, inputs ); } } //update the address(es) inputs.setSelectorName( "updateAddress" ); for( CustomerAddressDTO address : getCustomer().getAddresses() ) { if ( address != null ) { inputs.setJavaBean( address ); query1.updateObject( daoConn1, queryHandler, inputs ); } } // use a Second contract DAOConnection daoConn2 = daoConnList.get( contract2 ); Query query2 = daoConn2.getQuery(); inputs.setJavaBean( customer ); inputs.setSelectorName( "readIsCustomerAnAssociate" ); query2.getResult( daoConn2, queryHandler, inputs ); if ( getSaveHandler().isAssociate() ) { inputs.setSelectorName( "updateCustomerAssociate" ); query2.updateObject( daoConn2, queryHandler, inputs ); } } } protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { protected boolean isAssociate; /** @return the isAssociate */ public boolean isAssociate() { return isAssociate; } /** @param isAssociate the isAssociate to set */ public void setAssociate( boolean isAssociate ) { this.isAssociate = isAssociate; } @QuerySelector( name="updateHeader", operation=QueryMethod.updateObject ) public void handleHeaders( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException {

// Add logic as needed }

Page 52: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Using Two Contracts

DAO Framework Page 52 of 67

@QuerySelector( name="updatePhone", operation=QueryMethod.updateObject ) public void handlePhones( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException {

// Add logic as needed } @QuerySelector( name="updateAddress", operation=QueryMethod.updateObject ) public void handleAddresses( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException {

// Add logic as needed } @QuerySelector( name="readIsCustomerAnAssociate", operation=QueryMethod.getResult ) public void isAssociate( Results results, Query query, Inputs inputs )

throws QueryException { if ( results.next() ) { setAssociate( results.getBoolean( "isAssociate" ) ); } } @QuerySelector(name="updateCustomerAssociate", operation=QueryMethod.updateObject ) public void handleAssociate( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException {

// Add logic as needed } } }

In this new code you’ll notice a few new additions. Let’s start from the top:

protected static Contract contract1 = new BasicContract( "eCustomer", 1, 1 ); protected static Contract contract2 = new BasicContract( "EPR", 1, 1 );

The first change you’ll see is that we defined two contracts. The first contract is for eCustomer and the second is for EPR data. Note that the second contract is also added as a static member variable. The next big change is to the constructor, let’s review that code now:

public SaveCustomerConnHandler( CustomerDTO customer ) throws QueryException { setCustomer( customer ); QueryManager queryManager = QueryManager.getInstance( contract1 ); DAOConnection daoConn1 = queryManager.getDAOConnection( contract1 ); DAOConnection daoConn2 = daoConn1.shareControl( contract2, queryManager ); addDAOConnection( daoConn1 ); addDAOConnection( daoConn2 ); setQueryHandler( new SaveCustomerQueryHandler() ); }

You’ll note some differences from the previous constructor. The signature is the same, and the DTO handling is also the same. The section to retrieve multiple DAOConnection(s) is new. The first two lines to obtain the QueryManger and daoConn1 are identical. The second DAOConnection for the additional contract is new. Note how the call to obtain the second DAOConnection is using the first DAOConnection via a call to the shareControl() method call. What this call does is create two (or more) logical connection points that will share the same Controls structure. The DAOFramework Controls class is the means by which the framework accesses the database. It is the hub for actual JDBC connections to the database(s). By sharing the Controls, the two contracts will participate in the same commit/rollback logic and share a single JDBC connection to the database provided they are mapped to the same datasource definition. The next two lines are adding the DAOConnections with a call to the addDAOConnection() method. The last line for the custom QueryHandler is the same. Next let’s look at the handleQuery() method changes:

Page 53: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Using Two Contracts

DAO Framework Page 53 of 67

@Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler ) throws QueryException { CustomerDTO customer = getCustomer(); if ( customer != null ) { DAOConnection daoConn1 = daoConnList.get( contract1 ); Query query1 = daoConn1.getQuery(); JavaBeanStream inputs = new JavaBeanStream( customer ); inputs.setSelectorName( "updateHeader" ); query1.updateObject( daoConn1, queryHandler, inputs ); //update the phone(s) inputs.setSelectorName( "updatePhone" ); for( CustomerPhoneDTO phone : getCustomer().getPhoneNumbers() ) { if ( phone != null ) { inputs.setJavaBean( phone ); query1.updateObject( daoConn1, queryHandler, inputs ); } } inputs.setSelectorName( "updateAddress" ); for( CustomerAddressDTO address : getCustomer().getAddresses() ) { if ( address != null ) { inputs.setJavaBean( address ); query1.updateObject( daoConn1, queryHandler, inputs ); } } // use a Second contract DAOConnection daoConn2 = daoConnList.get( contract2 ); Query query2 = daoConn2.getQuery(); inputs.setJavaBean( customer ); inputs.setSelectorName( "readIsCustomerAnAssociate" ); query2.getResult( daoConn2, queryHandler, inputs ); if ( getSaveHandler().isAssociate() ) { inputs.setSelectorName( "updateCustomerAssociate" ); query2.updateObject( daoConn2, queryHandler, inputs ); } } }

The new parts are shown in bold. The second contract is used to gain access to the additional DAOConnection in a same way as the first contract. The second query implementation is then obtained and the DAO Framework selector call is made. It is not required that access to the different contract’s DAOConnections occurs sequentially. The ordering is up to you, and you can flip back and forth as needed between the multiple DAOConnection(s). The next new addition shows access to our custom QueryHandler to retrieve a value from the first DAO call. This is used to conditionally drive a second DAO call. Please look at the getSaveHandler().isAssociate() if-block to see how this is being done. The changes to the QueryHandler are pretty straight forward and we’ll leave this exploration to you.

Page 54: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 54 of 67

8.3 Second Desing Pattern Let’s begin with a brief review of what the first design pattern is providing. We have a Business Rules and DTO Object. We built a custom ConnectionHandler to manage the domain object. The separation of the roles and responsbilites is very clear between the classes and this example managed all components to save a CustomerDTO. But what if we need to handle just a customer phone or address update alone? We would need to construct several versions of this current design pattern with mutiple classes and we would need to copy many sections of the persistence code to allow an a-la carte access to the customer DTO components. Our next design pattern shows how we can break up the management of the entire DTO object into components and re-use them as needed. Let show the next iteratation of the design pattern, please see below:

In this new version there have been several changes that mostly involve code organization. The first major change is taking the SaveCustomerConnHandler that managed the full CustomerDTO and splitting it into three different ConnectionHandler classes. These three handlers just manage their respective parts of the CustomerDTO domain object the same way the previous example had done for the entire CustomerDTO. The SaveCustomerConnHandler was changed to be a NestedHandler that reuses these three new finer grained ConnectionHandlers. The nesting of handlers will not create multiple connections to the database. Actually, the nesting class and the other ConnectionHandlers participating in the work will share the same connection, any possible transaction, and the isolation level settings. Having each of the domain pieces broken down in a more fine grained way allows them to be reused in many different combinations without having to recode any of the logic that is managing their persistence. Let’s look at the code now to see the changes in more detail starting with the SaveCustomerHeader class, please see below:

Page 55: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 55 of 67

public class SaveCustomerHeader extends UniversalConnectionHandler { protected static Contract contract1 = new BasicContract( "eCustomer", 1, 1 ); protected static Contract contract2 = new BasicContract( "EPR", 1, 1 ); protected CustomerDTO customer; public SaveCustomerHeader( CustomerDTO customer ) throws QueryException {

super( true ); setCustomer( customer ); QueryManager queryManager = QueryManager.getInstance( contract1 ); DAOConnection daoConn1 = queryManager.getDAOConnection( contract1 ); DAOConnection daoConn2 = daoConn1.shareControl( contract2, queryManager ); addDAOConnection( daoConn1 ); addDAOConnection( daoConn2 ); } public CustomerDTO getCustomer() { return customer; } public void setCustomer(CustomerDTO customer) { this.customer = customer; } public SaveCustomerQueryHandler getSaveHandler() throws QueryException { return (SaveCustomerQueryHandler)getQueryHandler(); } @Override public void handleQuery( Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler) throws QueryException { CustomerDTO customer = getCustomer(); if ( customer != null ) { DAOConnection daoConn1 = daoConnList.get( contract1 ); Query query1 = daoConn1.getQuery(); JavaBeanStream inputs = new JavaBeanStream( customer ); inputs.setSelectorName( "updateHeader" ); query1.updateObject( daoConn1, queryHandler, inputs ); DAOConnection daoConn2 = daoConnList.get( contract2 ); Query query2 = daoConn2.getQuery(); inputs.setJavaBean( customer ); inputs.setSelectorName( "readIsCustomerAnAssociate" ); query2.getResult( daoConn2, queryHandler, inputs ); if ( getSaveHandler().isAssociate() ) { inputs.setSelectorName( "updateCustomerAssociate" ); query2.updateObject( daoConn2, queryHandler, inputs ); } } } protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { protected boolean isAssociate; public boolean isAssociate() { return isAssociate; } public void setAssociate(boolean isAssociate) { this.isAssociate = isAssociate; } @QuerySelector( name="updateHeader", operation=QueryMethod.updateObject ) public void handleHeaders( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException { }

Page 56: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 56 of 67

@QuerySelector( name="readIsCustomerAnAssociate", operation=QueryMethod.getResult ) public void isAssociate( Results results, Query query, Inputs inputs )

throws QueryException { if ( results.next() ) { setAssociate( results.getBoolean( "isAssociate" ) ); } } @QuerySelector(name="updateCustomerAssociate", operation=QueryMethod.updateObject ) public void handleAssociate( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException { } } }

You’ll notice that this class is very close to the old SaveCustomerConnHandler class from the first design pattern. The biggest change is that the class is only saving the Customer Header parts in the handleQuery() method and the custom QueryHandler class only have the pieces required for the header. Now let’s look at the SaveCustomerPhone class, please see below:

Page 57: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 57 of 67

public class SaveCustomerPhone extends UniversalConnectionHandler { protected static Contract contract1 = new BasicContract( "eCustomer", 1, 1 ); protected CustomerPhoneDTO[] phones; public SaveCustomerPhone( CustomerPhoneDTO... phones ) throws QueryException {

super( true ); setCustomerPhone( phones ); QueryManager queryManager = QueryManager.getInstance( contract1 ); DAOConnection daoConn1 = queryManager.getDAOConnection( contract1 ); addDAOConnection( daoConn1 ); setQueryHandler( new SaveCustomerQueryHandler() ); } protected CustomerPhoneDTO[] getCustomerPhones() { return phones; } protected void setCustomerPhone(CustomerPhoneDTO[] phones) { this.phones = phones; } @Override public void handleQuery(Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler) throws QueryException { CustomerPhoneDTO[] phones = getCustomerPhones(); if ( phones != null ) { DAOConnection daoConn1 = daoConnList.get( contract1 ); Query query1 = daoConn1.getQuery(); JavaBeanStream inputs = new JavaBeanStream( "updatePhone" ); for( CustomerPhoneDTO phone : phones ) { if ( phone != null ) { inputs.setJavaBean( phone ); query1.updateObject( daoConn1, queryHandler, inputs ); } } } } protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { @QuerySelector( name="updatePhone", operation=QueryMethod.updateObject ) public void handlePhones( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException { } } }

You’ll notice that this class looks very similar to the earlier design pattern example with just the pieces for saving a CustomerPhoneDTO domain object included. The details for the customer header and the addresses have been removed. You might well guess by now that the SaveCustomerAddress class will be following the same general re-orgainization pattern, let’s look at that now:

Page 58: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 58 of 67

public class SaveCustomerAddress extends UniversalConnectionHandler { protected static Contract contract1 = new BasicContract( "eCustomer", 1, 1 ); protected CustomerAddressDTO[] addresses; public SaveCustomerAddress( CustomerAddressDTO... addresses ) throws QueryException {

super ( true ); setCustomerAddresses( addresses ); QueryManager queryManager = QueryManager.getInstance( contract1 ); DAOConnection daoConn1 = queryManager.getDAOConnection( contract1 ); addDAOConnection( daoConn1 ); setQueryHandler( new SaveCustomerQueryHandler() ); } protected CustomerAddressDTO[] getCustomerAddresses() { return addresses; } protected void setCustomerAddresses(CustomerAddressDTO[] addresses) { this.addresses = addresses; } @Override public void handleQuery(Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler) throws QueryException { CustomerAddressDTO[] addresses = getCustomerAddresses(); if ( addresses != null ) { DAOConnection daoConn1 = daoConnList.get( contract1 ); Query query1 = daoConn1.getQuery(); JavaBeanStream inputs = new JavaBeanStream( "updateAddress" ); for( CustomerAddressDTO address : addresses ) { if ( address != null ) { inputs.setJavaBean( address ); query1.updateObject( daoConn1, queryHandler, inputs ); } } } } protected static class SaveCustomerQueryHandler extends AnnotatedQueryHandler { @QuerySelector( name="updateAddress", operation=QueryMethod.updateObject ) public void handlePhones( Object target, boolean success, int count,

Query query, Inputs inputs ) throws QueryException { } } }

This too is following the same design as the SaveCustomerPhone class. All three of these classes extend the UniversalConnectionHandler, so when used independently; they will be in a discrete unit-of-work for all the changes to the database. The major change therefore must be in the SaveCustomerConnHandler class, let’s look at it now:

Page 59: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 59 of 67

public class SaveCustomerConnHandler extends UniversalConnectionHandler { private CustomerDTO customer; public SaveCustomerConnHandler( CustomerDTO customer ) throws QueryException { super( true ); this.customer = customer; } @Override public void handleQuery(Map<DAOConnection, DAOConnection> daoConnList,

QueryHandler queryHandler) throws QueryException { if ( customer != null ) { new SaveCustomerHeader( customer ).execute(); new SaveCustomerPhone( customer.getPhoneNumbers() ).execute(); new SaveCustomerAddress( customer.getAddresses() ).execute(); }

} }

This updated class is short, but it is all new. Let’s look at each part of the class. First, note that the constructor is setting itseld as transactional and saving the customer DTO into a member varaible. Next, let’s look at the handleQuery() method. If the customer DTO object is not null, then the DTO component objects are created and an instance of the existing fine grained custom ConnectionHandlers we already reviewed are executed. Since universal handler’s auto-nest, the transaction scope is maintained across these fine grained sub-handlers and the unit of work scope is controlled by this outer most class. Note that the SaveCustomerHeader, SaveCustomerPhone, and SaveCustomerAddress handlers were also set to be transactional. What makes this design pattern very powerful is the sharing that occurs. First, all the connection handlers will use the same JDBC connection, or Controls, to the database. Recall from earlier that the Controls govern the interaction with the database. When using a UniveralConnectionHandler, the Controls are automatically shared. No discrete calls need to be added to share the controls like we had to do for multiple Contracts. Next, all the handlers are transactional so multiple units of work will not be created. These too will be shared between the handlers. All the work will happen inside of a single transaction with the database. If an exception is thrown in any of the nested handlers, all the work will be rolled back. Also, any isolation level setting will also be shared between the parent handler and all the participating handlers. Lastly, if any of the calls were to have used the framework BasicDAO methods, they also use UniversalConnectionHandlers so there interactions would also auto-participate into the interaction the same way as our custom handlers did. Finally, let’s look at the BusinessLogic rules class that starts all the work, please see below:

public class SaveCustomer { public void doWork() throws QueryException { CustomerDTO customer = new CustomerDTO(); // add code to set the customerDTO values as appropriate // Create the handler and execute it. SaveCustomerConnHandler saveHandler = new SaveCustomerConnHandler( customer ); saveHandler.execute(); } }

The code is identical to the first design pattern shown. The details on how you organize the custom connection handlers should be unimportant to the business rules class making use of them. The handler classes should encapsulate all this logic.

Page 60: DAO Consumer's Guide v1.0.11

Best Practice Design Patterns Second Desing Pattern

DAO Framework Page 60 of 67

Hopefully you see the power of this design pattern to reuse existing connection handler logic. Following this pattern should all but eliminate the need for having duplicate code in many places within your individual handlers. You can easily create interactions without having to rework major code sections. You are free to select and choose among the many ConnectionHandler types as you were with the Stream classes from the beginning of our journey with the DAO Framework.

Page 61: DAO Consumer's Guide v1.0.11

Debugging Calls to iterator()

DAO Framework Page 61 of 67

9 Debugging

There are several options available to help you debug DAO interactions. We will address some of the built in features and see an example of how each can be used. The DAO Framework library can be used with the debugger directly, so the option to walk through an interaction is always available to the consumer. Debugging would typically be done within the Eclipse toolset using the debugging perspective. This document will not cover how to use Eclipse. Please refer to Eclipse.org or Google.com for more details on how to use the debugger inside of Eclipse. 9.1 Calls to iterator() For NVStreams, sometime you just want to know what properties are available. Back in the NVStream section 2.4 we mentioned that there were some calls available, but they could slow down a production application. For debugging, however, they are very useful. Let’s take a look at how you can use the Iterator() call on an NVStream to see the property names and values:

NVStream stream = inputs.getNVStream();

for ( Iterator<String> itr = stream.iterator(); itr.hasNext(); ) {

String name = itr.next(); System.out.print( " Property name: " + name + " is: " ); System.out.println( stream.getObject(name) );

}

To see the names or properties in a stream you use a call to the iterator() method on an NVStream class. We have created a simple for-loop that will print each property name and then access the stream to retrieve its value. As you can see this is a simple way to see all the values. Let’s say we wanted to see the Qualifiers on a stream:

NVStream stream = inputs.getSelector().getQualifiers(); for ( Iterator<String> itr = stream.iterator(); itr.hasNext(); ) {

String name = itr.next(); System.out.print( " Qualifier name: " + name + " is: " ); System.out.println( stream.getObject(name) );

}

First, all we had to do was change the stream instance we obtain to the qualifier stream. The rest of the code is identical, outside of the System.out.println statement that displays “Qualifier name:” instead of “Property name:”. To make these calls display when you are debugging, you can use a Logger callout. All you would have to do is wrap them inside of an if-block that contains your logging mechanism check and you would be done. 9.2 Calls to Debug.printStream() What if you wanted to see where in a Stream the property value is coming from? As we have learned throughout this document, streams can wrap other streams. And since results are streams too, a property could be coming from a set of results or from another level of the Inputs. How can you tell where the property is coming from in that case? There is another built in feature that will print where the property is coming from. Please see the example below:

Page 62: DAO Consumer's Guide v1.0.11

Debugging DAO Runtime Debugging

DAO Framework Page 62 of 67

public void readResults(Results results, Query query, Inputs inputs) throws QueryException { list = new ArrayList<String>(); while ( results.next() ) { System.out.println( "debug: " + NVStream.Debug.printStream(results, false) ); String hostName = results.getString( "hostName" ); list.add( hostName ); } }

This is a snippet of code from a simple ResultsReader implementation. We have the results being passed into us but we don’t know what is under them in the hierarchy. To display this visually, you can make a call to the NVStream.Debug.printStream() method an pass in the NVStream instance. In our example above, you will note that a “false” value is being passed as a second argument. This is an optional argument that will trigger the method to print the value of the property. This value display will call the toString() method on the value object when set to “true”. Depending on what is being stored in the results the output may just be a Java Instance reference with a memory reference. Since most properties are lower level constructs like Strings or ints, you will see their values directly. Below is a sample of what the output looks like:

debug: com.homedepot.ta.aa.dao.stream.ResultsStream Main Stream com.homedepot.ta.aa.dao.stream.ResultsStream$InnerResultsStream property: hostID property: hostName Delegate Stream com.homedepot.ta.aa.dao.stream.InputsResults Source Stream encapsulates: com.homedepot.ta.aa.dao.stream.MapStream com.homedepot.ta.aa.dao.stream.MapStream, selector: allHosts

What this shows is that a ResultsStream is being used. All ResultsStreams are delegate streams by default. The main stream in the set is a ResultsStream$InnerResultsStream. This is the class in front of the actual database ResultsSet. It has two properties, which were displayed. The Delegate stream is an InputsResults Stream. Its source stream is a MapStream. The MapStream contains no properties, since none printed, but it has a selector name of “allHosts”. If you read a “hostName” field from these results it comes from the database results set directly. Please note that the output lines will indent each time a stream is wrapped by another stream. 9.3 DAO Runtime Debugging Sometimes you want to enable debugging at runtime without having to change your code. This is possible with the DAO Framework by using a built-in RESTful API that is available. First, let’s look at a sample report of currently active debugging settings on a server instance. To do this, add the string “/THDDAO.setDebugging” to the end of any valid URL call to your application before any query string ( ?name=value ) parameters and you will see something like the following:

<?xml version="1.0" encoding="utf-8" ?> <debugging> <debuggingElement> <contractName allowNull="false">TaAaDistributedBatch</contractName> <contractVersion allowNull="false">1</contractVersion> <selectorName allowNull="true">readJobEntryList</selectorName> <userName allowNull="true">kjs01</userName> </debuggingElement> </debugging>

Page 63: DAO Consumer's Guide v1.0.11

Debugging DAO Runtime Debugging

DAO Framework Page 63 of 67

This report shows that there is a single debugging element currently enabled. Let’s look at the entry in more detail. First, your will see there is a contactName, contractVersion, selectorName, and userName present in the xml entry with the values respectivley of TaAaDistributedBatch, 1, readJobEntryList, and kjs01. This entry enables detailed debugging for the user kjs01, on the selector readJobEntryList for the TaAaDistributedBatch v1 contract only. Any other users, selectors, or contracts will not output any debugging information. Let’s take a look at the sample debugging output now, see below:

Selector: readJobEntryList inputs are: Property name: status is: DONE Property name: applicationName is: /DistributedBatch/OtherLauncher Property name: creationTimestamp is: 2009-12-29 08:28:34.782 Qualifier name: includeWorker is: false called Connection prepareStatement( SELECT ID, APPNAME, LASTACCESS, CREATIONTIME, LISTENERCNT, MAXINACTIVETIME, USERNAME, SMALL FROM sessions WHERE PROPID = 'JOB' AND SMALL = ? AND APPNAME = ? AND CREATIONTIME <= ? ORDER BY LASTACCESS asc )

called PreparedStatement setString( 1, DONE ) called PreparedStatement setString( 2, /DistributedBatch/OtherLauncher ) called PreparedStatement setBigDecimal( 3, 1262093314782 ) called PreparedStatement executeQuery() called PreparedStatement close() called Connection close()

First, you will see the selector name being called and all the input values to the selector that were passed by the caller. This allows you to verify what values are being passed to the DAO call. Next, you will see a series of lines that start with “called [JDBC Type] detail”. Each of these lines reflects a call made by the DAO library against a specific JDBC resource. The call(s) made and the values passed are all output. You will note that the SQL statement that was used is displayed and subsequent calls show setting values, executing a query, and closing of the resources. The debugging output will display calls made against JDBC Connection, Statement, PreparedStatement, and CallableStatement classes in the java.sql package that are used by a DAO library. Now, let’s recall the entry shown in the xml above where a very specific entry enabled only one named selector for detailed debugging to be output. The debugging information at the DAO level can be very large, and when used with a large set of possible calls and/or large number of users in the system, it can be hard to read and intepret. The DAO Framework provides four setting levels to allow the debugging to be surgically enabled to look for a specific problem area. Let’s review the levels now from most specific to least specific: Debugging Levels:

1. Contract + Selector + User 2. Contract + User 3. Contract + Selector 4. Contract

You should choose the level that is highest in the list to limit the amount of data that is output. Limiting to a given user is a great way to accomplish this. If there is a problem in a given area of the code that is suspected, limiting to a named selector or a series of selectors is best. If the problem area (i.e. selector name(s)) is not known, then enabling for an entire contract would be the next best choice. The use of a user field will only work for applications that enable security. If the application does not use security, then the levels without a user field would be the next choice. Let’s take a look at how to enable a new debugging entry: Request:

http://localhost:8080/DistributedBatch/OtherLauncher/THDDAO.setDebugging?contractName=test&contractVersion=1&userName=kjs01&selectorName=sel1&mode=true

Page 64: DAO Consumer's Guide v1.0.11

Debugging DAO Runtime Debugging

DAO Framework Page 64 of 67

Response: <?xml version="1.0" encoding="utf-8" ?> <debugging> <callMade> <contractName allowNull="false">test</contractName> <contractVersion allowNull="false">1</contractVersion> <selectorName allowNull="true">sel1</selectorName> <userName allowNull="true">kjs01</userName> <mode allowNull="false">true</mode> </callMade> <debuggingElement> <contractName allowNull="false">test</contractName> <contractVersion allowNull="false">1</contractVersion> <selectorName allowNull="true">sel1</selectorName> <userName allowNull="true">kjs01</userName> </debuggingElement> </debugging>

First, you can see the bold section of the URL request made with a browser. The string as noted before was added to the end of the URL to an application, but this request also send 5 additional name-value pairs:

1. contractName=test 2. contractVersion=1 3. userName=kjs01 4. selectorName=sel1 5. mode=true

When you send name-value pairs, you are requesting a change to the current settings. Make sure to type the case of the name and values correctly, as they are taken literally. If the call is successful, then the output xml displays an additional element called <callMade> that shows what processing occurred and the values used. If you don’t see the <callMade> entry, then you most likely mistyped something. Next, in the xml is the current list of <debuggingElement> entries which should include the new entry just made. If the mode value (#5 above) is true, the entry is added. If the mode value is false, then the entry is removed. Mulitple calls are allowed and this is how a series of selectors can be enabled for a given contract. The xml entry provides hints to which fields are optional. If you look at the field names, you will see an attribute in the xml that states if the property allowsNull or not. Values that allowNull can be left out of the request to turn on lower levels of debugging like the following:

http://localhost:8080/DistributedBatch/OtherLauncher/THDDAO.setDebugging?contractName=test&contractVersion=1&mode=true <?xml version="1.0" encoding="utf-8" ?> <debugging> <callMade> <contractName allowNull="false">test</contractName> <contractVersion allowNull="false">1</contractVersion> <selectorName allowNull="true">null</selectorName> <userName allowNull="true">null</userName> <mode allowNull="false">true</mode> </callMade> <debuggingElement> <contractName allowNull="false">test</contractName> <contractVersion allowNull="false">1</contractVersion> <selectorName allowNull="true">sel1</selectorName> <userName allowNull="true">kjs01</userName> </debuggingElement> <debuggingElement> <contractName allowNull="false">test</contractName> <contractVersion allowNull="false">1</contractVersion> </debuggingElement> </debugging>

In this example, the selectorName and userName where left out. The <callMade> entry shows them as null. After the processing, you can now see both entries are displayed and are active.

Page 65: DAO Consumer's Guide v1.0.11

Debugging DAO Logs

DAO Framework Page 65 of 67

Using various requests, you should be able to setup the debugging to what you need to see to help diagnose an issue with the DAO Framework at runtime. It should be noted that all entries made are valid for upto 24 hrs. They will be automatically turned off. But, it is best practice to turn off all the entries made by calling them with a mode=false directive until the list is empty. Leaving debugging entries on for long periods of time will quickly fill up the application logs and also slow down the application. When all entries are removed you will see a report like the following:

<?xml version="1.0" encoding="utf-8" ?> <debugging> </debugging>

9.4 DAO Logs Another feature of the DAO Framework is automatic logging of each call to the framework. The log contains valuable information and it will be produced on production systems by default. If your single business logic call makes several DAO calls to produce its overall result, all of the individual calls will be logged and correlated for reference. Let’s take a look at a single line from the DAO logs:

[08/May/2009:07:45:04.000-0400] 127.0.0.1 kjs01 GET /eCustomer/FindCustomers?number=103 HTTP/1.1 18526215 eCustomer 1 - findCustomer O com.homedepot.ta.aa.example.services.CustomerQuery 128 172 Y 6 -

The 2 lines shown above are really one long that is wrapping. Each field is separated by a space character. Below is a list of each field’s definition:

# Column Definition Sample Value 1 Date and Time [08/May/2009:07:45:04.000 -0400] 2 Client IP Address/Hostname 127.0.0.1 3 User ID kjs01 [ - = no user id ] 4 Type of HTTP call GET, POST, BATCH 5 URL called /eCustomer/FindCustomers?number=103 6 HTTP protocol used HTTP/1.1 7 Correlation ID 18526215 8 Contract name eCustomer 9 Business Use ID 1

10 Contract Version - [- = no version, Number otherwise] 11 Selector Name findCustomer 12 Type of Operation O [ O=Object, R=Row-by-row, I=Insert,

U=Update, D=Delete ] 13 Implementing class com.homedepot.ta.aa.example.services.CustomerQuery14 Elapsed Time in Milliseconds

to Access of the First Row of Data

128

15 Elapsed Time in Milliseconds to the End of the Callback Method

172

16 Success/Failure Flag Y [ Y=success, N=failure ] 17 Row Count 6 [ - = no value recorded ] – The number of

rows read, updated, inserted or deleted. 18 Exception Details - [ - = no exception ]

Page 66: DAO Consumer's Guide v1.0.11

Debugging DAO Logs

DAO Framework Page 66 of 67

Taking all these fields together, the user ID and the URL that generated the call are easy to identify. The DAO operation they ran is also easy to understand. If any exception is produced, the exception details would have be logged. The correlation ID is a generated number that helps tie multiple calls together. It will match between multiple lines in the log to show that multiple actions occurred for a single URL request. Let’s take a look at what correlated ids would look like for three log lines:

[08/May/2009:07:45:19.001-0400] 127.0.0.1 kjs01 GET /TestDao/TestServlet HTTP/1.1 18526217 placementHosts 1 - allHosts R com.homedepot.ta.aa.test.db.AnnoPlacementQuery 0 0 Y 14 - [08/May/2009:07:45:19.001-0400] 127.0.0.1 kjs01 GET /TestDao/TestServlet HTTP/1.1 18526217 placementHosts 1 - racks R com.homedepot.ta.aa.test.db.AnnoPlacementQuery 15 15 Y 22 - [08/May/2009:07:45:19.001-0400] 127.0.0.1 kjs01 GET /TestDao/TestServlet HTTP/1.1 18526217 placementHosts 1 - racks U com.homedepot.ta.aa.test.db.AnnoPlacementQuery 15 15 Y 1 -

The 6 lines shown above are really three that are wrapping. A blank line was added between each line for readability. If you look at the correlation ID 18526217 in each line, they match. All these DAO operations ran on behalf of a single URL request. Note that the selector names are changing between individual calls. You may also note that the type of operation changed for the final call from “R” in the first two to a “U” call for the last. If you are using the DAO Framework with Tomcat from a normal interactive web application that is making periodic browser requests, logging is fully automatic. If you have a background thread in your program, like a TimerTask or a batch job, the DAO Framework logging is not automatic until you take an additional action. You will need to install a BatchDAOLogger instance with a call to a QueryManager method once inside the scope of the new thread. The one-time call that is required from the new thread is shown below:

QueryManager.setDAOLogger( new BatchDAOLogger() );

Page 67: DAO Consumer's Guide v1.0.11

Debugging DAO Monitor

DAO Framework Page 67 of 67

9.5 DAO Monitor The DAO Framework also keeps detailed statistics for each call made at a Contract/Selector level. An XML output can be obtained to see the internal statistics. Let’s take a look at some sample output from the monitor request:

<?xml version="1.0" encoding="utf-8" ?> <stats>

<runningFor name="BasicQueryManager" milliseconds="8951720" /> <contract name="eCustomer" version="1">

<selector name="findCustomer"> <totalCount>2</totalCount> <succeededCount>1</succeededCount> <failedCount>1</failedCount> <lastCallTime>94 ms</lastCallTime> <averageCallTime>55.0 ms</averageCallTime> <lastElapsedTime>94 ms</lastElapsedTime> <averageElapsedTime>55.0 ms</averageElapsedTime> <minMillis>16</minMillis> <maxMillis>94</maxMillis> <maxMillisTime>Mon Aug 10 14:00:59 EDT 2009</maxMillisTime> <lastExecutionTime>Mon Aug 10 14:00:59 EDT 2009</lastExecutionTime> <lastErrorTime>Mon Aug 10 14:00:54 EDT 2009</lastErrorTime>

</selector> <selector name="customersList">

<totalCount>3</totalCount> <succeededCount>2</succeededCount> <failedCount>1</failedCount> <lastCallTime>78 ms</lastCallTime> <averageCallTime>62.0 ms</averageCallTime> <lastElapsedTime>78 ms</lastElapsedTime> <averageElapsedTime>62.0 ms</averageElapsedTime> <minMillis>16</minMillis> <maxMillis>94</maxMillis> <maxMillisTime>Mon Aug 10 14:00:59 EDT 2009</maxMillisTime> <lastExecutionTime>Mon Aug 10 14:03:15 EDT 2009</lastExecutionTime> <lastErrorTime>Mon Aug 10 14:00:54 EDT 2009</lastErrorTime> </selector>

</contract> </stats>

What you can see is that a concrete implementation of a QueryManager called BasicQueryManager is running. It has been running for 8951720 milliseonds (or ~2.48 hours). One contract has executed called eCustomer version 1. Within the eCustomer contract two selectors have executed, findCustomer and customersList. The number of executions and the timing statistics are shown for each. The failed count and the last failure time are also displayed. To generate this report add the string “/THDDAO.getStatistics” to the end of any valid URL call to your application before the query string ( ?name=value ) parameters and you will see this data.