Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Dr.Dobb's journal.2005.12

.PDF
Скачиваний:
25
Добавлен:
23.08.2013
Размер:
9.06 Mб
Скачать

Corio

David

©

“IEEE is the virtual ‘office water cooler’ for the engineering profession. It’s a chance to network with peers, bounce ideas off people and get a feeling for what’s going on in other parts of the industry.”

Andrew Greenberg

IEEE Student Member

Portland State University

(shown far left)

Where are you getting your information from? Join IEEE.

www.ieee.org/member2

Month-Text Ordering

A month field-type that

sorts in month order

DAVID WINCELBERG

To have a fighting chance of remembering relatives’ birthdays and anniversaries, I put together a database table with the necessary information.

But in designing the table I ran into a problem: Which field-types should I select? The database I use, Corel Paradox, provides three date-related field-types —

Date, Time, and Timestamp. None of these seem appropriate since I do not want to sort rows based on the years when people were born or married. Instead, I included Month Name, Day, Year, and

Month Number columns. After entering the data, I sorted on the Month Number, Day, Last Name, and First Name columns.

This worked but has two drawbacks — I had to enter month numbers that correspond to month names, and the Month Number column was displayed on the printout. I later found that I could define a date display screen format that suppresses year numbers. (This format does not apply to printing, exporting, or publishing in HTML.) I then combined the

Month Name, Day, and Month Number columns into a Date column. The resulting display looks cleaner but makes adding to the table more difficult. The reason is that the Date column includes — but doesn’t show— year information. To add birthdays or anniversaries in future years, I will need to display the dates in a format that shows year numbers and enter new rows with the year set to 2005.

I present an alternative in this article. Databases should support a month fieldtype that will sort in month order. In searching the Web, I found that databases have varying support for date-related field-types. Of those that I looked at, MySQL has the most such field-types. It

David runs FileJockey Software and can be reached at http://www.FileJockeySoftware

.com/ or filejockey@compuserve.com.

allows Date, Datetime, Time, Timestamp, and Year fields.

Month-text ordering is also useful for filesystem sorting. For example, you could have a 2005 Marketing Plan folder that contains files such as January.doc, February.doc, and so on. These files might not be created in order and they may be modified out of order. As a result, sorting by the last-modified date/time would not necessarily arrange them properly. Other examples of month-related files include progress reports, sales reports, and newsletters. Some of these may be edited months after they are created. Folders with month names could be useful for storing photos and magazine articles.

MonthOrder Class

The first design goal for this class was to allow for short and long month names. In addition, I decided to include support for the common abbreviation “Sept.” Because the month names are not expected to change, they can be sorted alphabetically, stored in an array, and found using a binary search. However, because hash tables are available in Java and MFC, I decided to use one to store the month names.

How many entries should be placed in this hash table? In a shareware file manager that I haven’t completed, I use 12 entries that are keyed on the first three letters of the month names. To check if a word corresponds to a short or long month name, this program copies the first three letters, converts them to uppercase, and tests whether this key is in the table. If there is a match, it then tests if this word matches the beginning part of the corresponding month name and that the length of this word is three for any month, four for September, or the full length of the month name.

For the MonthOrder class, I decided to use 24 table entries. The additional entries are the short names, except for “MAY,” plus “SEPT.” As a result, the testing logic is simplified. A potential month name is converted to uppercase, an existing trailing dot is removed, and the key is checked in the hash table. (The previous routine does not handle trailing dots since they are removed by a tokenizer.) See Listing One.

The compareTo function in Listing Two starts by requesting month numbers for a pair of strings. If both strings are month names and their month numbers differ, these numbers are subtracted. If they

match, their lengths are subtracted. This is unnecessary but makes the order look nicer. If only one string is a month name, it is placed first. By grouping month names above other strings, there are no inconsistencies. For example, sorting “January,” “February,” and “Hat” would lead to an unpredictable arrangement without grouping. If neither string is a month name, they are ordered by using a routine that is based on my article “Alphanumeric Ordering” (DDJ, October 2000).

“The first design goal for this class was to allow for short and long month names”

The Java implementation of this routine differs from the C implementation in that substrings consisting solely of digits must be created for Integer.parseInt to work. The C function atoi can handle strings that start with digits and are followed by letters; see Listing Three.

Sample Program

MonthOrderApplet (available electronically, see “Resource Center,” page 4) demonstrates month-text ordering. To use it, load MonthOrder.html into a browser. The class files must be in the same folder as this file. Then, you could either add strings individually or you could add name sets. For the latter, check either or both of the checkboxes and press the Add Name Set(s) button. After checking the Long Month Names box and pressing this button, you will see what is in Figure 1. Then press the Sort button. The month names will be arranged as in Figure 2. You can add short month names and resort the list. Because the compareTo function in Listing Two orders strings based on length when they represent the same month, short names appear before full names; see Figure 3.

Pressing the Remove Item(s) button removes selected items. The corresponding

30

Dr. Dobb’s Journal, December 2005

http://www.ddj.com

DEC. 4–9, 2005

www.usenix.org/lisa05/dja

SAN DIEGO, CA

19TH LARGE INSTALLATION SYSTEM ADMINISTRATION CONFERENCE

FIND THE MISSING PIECES

TO YOUR TOUGHEST PUZZLES.

6 DAYS OF TRAINING

by industry experts, including:

Rik Farrow on Hands-on Linux Security

Don Bailey on 802.11 Wireless Network Penetration Testing

Richard Bejtlich on Network Incident Response

Jacob Farmer on Disk-to-Disk Backup

DLISA ’05 offers the most in-depth, real-world system administration training available!

3-DAY TECHNICAL PROGRAM

KEYNOTE: Qi Lu, Vice President of Engineering, Yahoo! Inc., on “Scaling Search Beyond the Public Web”

20+ INVITED TALKS INCLUDING:

Matt Blaze, University of Pennsylvania: “Picking Locks with Cryptology”

Kevin Bankston, EFF: “How Sysadmins Can Protect Free Speech and Privacy on the Electronic Frontier”

NEW! Hit the Ground Running Track

Refereed Papers, Guru Is In Sessions, Vendor Exhibition, BoFs, WiPs,

and more!

Real

World

System Administration

SPONSORED BY

THE ADVANCED COMPUTING SYSTEMS ASSOCIATION

Register by November 18 and save! www.usenix.org/lisa05/dja

(continued from page 30)

lem. One resolution is to surround text

Month Field-Type

routine does not use an array of indexes.

with braces to signal that it should be in-

A month field-type could contain short

Instead, it looks at each item and deletes

terpreted as a potential month. Then,

and long month names and compare them

it if it is selected. Afterwards, either the

when a filesystem comparison function

using the routines in Listings One and

count variable or the current index is up-

sees a string such as “{May},” it knows to

Two. An alternate approach is to cache

dated; see Listing Four.

look in a hash table to find the index of

the month indexes along with case and

File and Folder Names

the text inside the braces.

length information.

The other problem with mixing month

One possible encoding in a 32-bit in-

Several month names have meanings in

names and other strings was already ad-

teger starts with the left-most byte being

other contexts. For example, “march” and

dressed. Month names are grouped above

zero. The remaining bytes would contain

“may” are common words. In addition,

other strings so that a comparison func-

a case code, a length, and a month in-

“April,” “May,” and “June” are women’s

tion does not return inconsistent results

dex. Months encoded in this manner

names. This creates an interpretation prob-

to a sorting algorithm.

would be compared after applying a 0xFF

 

 

 

 

mask to extract the month index. The

 

 

 

 

 

 

 

 

case code would range from zero to two

 

 

 

 

to represent mixed, upper, and lower-

 

 

 

 

case, respectively. If the first letter of an

 

 

 

 

input month name is in lowercase, as-

 

 

 

 

sume that this name is in lowercase.

 

 

 

 

Otherwise, if the second letter is in upper-

 

 

 

 

case, assume that this name is in up-

 

 

 

 

percase. The remaining circumstance is

 

 

 

 

to assume that this name is in mixed case,

 

 

 

 

as in “January.”

 

 

 

 

The lengths would range from three to

 

 

 

 

nine, and the month indexes would range

 

 

 

 

from one to twelve. A display routine

 

 

 

 

would subtract one from an index, use this

 

 

 

 

value to select a month name from a string

 

 

 

 

array, extract the required number of char-

Figure 1: Alphabetically ordered long

Figure 2: Month-text ordered long

acters, and adjust the case, if necessary.

month names.

month names.

If 16-bit integers are available, then each

 

 

 

 

part of a month-field code could be put

 

 

 

 

into a nybble (4 bits). Then the mask

 

 

 

 

would be 0xF.

Conclusion

A month field-type would be helpful in database tables when day or year information is not useful for sorting or display. In a filesystem context, month-text ordering supplements alphanumeric ordering in that it also arranges file and folder names in a way that respects the patterns that people see in strings.

DDJ

Figure 3: Month-text ordered short and long month names.

32

Dr. Dobb’s Journal, December 2005

http://www.ddj.com

Listing One

import java.util.Hashtable;

public class MonthOrder implements SortTest { Hashtable map = new Hashtable();

public MonthOrder() {

 

 

MonthInfo[] monthList = new MonthInfo[24];

 

monthList[0]

= new MonthInfo ("JANUARY",

1);

monthList[1]

= new MonthInfo ("FEBRUARY",

2);

monthList[2]

= new MonthInfo ("MARCH",

 

3);

monthList[3]

= new MonthInfo ("APRIL",

 

4);

monthList[4]

= new MonthInfo ("MAY",

 

5);

monthList[5]

= new MonthInfo ("JUNE",

 

6);

monthList[6]

= new MonthInfo ("JULY",

 

7);

monthList[7]

= new MonthInfo ("AUGUST",

8);

monthList[8]

= new MonthInfo ("SEPTEMBER", 9);

monthList[9]

= new MonthInfo ("OCTOBER",

10);

monthList[10] = new MonthInfo ("NOVEMBER", 11);

monthList[11] = new MonthInfo ("DECEMBER", 12);

monthList[12] = new MonthInfo ("JAN",

1);

 

monthList[13] = new MonthInfo ("FEB",

2);

 

monthList[14] = new MonthInfo ("MAR",

3);

 

monthList[15] = new MonthInfo ("APR",

4);

 

monthList[16] = new MonthInfo ("JUN",

6);

// MAY is above

monthList[17] = new MonthInfo ("JUL",

7);

 

monthList[18] = new MonthInfo ("AUG",

8);

 

monthList[19] = new MonthInfo ("SEP",

9);

 

monthList[20] = new MonthInfo ("SEPT", 9);

// alternative

monthList[21] = new MonthInfo ("OCT", 10);

 

monthList[22] = new MonthInfo ("NOV", 11);

 

monthList[23] = new MonthInfo ("DEC", 12);

 

for (int i = 0; i < monthList.length; i++) { map.put (monthList[i].monthName, monthList[i]);

}

}// MonthOrder

private int findMonth (String monthName) { if (monthName == null) return -1;

// Prepares test string

String key = monthName.toUpperCase();

int dot = key.indexOf ('.'); // trims off trailing dot if (dot != -1) key = key.substring (0, dot);

MonthInfo mi = (MonthInfo) map.get (key); if (mi == null)

return -1;

else

return mi.index; } // findMonth

...

}

Listing Two

public int compareTo (String name1, String name2) { int m1 = findMonth (name1);

int m2 = findMonth (name2); if (m1 == -1 && m2 == -1)

return simpleANCompareTo (name1, name2);

else if (m1 == -1) // month names appear before other items return +1;

else if (m2 == -1) return -1;

else if (m1 == m2) // not needed, but makes order look nicer return name1.length() - name2.length();

else

return m1 - m2;

}// compareTo

Listing Three

private int simpleANCompareTo (String name1, String name2) { int size1 = name1.length(), size2 = name2.length();

int n1 = 0, n2 = 0, e1, e2; int val1, val2, test = 0;

while (n1 < size1 && n2 < size2) {

if (Character.isDigit (name1.charAt (n1))

&&Character.isDigit (name2.charAt (n2))) {

// Finds ends of numbers so that parseInt will work for (e1 = n1 + 1; e1 < size1

&& Character.isDigit (name1.charAt (e1)); ) e1++;

for (e2 = n2 + 1; e2 < size2

&& Character.isDigit (name2.charAt (e2)); ) e2++;

try {

val1 = Integer.parseInt (name1.substring (n1, e1));

}

catch (NumberFormatException ex) { val1 = -1;

}

try {

val2 = Integer.parseInt (name2.substring (n2, e2));

}

catch (NumberFormatException ex) {

val2 = -1;

}

if ((test = val1 - val2) != 0) return test;

n1 = e1;

n2 = e2;

}

else { // caseless match

test = Character.toLowerCase (name1.charAt (n1)) - Character.toLowerCase (name2.charAt (n2));

if (test != 0) return test;

else {

n1++; n2++;

}

}

}

// One or more strings has ended if (n1 >= size1 && n2 >= size2)

return 0;

else if (n1 >= size1) return -1;

else

return +1;

}// simpleANCompareTo

Listing Four

void removeButton_ActionPerformed (java.awt.event.ActionEvent event)

{

try {

int count = itemList.getItemCount();

for (int i = 0; i < count; ) {

if (itemList.isIndexSelected (i)) { itemList.delItem (i);

count--;

}

else

i++;

}

} catch (Exception e) {

}

}// removeButton_ActionPerformed

DDJ

(PPHMF IBT DIBMMFOHFT XJUI ZPVS OBNF PO UIFN

0VS NJTTJPO JT UP PSHBOJ[F UIF XPSMET JOGPSNBUJPO BOE NBLF JU VOJWFSTBMMZ BDDFTTJCMF BOE VTFGVM 5IJT JOWPMWFT TVSNPVOUJOH TPNF JOUFSFTUJOH DIBMMFOHFT BOE XF OFFE FYQFSJFODFE TPGUXBSF FOHJOFFST XJUI FYDFQUJPOBM EFTJHO BOE JNQMFNFOUBUJPO TLJMMT UP IFMQ VT EP UIBU *EFBMMZ ZPV IBWF B CBDLHSPVOE JO TVDI BSFBT BT IJHI FOE EJTUSJCVUFE TZTUFNT PQFSBUJOH TZTUFNT EBUB NJOJOH JOGPSNBUJPO SFUSJFWBM BOE NBDIJOF MFBSOJOH BOE ZPVSF QBTTJPOBUF BCPVU EPJOH HSFBU XPSL

*G ZPVWF FWFS JNBHJOFE BQQMZJOH ZPVS UFDIOJDBM DIPQT UP CFOFmU IVOESFET PG NJMMJPOT PG QFPQMF BSPVOE UIF XPSME (PPHMF NJHIU CF UIF QMBDF GPS ZPV "OE XIFUIFS ZPV XPSL JO .PVOUBJO 7JFX 4BOUB

.POJDB /FX :PSL #BOHBMPSF )ZEFSBCBE ;VSJDI PS 5PLZP XF UIJOL ZPVMM mOE (PPHMF UP CF B QMBDF XIFSF ZPV DBO BTQJSF UP PVUTJ[FE BDDPNQMJTINFOUT

$IFDL PVU UIF PQQPSUVOJUJFT BU XXXHPPHMF DPN KPCT *G ZPV TFF TPNFUIJOH UIBU TFFNT B QFSGFDU mU GPS ZPV QMFBTF DPOUBDU VT BU KPCT!HPPHMF DPN 8FE MPWF UP IFBS GSPN ZPV

http://www.ddj.com

Dr. Dobb’s Journal, December 2005

33

P R O G R A M M E R ’ S T O O L C H E S T

Object-Relational Mapping in Java with SimpleORM

A lightweight, yet powerful, ORM implementation

MARTIN SNYDER AND TED O’CONNOR

Object-relational mapping encompasses a wide range of techniques and libraries for handling bidirectional serialization of runtime

objects in an object-oriented system within a relational database. As is often the case with Java, a number of options are available, many of them open source. In this article, we examine a mature alternative named SimpleORM (http://www.simpleorm.org/), which was created and is maintained by Anthony Berglas.

We have adopted SimpleORM as our primary database integration library for a number of reasons:

SimpleORM acts as a direct conduit to the underlying JDBC data source. While there are internal caching mechanisms for performance reasons, there are no intermediate storage mechanisms visible to the user of the library. SimpleORM operations have the effect of acting directly on the underlying database.

SimpleORM lives up to its name in terms of providing well-defined functionality in a small and clear package. The simpleorm.jar weighs in at 112 KB and has no dependencies. The implementation easily supports the web site’s claims that it is a practical option for developers to step into the library code to diagnose and fix any issues they might have.

SimpleORM is easy to configure, requiring few lines of code and no XML.

SimpleORM doesn’t do much. This may sound like a strange thing to desire in an integration library, but in our case

Martin and Ted are senior technologists at Wingspan Technology. They can be reached at info@wingspantech.com.

we appreciated SimpleORM doing what was required in a direct manner and leaving the rest to us.

SimpleORM can be compiled under both Java and J# (.NET). We wouldn’t expect this feature to be of particular importance in most cases, but we were able to take advantage of it in our own development on one project.

To show how to use SimpleORM, we created a sample “Settings Storage” component. The source code for this component is available electronically (see “Resource Center,” page 4) and is part of the (upcoming) 2.19 release of SimpleORM. Our settings component uses a representation similar to a filesystem or the Windows Registry. Logically, the only type of object in the system is a folder. A folder can contain subfolders and properties. Properties are simple name/value pairs where the value can be any serializable object in Java. Infinitely deep folder hierarchies are supported, subject to database constraints.

Data Model Authority

One of the most important elements of an Object-Relational Mapping tool is the location and specification of the data model to be used. In any Object-Relational Mapping (ORM) system, there will be at least two models — one or more table schemas in the database, and one or more class definitions in the OO environment. There can potentially be more, especially if the definition of the data models and mappings is external to both the programming language and database. The best case for any ORM is when the datamodel authority is used to generate everything else. While this is easily achieved for new applications, it is often impossible when integrating with an existing system or code base.

In SimpleORM, the authority is built within the Java classes themselves. If desired, this mapping can be used to generate database tables or the mapping can be described in such a manner as to map to an already existing table. Users will immediately note how no effort is made to shield the user from database concepts.

This, in part, illustrates the intention of SimpleORM to act as a direct conduit between the caller and the database.

Listing One shows two simple SimpleORM objects. SimpleORM objects inherit from SRecordInstance and include an

SRecordMeta object. SRecordMeta objects are the heart of the SimpleORM system and contain the mappings used at runtime to present rows in database tables or queries as Java. By convention, the SRecordMeta is typically defined as a static member of the Java class it represents.

“SimpleORM acts as a direct conduit to the underlying JDBC data source”

This is not a strict requirement, but it does enable certain conveniences and there is no compelling reason to do otherwise.

Next we define the fields of the object (and columns of the corresponding table). Note that each field constructor takes the SRecordMeta as its first argument. The fields automatically register themselves with the SRecordMeta in their constructors, and are registered in the order they are defined in the source file. Order is generally not important, though all primary key columns must appear before all others. At least one primary key field is required for all objects as SimpleORM requires that all objects be uniquely identifiable by the system.

SRecordInstance is an abstract class with one abstract method getMeta( ). Given an SRecordMeta, it is trivial to determine the Java class associated with it. Using this method, it is trivial to perform the reverse operation — to determine which SRecordMeta is bound to an arbitrary SimpleORM object instance. No reflection is required.

Further examination of Listing One illustrates a handful of SimpleORM mechanisms. Each data type in the system is

34

Dr. Dobb’s Journal, December 2005

http://www.ddj.com

represented by a different SField* class. All major data types are supported by SimpleORM, and it is trivial to add additional user-defined SField definitions, though out of the scope of this article.

Various flags are available to modify the field mappings. The SFD_GENERATED_KEY mapping indicates that the database should be used to generated values for the specified field. This only works for Integer columns and its implementation is database dependent. SimpleORM does not currently support optimal implementations for many databases (PostgreSQL is one for which it supports this mechanism very well), but this facility is the target of significant work in the upcoming release. The SFD_MANDATORY flag is used to indicate that NULL values should not be accepted for this field. There are of course many other options available.

The SFieldReference field mapping deserves special mention, as it is used to represent foreign key references in the database. SimpleORM supports an elegant mechanism for traversing foreign key references.

Connection

Management & Transactions

Because of the various environments where SimpleORM might be used, creation of connections is deferred to the caller. This means that the caller is free to use any internal or external connection allocation or pooling mechanism. Such a mechanism might be provided as part of an application framework or J2EE server or can be trivially created.

Listing Two demonstrates the steps involved in initializing and terminating SimpleORM using the vanilla JDBC to create a database connection. Static methods on the SConnection class are used to bind the connection to the SimpleORM library, control a transaction, and release the connection. Internally, SimpleORM binds the JDBC connection to the current thread, meaning that all of the code elements in Listing Two must be called by the same thread. Any SimpleORM invocations made by that thread in the interior of the requested transaction is automatically routed to the appropriate connection.

Though it may not appear so at first glance, this mechanism is thread safe due to SimpleORM’s internal usage of Thread Local Storage mechanisms. In fact, because of those mechanisms, SimpleORM is able to defer almost all locking mechanisms to the database itself. Simultaneous threads executing within SimpleORM each have their own connections and transactions; they each execute within their own operating contexts.

Object Management

Database-mapped objects in SimpleORM are all handled through the SRecordInstance object interface. This interface can be extended via subclassing if desired, but it is entirely possible to utilize SimpleORM with nothing more than Listing One.

Object creation and loading is handled via a single method on SRecordMeta, findOrCreate( ). FindOrCreate takes as its parameter a single object or an object array representing the primary key value(s) of the object being requested. If the primary key is designated to be generated by the database, then new objects can be created with the createWithGeneratedKey( ) interface.

Listing Three demonstrates some common interactions we might perform on our example Folder object. We begin by loading a folder with an ID value of 5 and creating a second folder with a newly generated ID. We can either cast these objects to be our defined type Folder, or deal with them as generic SRecordInstance objects.

We then go on to request the name of folder1 and the parent of folder2. Note how we use specific, type-based get routines of the SRecordInstance class. These routines provide additional data validation both in ensuring that the SField instance is of the appropriate type and is valid in the current transaction and also that the value retrieved from the database adheres to the declared expectation. In the case of a foreign-key reference, the referenced object is automatically loaded and returned, or retrieved from the transaction cache if possible. For most primitive data types, we can retrieve the data any number of ways and SimpleORM will coerce the actual value into the requested type. We do this with the ID field, requesting it as three different data types.

It is also trivial to define and use methods such as getName( ) if that is preferred as a matter of style, but there is no need to do so. When data is requested from the database (such as during the object load), the attached connection is immediately used to retrieve the appropriate values. This data is then loaded into the SRe- cordInstance-derived object. Unless specifically directed otherwise, SimpleORM never reloads a data value during a single transaction, so repeated requests for the same data return the value cached in the SRecordInstance and do not touch the connection a second time.

For this reason, SRecordInstance objects are only valid within the context of the SimpleORM transaction that created them. This aspect initially caused us great discomfort and can, for some developers, force you to rethink how your application should interact with the database. It is the nature of many developers to think of the database

as a storage mechanism like a filesystem. Data objects are saved and loaded from the database and an ORM simply facilitates that process. SimpleORM takes a different approach, where you are either talking to the database or you are not. The data lives only in the database, and you use SimpleORM as a conduit to facilitate that communication. During our own education, we found that understanding both models leads to a much deeper understanding of ORM technology in general. SimpleORM is a great choice if you want your application to directly manipulate your database.

There are parallel set routines for all of the get routines with the same type signatures. Set operations manipulate the object cache immediately, and the database is updated en-masse when a transaction is committed or when SConnection.flush( ) is called. A unique object in the database is only ever loaded into the object cache for a transaction once. This means that if you load an object, navigate to it via foreignkey references, and retrieve it from a query’s result set, the exact same object instance will be returned each time.

Object deletion is accomplished by calling the deleteRecord method on any SRecordInstance object. This method can be overridden as in Listing Four so long as the base implementation is invoked. This would typically be done to prevent any foreign-key violations that might result from deleting an object.

It is always faster to delete or update several related records at once using a single SQL Delete statement than to fetch them into memory using JDBC and delete them one by one. SimpleORM supports this by enabling records for a specific table to be purged from memory, and so maintaining a consistent view of the data. (Most databases also support Cascade Delete options, but they can be very confusing for both Java and stored procedure code.)

Queries

Listing Five illustrates usage of the SimpleORM query API to iterate over all folder objects at the root of our hierarchy. The newQuery method on SRecordMeta creates a new query object designed to return objects of the type represented by SRecordMeta. There are a great number of operators available, including the isNull method we use here. There are also numerous type-specific gt (greater than), lt (less than), and eq (equals) methods. Conjunction operations (and and or) are available as well. The eq method can directly support references to other tables, and more general table joins are also supported, but our example is not well suited to demonstrate this advanced functionality.

Listing Six demonstrates another usage of the query API. In this case, our queries

http://www.ddj.com

Dr. Dobb’s Journal, December 2005

35

are designed to return exactly 0 or 1 records. Note how isNull, and, and eq are chained together mimicking the equivalent SQL. The SimpleORM query API is an extremely thin layer on top of SQL. This mechanism lends itself to rapid adoption by developers experienced with SQL queries.

It is also possible to manually specify the SQL fragment that forms part of the where clause, which is occasionally useful for less common or database specific queries. A further mechanism enables the entire SQL query to be specified with the cache being flushed manually if needed.

Rules Enforcement

SimpleORM supports rules enforcement via a series of overridable methods on the SRecordInstance class. By far, the most common use of this model is in the area of field validation. validateField is called each time a field value is set. validateRecord is called once immediately before an object is written to the database. A violation is signaled by throwing an SValidationException.

In practice, it is better to use validateField wherever possible, as this will cause an exception to be thrown at the exact location where the offense is committed. validateRecord is called only when SConnection.flush( ) is called, or when a transaction is committed. As a result, it may not be immediately obvious when the infraction occurred. Of course, in many cases, per-field validation is not sufficient. For example, the possible values for one field might be dependent on the setting of another field. To avoid chicken-and- egg type problems, it is better to simply validate the entire record at once rather than try to do it piecemeal as the fields are being set.

Listing Seven shows a simple validation routine applied to the Folder’s name field. Here we see one of the benefits of having our ORM metadata (SRecordMeta and SField* entries) defined as static members of the object implementation (SRecordInstance). With a simple reference equality check we can quickly determine which field is being modified and apply whatever logic we wish. Because we defined this field with the SFD_MANDATORY flag,

null values will not be permitted. That enforcement is only done when the object is written to the database at the end of a transaction, so we can add an additional check here to provide for a more timely error. In our full implementation of the example, we wish to be able to represent a folder hierarchy with a path string. Blank folder names and folder names containing our path delimiter character make parsing a path string more difficult, so we can simply disallow them here.

Conclusion

SimpleORM is a lightweight, yet powerful ORM implementation available under a liberal open-source license. SimpleORM makes little, if any, attempt to shield the user from database concepts, so developers with existing database knowledge and experience will have more success when using the library. The code base is similarly lightweight and easy to extend, though for most purposes, this will not be necessary.

DDJ

Listing One

public static class Folder extends SRecordInstance

{

static SRecordMeta s_meta = new SRecordMeta(Folder.class, "FOLDER");

static SFieldInteger ID = new SFieldInteger(s_meta,"ID",new SPropertyValue[] { SSimpleORMProperties.SFD_PRIMARY_KEY,

SSimpleORMProperties.SFD_GENERATED_KEY }); static SFieldReference PARENT = new SFieldReference(s_meta,s_meta,"PARENT"); static SFieldString NAME = new SFieldString(s_meta, "NAME", 64,

SSimpleORMProperties.SFD_MANDATORY); public SRecordMeta getMeta() { return s_meta; }

}

public static class Property extends SrecordInstance

{

static SRecordMeta s_meta = new SRecordMeta(Property.class, "PROPERTY"); static SFieldReference PARENT =

new SFieldReference(s_meta, Folder.s_meta, "PARENT", new SPropertyValue[] { SSimpleORMProperties.SFD_PRIMARY_KEY,

SSimpleORMProperties.SFD_MANDATORY });

static SFieldString NAME = new SFieldString(s_meta, "NAME", 64, new SpropertyValue[]

{SSimpleORMProperties.SFD_PRIMARY_KEY, SSimpleORMProperties.SFD_MANDATORY });

static SFieldObject VALUE = new SFieldObject(s_meta, "VALUE"); public SRecordMeta getMeta() { return s_meta; }

}

Listing Two

Connection conn = DriverManager.getConnection("jdbcURL", "user", "pass"); SConnection.attach(conn, "Connection Name");

SConnection.begin();

...

SConnection.commit();

SConnection.detachAndClose();

Listing Three

Folder folder1 = (Folder)Folder.s_meta.findOrCreate(new Integer(5)); Folder1.assertNotNewRow();

SRecordInstance folder2 = Folder.s_meta.createWithGeneratedKey(); String name = folder1.getString(Folder.NAME);

Folder parent = (Folder)folder2.getReference(Folder.PARENT);

Object idObject = folder2.getObject(Folder.ID);

String idString = folder2.getString(Folder.ID);

Int idInt = folder2.getInt(Folder.ID);

folder1.deleteRecord();

Listing Four

public static class Folder extends SRecordInstance

{

...

public void deleteRecord()

{

// Delete all subfolders

SResultSet subFolders = s_meta.newQuery().eq(PARENT, this).execute(); while (subFolders.hasNext())

subFolders.getRecord().deleteRecord();

//Delete all properties of this folder SResultSet props =

Property.s_meta.newQuery().eq(Property.PARENT, this).execute(); while (props .hasNext())

props.getRecord().deleteRecord();

//Invoke the base implementation

super.deleteRecord();

}

Listing Five

SQuery query = Folder.s_meta.newQuery().isNull(Folder.PARENT). ascending(Folder.NAME);

SResultSet rs = query.execute(); while (rs.hasNext)

{

Folder folder = (Folder)rs.getRecord();

...

}

Listing Six

SQuery query = Folder.s_meta.newQuery()

.isNull(Folder.PARENT)

.and()

.eq(Folder.NAME, "Test"); SResultSet rs = query.execute();

Folder folder = (Folder)rs.getOnlyRecord();

Listing Seven

public static class Folder extends SRecordInstance

{

...

public static final String DELIMETER = "/";

public void validateField(SFieldMeta field, Object newValue)

{

if (NAME == field)

{

if (null == newValue || "".equals(newValue) ||

(newValue.toString().indexOf(DELIMETER) >= 0))

{

throw new SValidationException("Folder name cannot be blank or contain " + "the DELIMETER character: " + DELIMETER);

}

}

}

}

DDJ

36

Dr. Dobb’s Journal, December 2005

http://www.ddj.com

A D V E R T I S E M E N T

JAVA BUSINESS INTEGRATION

Java™ Business Integration (JBI)

The Foundation for Integration and Service Oriented Architecture (SOA) Solutions

Highlights

JBI (JSR 208) was approved as a Java standard by the JCP on June 20, 2005.

This new standard delivers the foundation for Integration and SOA solutions. Specifically, it delivers two things:

-JBI Pluggable Architecture: an open, pluggable environment that enables the collaboration between integration technologies and Web services.

-JBI Service Assembly: the first standard description of a composite application. The JBI Service descriptor defines in a single document all the routing information and artifacts that make up an SOA “application.”

<

Integration has been a top IT spending priority worldwide for three years running. The

 

 

problem is that integration solutions adoption has been held back by cost, complexity,

 

vendor lockin and a lack of skills transferability. Proposed Web services standards and

 

SOA hold promise, but something more is needed.

The Better Way

JBI provides a new industry standard foundation for Integration and SOA. JBI Pluggable Architecture means that JBI platform vendors will be able to leverage open standards to build Integration and SOA standards faster.

It also means that they will be able to optimize their investment by leveraging the JBI partner community. Vendors will be able to use open standards to take advantage of new specialty service engines and protocol bindings from the JBI ecosystem.

By defining a standard composite application, the JBI Service Assembly will make it easier for IT developers to move SOA “applications” from

Java Specification Requests (JSR) are the actual descriptions of proposed and final specifications for the Java platform, as administered through the Java Community Process (JCP). The JCP holds the responsibility for the development of Java technology.

development to deployment. More importantly it will enable a whole new generation of composite application development tools that will increase developer productivity based on open standards.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Service

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Assembly

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

WSDL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BPEL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

xform

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Rules

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BPM

 

xform

 

 

Rules

 

 

 

B2B

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Engine Service Provider Interfaces

 

Management andTools

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Normalized Message Router

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Bindings

 

 

 

 

 

 

 

 

 

 

Legend

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SOAP

 

ebXML

 

 

JMS

 

 

 

AS2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

JSR 208

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

JBI Pluggable Architecture provides standard interfaces and normalized messaging for service engines and communications protocols. JBI Service Assembly defines a standard composite application.

A D V E R T I S E M E N T

Java Business Integration

http://java.sun.com/integration

System Developers

SOA Platform

Providers

Web Service and

Service Engine Providers

Tool Providers

Application

Providers

System Integrators

Customers

JBI enables a partner ecosystem for service engines, protocols, tools and services around the JBI standard.

The Business Benefits of JBI

For IT Managers -- IT managers who adopt solutions built on JBI will benefit from vendors who can be more responsive to their requirements through the use of open standards to build and extend their solution. More importantly, JBI provides investment protection. If a vendor cannot or will not provide a needed new functionality, it can be added by the customer directly or by leveraging the JBI partner ecosystem.

The result is a solution that grows with your business. Also, the developer productivity enabled by the JBI Service Assembly will mean higher quality, next-generation software applications delivered faster and for less total cost.

For JBI Platform Partners – JBI platform partners up-level their investment by adopting JBI. They can also leverage the partner community to source new technology or specialty technology to expand their solution offering.

For JBI Service Engine and Binding Partners –

JBI means that partners who port their service engine or communication protocol binding to JBI, will have it run on any JBI implementation. This means a vibrant market for JBI partners.

For Tools Vendors – The JBI Service Assembly provides the industry's first standard composite application description. This

will enable next-generation tools that allow Java professionals to collaborate with business analysts, data analysts, and other non-professional programmers to create SOA applications.

For Systems Integrators – SIs who build a solution around JBI, will know that they will be able to customize and extend that solution for their customers based on open standards. They can also leverage the JBI partner community to deliver complete customer solutions.

New Opportunity for SunSM Partner Advantage Program Members

Sun Microsystems has launched a new JBI Partner offering for Sun Partner Advantage Program members. This new offering is for software vendors and system integrators interested in the delivery of open and interoperable solutions designed to enable rapid and cost-effective development, deployment and management of Web services and SOA.

Partner Benefits

The offering will provide partners with access to technical, marketing and sales support for the development of standards-based SOA components, including:

Targeted JBI co-marketing, advertising and demand creation opportunities

Quarterly activities to improve visibility with Sun customers

JBI Initiative partner-only events

Technical assistance

Program Eligibility

To become a Sun Partner in the JBI Initiative, your company must already be a Sun Partner Advantage Program member. In addition, your company must have at least one product targeted for supporting JBI specifications,

or your company must first announce this intention and have product available within 12 months.

Learn More

For more information and to download the JBI Reference Implementation 1.0, please visit:

http://java.sun.com/integration

For more information about Sun’s JBI Initiative for Sun Partner Advantage Program members, please visit:

www.sun.com/partners/jbi

For information about the Sun Partner Advantage Program, please visit:

http://sun.com/partners/development

JBI Partners

Sun Microsystems, Inc. 4150 Network Circle, Santa Clara, CA 95054 USA Phone 1-650-960-1300 or 1-800-555-9SUN Web sun.com

SUN™ ©2005 Sun Microsystems, Inc. All rights reserved. Sun, Sun Microsystems, the Sun logo, Java and the Java Coffee Cup logo are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. For further information on Sun’s trademarks, please visit: http://www.sun.com/suntrademarks