DataAccessObjectPattern
HomePage>>SourceCode>>JavaSource
Read more about the Data Access Object pattern here: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Okay, now that you've gotten through that, here's an example of this pattern implemented for use with UniData. This is the second-to-last diagram, the "Factory Method Pattern" since there's only one DBMS involved.
This code assumes that you have a PERSON file with these fields:
:LISTDICT PERSON SORT DICT PERSON TYP LOC CONV MNAME FORMAT SM ASSOC BY TYP BY LOC BY @ID 18:47:27 Apr 23 2003 1 @ID............ TYP LOC.......... CONV MNAME.......... FORMAT SM ASSOC..... @ID D 0 PERSON 15R S LAST.NAME D 1 Last Name 25L S FIRST.NAME D 3 First Name 15L S 3 records listed
So now you need to model your data structures in Java. The Person interface and PersonImpl implementation below handle that. The PersonImpl class adheres to the JavaBeans specification, which can make life easier later on in the project. These classes are in the "dto" package. DTO stands for Data Transport Object. There's a new name for this that I can't remember at the moment. The term "Model Bean" may also apply. These classes are in the "Model" part of the "Model View Controller" or MVC pattern.
The first version of Person.java looks like this. It will undergo a minor change a little later on.
package dto; public interface Person { String get[[FirstName]](); String get[[LastName]](); void set[[FirstName]](String name); void set[[LastName]](String name); }
Similarly, the initial PersonImpl.java follows, and it, too, will change slightly.
package dto; public class [[PersonImpl]] implements Person { private String firstName; private String lastName; public String get[[FirstName]]() { return firstName; } public String get[[LastName]]() { return lastName; } public void set[[FirstName]](String name) { firstName = name; } public void set[[LastName]](String name) { lastName = name; } }
Okay, so now you have a PERSON file full of records, and some Java classes to encapsulate the data. How do you get the data out of the database and into a PERSON object so you can use it in your Java program? That's where a DAO comes in. DAO stands Data Access Object, which you read about in the link at the top of the page. If you have not yet visited that link, I highly recommend it!
If you have not already customized and tested your UniSessionFactory, please go do that now. There is test code at TestFactory. You'll need it in the UniDataPersonDAO that's coming up soon.
Now that you can reliably retrieve connections to your database, it's time to use one to retrieve a PERSON record and construct a Person object. Remember the DAO Pattern document? Following it closely, we end up with the following classes which can be found in the .zip file linked to at the bottom of the page.
DAOFactory.java
UniDataDAOFactory.java
PersonDAO.java
UniDataPersonDAO.java
See the pattern? There is an interface (or abstract class) and an implementation for each piece of the puzzle. The reason for this is that the classes that need to use a "Person" object should not know or care that they are getting the data from UniData/UniVerse. The data layer is completely separate from the other layers of the application. In theory, this means you could replace UniData with something else, and not have to change any code except that which deals directly with UniData. In reality, it's never quite that simple, but it's a good thing to aspire to.
The code inside UniDataPersonDAO.java that actually reads from the database and constructs a Person object looks like this:
public Person read(String key) throws DAOException { Person person = null; try { /* * get a connection to the database * open the file and read the record * then close the file and the connection */ [[UniSession]] uSession = [[UniSessionFactory]].openSession(); [[UniFile]] uFile = uSession.openFile("PERSON"); [[UniString]] uString = uFile.read(key); uFile.close(); uSession.disconnect(); /* * Make a dynamic array out of the record so that * it can be addressed by field position */ [[UniDynArray]] udArray = new [[UniDynArray]](uString); person = new [[PersonImpl]](); person.setKey(key); person.set[[FirstName]]( udArray.extract(3).toString() ); person.set[[LastName]]( udArray.extract(1).toString() ); } catch ([[UniException]] ex) { throw new DAOException( ex ); } return person; }
A small issue comes up... while a "Person" has a first and last name, he doesn't really have a "key". But you'll need that if you ever want to write a new or changed PERSON record. The solution is to create a BaseDataTransportObject class which will have all the properties that all of your DTO's share. At least, a key, possibly an add/change date if that sort of information is stored in your database files. The system that I use has fields ADD.DATE, ADD.OPERATOR, CHANGE.DATE, and CHANGE.OPERATOR in [almost] every file. This is the small change I mentioned earlier, the Person/PersonImpl classes (and all future DTO's) will extend from the BaseDataTransportObject/BaseDataTransportObjectImpl, meaning that they will inheirit the getKey/setKey method, and any other methods you put there. The changed versions of the source code are contained in the .zip file.
Several things can happen when you read from a UniFile. If you try to use a null string for the key, it will throw an exception. You probably don't have any records that have an empty string for a key, so that's not a good choice, either. And what if the record is not found? UOJ will throw a UniFile exception, but do you want your application to come to a screeching halt just because the record was not found? Some ideas here: UniFileRecordNotFound
Hopefully that made sense, and you now have an idea of how to model your data files in Java and retrieve informatin from them. But how do you know that this works? Here's some test code, which can be found in the /src/test/dao directory of the .zip file above:
package dao; import java.io.[[BufferedReader]]; import java.io.[[InputStreamReader]]; import dto.Person; /** *@author Wendy Smoak *@created April 26, 2003 */ public class [[TestPersonRead]] { public static void main(String[] args) throws Exception { System.out.println("Entered [[TestPersonRead]].main"); [[BufferedReader]] in = new [[BufferedReader]](new [[InputStreamReader]](System.in)); System.out.print("Enter the PERSON record key: "); String key = in.readLine(); DAOFactory udFactory = DAOFactory.getDAOFactory(); [[PersonDAO]] personDAO = udFactory.get[[PersonDAO]](); Person person = personDAO.read(key); System.out.println("key: " + person.getKey() ); System.out.println("first name: " + person.get[[FirstName]]() ); System.out.println("last name: " + person.get[[LastName]]() ); System.out.println("Exiting [[TestPersonRead]].main\n\n"); } }
Onward to writing a record to the database! It's very similar to the code above to read, except that you accept a "Person" object and use it to update the database, rather than accepting a key and returning a Person.
public void update(Person person) throws DAOException { [[UniDynArray]] udArray = new [[UniDynArray]](); udArray.replace(1, person.get[[LastName]]() ); udArray.replace(3, person.get[[FirstName]]() ); try { /* * get a connection to the database * open the file and read the record * then close the file and the connection */ [[UniSession]] uSession = [[UniSessionFactory]].openSession(); [[UniFile]] uFile = uSession.openFile("PERSON"); uFile.write( person.getKey() , udArray ); uFile.close(); uSession.disconnect(); } catch ([[UniException]] ex) { throw new DAOException( ex ); } }
And again, the way you know this works is by running the test code:
package dao; import java.io.[[BufferedReader]]; import java.io.[[InputStreamReader]]; import dto.Person; import dto.[[PersonImpl]]; /** *@author Wendy Smoak *@created April 26, 2003 */ public class [[TestPersonWrite]] { public static void main(String[] args) throws Exception { System.out.println("Entered [[TestPersonWrite]].main"); [[BufferedReader]] in = new [[BufferedReader]](new [[InputStreamReader]](System.in)); System.out.print("Enter the PERSON record key: "); String key = in.readLine(); System.out.print("Enter the first name: "); String first = in.readLine(); System.out.print("Enter the last name: "); String last = in.readLine(); Person person = new [[PersonImpl]](); person.setKey( key ); person.set[[FirstName]]( first ); person.set[[LastName]]( last ); DAOFactory udFactory = DAOFactory.getDAOFactory(); [[PersonDAO]] personDAO = udFactory.get[[PersonDAO]](); System.out.println("Writing the person data"); if( key == null || "".equals(key) ) { System.out.println( "No key, not writing" ); return; } else { personDAO.update(person); } person = null; System.out.println("\nReading from the database"); Person newPerson = personDAO.read( key ); System.out.println("key: " + newPerson.getKey() ); System.out.println("first name: " + newPerson.get[[FirstName]]() ); System.out.println("last name: " + newPerson.get[[LastName]]() ); System.out.println("Exiting [[TestPersonWrite]].main\n\n"); } }
The .zip file containing all of the code and javadoc can be found here:
http://members.cox.net/opensourceaz/etc/unidata-dao-20030426.zip
Please read the README file in the 'unidata-dao' directory of the unzipped files. In order for this to work you will have to provide your own 'asjava.jar' (or asjava.zip) file, because I do not have permission to redistribute it. That file comes with UniData/UniVerse. In the Personal Edition of UniData 6, I found it in two places: /usr/ud60/bin/uojsdk/lib/asjava.zip or /usr/unishared/uojsdk/lib/asjava.zip
As always, feel free to contact me at wsmoak -at- gmail.com with any questions, and don't hesitate to edit this document if something is wrong or misleading.
-- Wendy Smoak