com.marringtons.object
Class DAO

java.lang.Object
  extended bycom.marringtons.object.DAO
All Implemented Interfaces:
Closeable, Comparable
Direct Known Subclasses:
Session.PersistentData

public class DAO
extends Object
implements Closeable, Comparable

DAO is the core of the Adept persistent storage mechanism. All database objects inherit from it. All fields that are not static, final or transient are saved. Objects as well as primative are written to any depth necessary. Strings, Maps, Collections and DAOs are treated specially. A DAO field inside a DAO will be saved as a pointer into the database of the inner field. This makes the objects fully relational. Care must be taken with DAO fields that they are retrieved from the database when the outer DAO is created. They are committed before saving the location. This will cause changes to be recorded on update() or add(). If they are not retrieved from the database nor set as update() or add(), they will not be saved and later retrieved as empty. As well as individual inner DAOs, Adept supports array, Collections and Maps that include DAOs in a relational manner.

Defining a Database Object

Typically create an static inner extending DAO. It is a value object and should have the minimum or no code. If it has inner classes extending Index, they will be used to create hash indexes for the DAO. Names in the Index class must have the same names as fields in the DAO. For efficiency, put the most unique index first. This is particularly important for inner DAOs as they use commitUnique() when loaded from XML.

  static class MyDAO extends DAO
   {
     public int integer; // can contain any form of primative
     public String string; // and Strings
     public MyOtherClass myOtherClass; // and any other class you have defined to any depth
     public HashMap nameValuePairs; // Maps are saved and NVP for more efficient storage
     public DAO anotherDAO; // normalised - committed and saved as a database pointer only
     public ArrayList myList; // as are Collections
     public Date timeStamp;	// a very special field - with the record timestamp of the last record update
     public static class PrimaryIndex extends Index { int integer; String string; }
     public static class SecondaryIndex extends Index( String string; }
   }

 

Creating/Attaching to a database

For a simple application with a single database:
 DAO.associateDatabase("db1", false); // in init code - for all access ojects
 // ... and for each DAO as needed ...
 MyDAO myDAO = new MyDAO(); // will be in the last (or only) database used.

 
If the application needs to run multiple databases, the DAO objects need to be associated with the correct database. See Database for examples. Association is not mandatory. If it is not done, the class is associated with the default database for the application. This database is named after the program in the output directory. The index size can be be set directly from properties. When adding a new type of DAO to the system, the database needs a guess on the likely number of records to size the index. This is not a critical requirement. Only if it is 10 times too small will performance degrade perceptibly. Too large a figure will cause some wasted disk space, but again not excessive. This maximum can be specified on the associate() command as above, in the properties file (as indexClassName.indexSize ) or use the default of 1,000,000. To get the required property key, run once without and get the full class name from the log file.

Database Integrity Protection

Rather than copying a DAO object to a POJO, override update() in your DAO then create a package private update method that calls super.update. This way only methods in the same package can update data - providing whatever checks you like. Alternatively, call the readOnly() method on the DAO or search objects to stop updates from occurring. And if that is still not good enough, inherit from Object or POJO and use the DTO class to associate it with persistence.
  static class MyDAO extends DAO
   {
     ...
     public void update() { throw IOException( "Illegal commit()"); }
     void myDAOupdate() { super.update(); }
   }
 

Adding/Changing/Deleting Records

The DAO system uses an add/update/commit method of operation. update() includes a check that stops the adding of initialised records. It also keeps a copy of the disk based data and compares before doing the physical update. commitUnique() is a special form of commit that checks for an existing identical record before committing. It is used by inner DAOs specifically during XML import.
 MyDAO myDAO = new MyDAO();
 myDAO.update();
 loadMyDAO(myDAO, 1);
 myDAO.commit();

 MyDAO dao = myDAO.copy();
 dao.update();
 changeMyDAO(dao, 2);
 dao.commitUnique();
 
update() is mandatory for changing existing records and must be called before changing the data when updating a record.
 MyDAO dao = search.first("test record", false);
 if (dao == null) dao = defaultDAO.copy();
 dao.update();
 // ...do the normal stuff
 changeMyDAO(dao);
 dao.commit(); // will only write if the dao was actually changed

 
The example above will use update() for either depending on whether an existing record is found. Updates can be dropped instead of written to disk by using discard() instead of commit(). The basic system allows duplicate entries. If you have an application with a unique constraint you need to account for that in the code. The above example does this by first searching for a record and only adding it if not found. A commit is done explicitly when a DAO is closed or finalised. The current record can be deleted, along with all indexes.
 if ((dao = search.first("my key", false)) != null) dao.delete();
 
All DAO based classes implement comparable with super.comparable();

Importing and Exporting Objects

There are times when the objects need to be saved externally, transmitted or represented in an (almost) human readable form. To this end, there are a number of classes in ObjectXML that create a XML representation. It is possible to scrape a DTO directly from or to a user entry structure, including conversion to and from strings using the static ObjectScraper class methods. Both classes also have pojo interfaces for when the data is not to persist in the object database.

Timestamps

Sometimes it is important to record when a record is written to the database. If you create a public field of Class Date and name timeStamp, it will automatically updated whenever the record is written.

DAOs without a Database

While it is not quite obvious, the POJO class lives in the database package because it uses common code for creation processing and internal functions. Use POJO if you want to create an object with deep hashcode, compare toString and equals methods.

DAO for the Purist

For the purist, or when the DAO is to be sent to remote code bases via RMI, database access needs to be disassociated from the data. The data should be contained in either a pojo or a POJO if you want some of the advanced features without being tied to persistent storage. DAO allows for this separation with a minimum if inconvenience.
 Class MyPojo
   {
     String data;
     Class index extends Index { String data; }
   }
 MyPojo myPojo = new MyPojo();
 DAO dao = DAO.getInstance( myPojo);	// call whenever you need to retrieve DAO interface
 myPojo = (MyPojo) dao.data();	// to retrieve data from DAO
 
If you prefer not to sully your data with index references you can create a new DAO type with indexes and associate that way.
 Class MyPojo
   { String data; }
 Class MyPojoDAO
   { Class index extends Index { String data; } }
 static { DAO.associate( MyPojo.class, MyPojoDAO.class); }
 MyPojo myPojo = new MyPojo();
 DAO dao = DAO.getInstance( myPojo);	// call whenever you need to retrieve DAO interface
 myPojo = (MyPojo) dao.data();	// to retrieve data from DAO
 

Author:
Paul Marrington
See Also:
Index, Database, ObjectXML, ObjectScraper

Constructor Summary
DAO()
          DAO instantiation.
 
Method Summary
 void close()
          Close the DAO - writing if dirty.
 void commit()
          Replace existing object with changes (if any are recorded).
 void commitImmediate()
          Replace existing object with changes (if any are recorded).
 int compareTo(Object that)
          So that DAO value objects can be sorted, added into trees, etc.
 DAO copy()
          Return a copy of a DAO object that can then be added to the database as a new record.
 void delete()
          Delete the current object from the database.
 void discard()
          Discard any changes made to the current object.
 boolean equals(Object object)
          For hashmaps a DAO is equal if it is the same record on disk.
 boolean find()
          Search the database for a unique DAO.
 DAO get(int record)
          Get the record from the cache if available, otherwise from the database.
 int getCurrentRecord()
          External access to current record is via a getter so that if a new DAO has been created it is committed to disk to provide a current record for use elsewhere.
 int hashCode()
          For hashmaps a DAO is equal if it is the same record on disk.
 boolean isActive()
          If the DAO fails to initialise it cannot throw an exception.
 DAO load()
          If you are using lazy loading on a first/next, then you will need to use load() on DAOs inside the DAO returned (relational DAOs) to ensure that the data is loaded.
static DAO newInstance(Class daoClass)
          Factory method to make a new instance of a DAO.
 void readOnly()
          Sets the DAO being created to read/only.
 void setCurrentRecord(int record)
          Set the current record when an initialiser just won't do.
 String toString()
          The String representation of an object is in XML.
 void update()
          Mark an item to save on commit if changed.
 
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
 

Constructor Detail

DAO

public DAO()
DAO instantiation. Uses prior association with a database or the most recent database association if there was not prior recorded.

Method Detail

readOnly

public void readOnly()
Sets the DAO being created to read/only. Search us used to return search results as read/only. This method could be used for DAOs that are never to make it to the database.


isActive

public boolean isActive()
If the DAO fails to initialise it cannot throw an exception. Use isActive if you need to check. It is very unlikely to fail and you will get IOException on other activities if it does. Closing a DAO does not deactivate it.

Returns:
true if the DAO is related to a database and ready for use.

close

public void close()
Close the DAO - writing if dirty. Not a true close as the DAO can continue to be used afterwards. It is a requirement for caches and other storage mechanisms.

Specified by:
close in interface Closeable
See Also:
Closeable.close()

get

public DAO get(int record)
        throws IOException
Get the record from the cache if available, otherwise from the database. Options for lazy/eager and read only/write are set from this the parent DAO.

Parameters:
record - database record to retrieve
Returns:
Data Object
Throws:
IOException

load

public DAO load()
         throws IOException
If you are using lazy loading on a first/next, then you will need to use load() on DAOs inside the DAO returned (relational DAOs) to ensure that the data is loaded. If will not force a reload if it has already been loaded.

Returns:
DAO loaded (one in object is not functional).
Throws:
IOException

copy

public DAO copy()
Return a copy of a DAO object that can then be added to the database as a new record.

Returns:
a deep copy of the DAO as a new object.

update

public void update()
Mark an item to save on commit if changed.


find

public boolean find()
             throws IOException
Search the database for a unique DAO. Used by commitUnique() to make sure that inner DAOs are unique.

Returns:
true if it is set to the unique value, false if not found.
Throws:
IOException

commit

public void commit()
Replace existing object with changes (if any are recorded). Queued to be written in good time. Use commitImmediate if you need to retrieve again in the next millisecond or so. If the action is part of a transaction the commit is deferred until the transaction is instructed to complete it's task.


getCurrentRecord

public int getCurrentRecord()
                     throws IOException
External access to current record is via a getter so that if a new DAO has been created it is committed to disk to provide a current record for use elsewhere. Note that as a side-effect it will commit a new record to disk immediately while an existing record is not committed before. Hence if you want to record the current record elsewhere for a possibly changed DAO, call getCurrentRecord() before commit() as the latter may not be necessary.

Returns:
the current record written for this DAO - does not change as DAO is updated
Throws:
IOException

setCurrentRecord

public void setCurrentRecord(int record)
Set the current record when an initialiser just won't do.

Parameters:
record - to set as current record.

commitImmediate

public void commitImmediate()
                     throws IOException
Replace existing object with changes (if any are recorded). Take care as it bypasses any transactions and writes immediatelly.

Throws:
IOException

delete

public void delete()
            throws IOException
Delete the current object from the database.

Throws:
IOException

discard

public void discard()
Discard any changes made to the current object.


equals

public boolean equals(Object object)
For hashmaps a DAO is equal if it is the same record on disk.

Parameters:
object - to compare against.
Returns:
True if they are logically the same.
See Also:
Object.equals(java.lang.Object)

hashCode

public int hashCode()
For hashmaps a DAO is equal if it is the same record on disk. Forces a write for newly created records.

Returns:
unique code for this item - use the record number.
See Also:
Object.hashCode()

compareTo

public int compareTo(Object that)
So that DAO value objects can be sorted, added into trees, etc.

Specified by:
compareTo in interface Comparable
Parameters:
that - to compare to.
Returns:
-1, 0 or 1.
See Also:
Comparable.compareTo(java.lang.Object)

toString

public String toString()
The String representation of an object is in XML. This is far more useful that the default object address. Start using it in stack dumps to see what the data was really like.

Returns:
XML representation of the object.
See Also:
Object.toString()

newInstance

public static DAO newInstance(Class daoClass)
Factory method to make a new instance of a DAO. It turns the Java exceptions into an unchecked ProgrammingErrorException if it is not possible.

Parameters:
daoClass - Being the class for the DAO we want.
Returns:
A new instance of the DAO using the default constructor.


Copyright © 2005 Paul Marrington http://library.marringtons.com