Wednesday 6 April 2011

J2ME Database Programming Using RMS


Persistent Storage for MIDlets

Persistent storage is a non-volatile place for storing the state of objects. For some applications, you might need objects to exist even after the application that created those objects quits. Without persistent storage, objects and their states are destroyed when an application closes. If you save objects to persistent storage, their lifetime is longer than the program that created them, and later you can read their state and continue to work with them.
The persistent storage facilities provided in the Java 2 Standard Edition (J2SE) platform, such as the JDBC and Object Serialization APIs, are not suitable for handheld devices with a small memory footprint. This is because the storage requirements vary significantly from one resource-constrained device to another. For example, a MIDlet that lets you buy (add to your portfolio) and sell (delete from your portfolio) stocks through your cell phone needs a place to store the stock database.
This article introduces the details of the MIDP Record Management System (RMS), a persistent storage for MIDlets, and shows how to develop MIDP database applications, using a stock database example. Throughout this article the termsrecord store and database are used interchangeably






Introducing the RMS
The MIDP provides a mechanism for MIDlets to persistently store data and retrieve it later. This mechanism is a simple record-oriented database called the Record Management System (RMS). A MIDP database (or a record store) consists of a collection of records that remain persistent after the MIDlet exits. When you invoke the MIDlet again, it can retrieve data from the persistent record store.
To use the RMS, import the javax.microedition.rms package.
Introducing the Record Store
Record stores (binary files) are platform-dependent because they are created in platform-dependent locations. MIDlets within a single application (a MIDlet suite) can create multiple record stores (database files) with different names. The RMS APIs provide the following functionality:
  • Allow MIDlets to manipulate (add and remove) records within a record store.
  • Allow MIDlets in the same application to share records (access one another's record store directly).
  • Do not provide a mechanism for sharing records between MIDlets in different applications.
Record Store Names
Record store names are case sensitive, and cannot be more than 32 characters long. Also, a MIDlet cannot create two record stores with the same name in the same application, but it can create a record store with the same name as a MIDlet in another application. When you create a new record store, it is stored under a directory called NOJAM. For example, assume you are using the Wireless Toolkit and that it is is installed under C:\J2MEWTK. If your project name is StockQuotes and your record store is mystocks, the record store is created underC:\J2MEWTK\NOJAM and it has the name mystocks.db.
Working with Threads
The MIDP RMS implementation ensures that all individual record store operations are atomic, synchronous, and serialized, so no corruption occurs with multiple access. However, if your MIDlets use multiple threads to access a record store, it is your responsibility to synchronize this access, or some of your records might be overwritten.
The RMS Package
The RMS package consists of the following four interfaces, one class, and five exception classes:
Interfaces
  • RecordComparator: Defines a comparator to compare two records.
  • RecordEnumeration: Represents a bidirectional record enumerator.
  • RecordFilter: Defines a filter to examine a record and checks if it matches based on a criteria defined by the application.
  • RecordListener: Receives records which were added, changed, or deleted from a record store.
Classes
  • RecordStore: Represents a record store.
Exceptions
  • InvalidRecordIDException: Thrown to indicate the RecordID is invalid.
  • RecordStoreException: Thrown to indicate a general exception was thrown.
  • RecordStoreFullException: Thrown to indicate the record store file system is full.
  • RecordStoreNotFoundException: Thrown to indicate the record store could not be found.
  • RecordStoreNotOpenException: Thrown to indicate an operation on a closed record store.
Programming with the RMS
Database programming with RMS is relatively straightforward. This section covers the essential RecordStore methods, and if you want to learn about its other methods, see the javax.microedition.rms APIs.
What is a Record Store?
A record store consists of a collection of records that are uniquely identified by their record ID, which is an integer value. The record ID is theprimary key for the records. The first record has an ID of 1, and each additional record is assigned an ID that is the previous value plus 1.
Opening a Record Store
To open a record store, use the openRecordStore() static method:
RecordStore db = RecordStore.openRecordStore("myDBfile", true);
The above code creates a new database file named myDBfile. The second parameter, which is set to true, says that if the record store does not exist, create it.
Note: If the openRecordStore() method is called by a MIDlet when the record store is already open by another MIDlet in the same application, the method returns a reference to the same RecordStore object..

Creating a New Record
A record is an array of bytes. You can use the DataInputStreamDataOutputStreamByteArrayInputStream, and ByteArrayOutputStreamclasses to pack and unpack data types into and out of the byte arrays. The first record created has an ID of 1 and is the primary key. The second record has the previous ID + 1.
Now suppose you have the following string record: FirstName, LastName, Age. To add this record to the record store, use the addRecord()method as follows:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF(record);
byte[] b = baos.toByteArray();
db.addRecord(b, 0, b.length);
You construct a DataOutputStream for writing the record to the record store, then you convert the ByteArrayOutputStream to a byte array, and finally you invoke addRecord() to add the record to the record store. Note that in this segment of code, no exceptions are handled. The stock database example discussed later shows how to handle the exceptions.
Reading Data from the Record Store
To read a record from the record store, you construct input streams instead of output streams. This is done as follows:
ByteArrayInputStream bais = new
ByteArrayInputStream(record1);
DataInputStream dis = new
DataInputStream(bais);
String in = dis.readUTF();
Deleting a Record from the Record Store
To delete a record from the record store, you have to know the record ID for the record to be deleted. To delete the record, use thedeleteRecord() method. This method takes an integer as a parameter, which is the record ID of the record to be deleted.
There is no method to get the record ID. To work around this, every time you create a new record, add its record ID to a vector like this:
Vector recordIDs = new Vector();
int lastID = 1;
//Add a record....parameters are missing here
db.addRecord();
// Now add the ID to the vector
recordIDs.addElement(new Integer(++lastID));
Now, to delete a record, find the record ID of the record you want to delete:
Enumeration IDs = recordIDs.elements();
while(IDs.hasMoreElements()) {
    int id = ((Integer) IDs.nextElement()).intValue();
    //Compare to see if this is the record you want by
    //invoking compare() which is shown next.
    //Then call db.deleteRecord(id);
}
Comparing my Record with Records in the Record Store
To search for the right record to delete, your application must implement the Comparator interface (by providing an implementation to thecompare method) to compare two records. The return value indicates the ordering of the two records. For example, suppose you want to compare two strings that you retrieved from two records. Here is a sample implementation:
public someClas implements Comparator {
  public int compare(byte[] record1, 
      byte[] record2) {
    ByteArrayInputStream bais1 = new 
  ByteArrayInputStream(record1);
    DataInputStream dis1 = new 
  DataInputStream(bais1);
    ByteArrayInputStream bais2 = new 
  ByteArrayInputStream(record2);
    DataInputStream dis2 = new 
  DataInputStream(bais2);

    String name1 = dis1.readUTF();
    String name2 = dis.readUTF();
    int num = name1.compareTo(name2);
    if (num > 0) {
      return RecordComparator.FOLLOWS;
    } else if (num < 0) {
      return recordcomparator.precedes;
    } else {
      return recordcomparator.equivalent;
    }
  }
}
The constants FOLLOWSPRECEDES, and EQUIVALENT are defined in the RecordComparator interface and have the following meanings:
  • FOLLOWS: Its value is 1 and means the left parameter follows the right parameter in terms of search or sort order.
  • PRECEDES: Its value is -1 and means the left parameter precedes the right parameter in terms on search or sort order.
  • EQUIVALENT: Its value is 0 and means the two parameters are the same.
Closing the Record Store
To close the record store, use the closeRecordStore() method












No comments:

Post a Comment