The Library program prototypes a simple system for a small town library. Figure 2-3 in Chapter 2 shows the UML model for application data classes used by the Library program. There are four application data classes in Figure 2-3: Book, Category, Borrower, and Volunteer. Using the Library program, you can manipulate the persistent objects, define queries, and view the results.
The Library program is built from six primary source files, the four application data classes, the Library class and its command classes, and the LibraryHandler data service. These are contained in the com.ysoft.jdo.book.library and com.ysoft.jdo.book.library.client packages. All of the explicit use of JDO occurs within the LibraryHandler data service. The console interface handles all exceptions thrown by the implicit use of JDO. The persistence manager factory is configured by a properties file that varies by the JDO implementation. For the reference implementation, the file is named jdori.properties and is found in the com/ysoft/jdo/book/library directory. The build copies it to the build/com/ysoft/jdo/book/library directory and renames it to factory.properties.
To build the program, go to the bookants directory and type ant library. This build is part of the ant learning-programs build. Listing 8-6 shows some of the expected output from the build when using the reference implementation.
Listing 8-6: Expected Output from Running ant library
library: [javac] Compiling 7 source files to E:\Bookcode\build [echo] returned from com/ysoft/jdo/book/library/build.xml [copy] Copying 1 file to E:\Bookcode\enhanced\com\ysoft\jdo\book [copy] Copying 1 file to E:\Bookcode\build\com\ysoft\jdo\book\library [echo] Enhancing the persistent classes of library [java] done. [echo] creating runLibrary.bat BUILD SUCCESSFUL
To run the Library program, go to the bookcode-home directory and type runLibrary.
When you enter the help command after starting the Library program, you see all the commands that it supports. The expected output is shown in Listing 8-7.
Listing 8-7: Example of Help Output from the Library Program
E:\Bookcode>runlibrary enter command: --> help commands: quit begin commit rollback get pm config view attributes define query variable find all find find in results add data object delete data object view volunteer view borrower view book view category borrow book return book modify volunteer modify book populate database clear database enter command: -->
As Listing 8-7 shows, the program recognizes a large number of commands. Begin by populating the database. This will add seven books, six categories, three borrowers, and one volunteer to the datastore. The properties file sets to false all the Boolean properties of the PersistenceManager and Transaction. This can be verified by executing the get pm config command.
The three commands begin, commit, and rollback allow you to control the transactional boundaries. When you execute a begin command, a JDO datastore transaction starts. If you then execute a series of find all commands, you can view the summary information for all objects in the datastore. There is an apparent bug in the 1.0 reference implementation that prevents the capture of the identity string on the first transaction for an object. Finding all objects and executing a commit works around this bug. If you find all the objects again, you will see the value of the identity strings in the output.
A large number of commands allows you to manipulate the state of the persistent objects. The add, delete, view, borrow, return, and modify commands allow you to add books, categories, borrowers, and volunteers; delete the same; view all the information about them; borrow books; return them; and modify information about books and volunteers. The view commands track the last list of objects that were presented. As a result, if you view a category and it lists two books, then the view book command will present that list of two books to choose from.
The Library program's primary purpose is to exercise the JDO query language. The find command will query against the extent, while the find in results command will query against the last collection of query results. The view attributes command shows the names and types of all the attributes of the application data classes. Finally, the define query variable command allows you to define a query variable for use in navigating collections within JDOQL. The effect of defining a query variable lasts only until the next query is executed. Consequently, the query variable must be defined before executing the query that will use it.
As an example, suppose that you want to know what categories of books interest Tom. Listing 8-8 shows the user interactions to find that information.
Listing 8-8: User Commands to Find All the Categories That Interest Tom
enter command: --> begin Okay enter command: --> define variable Enter query variable declaration: --> Book b Okay enter command: --> find Find what type of objects: 1. Book 2. Borrower 3. Volunteer 4. Category Enter selection: --> 4 Enter query string: --> books.contains(b) && b.borrower.name == "Tom" Find Class: com.ysoft.jdo.book.library.Category Filter: books.contains(b) && b.borrower.name == "Tom" Variables: Book b Found 2 objects in the results category [OID: 103-12] "Sportsman" category [OID: 103-11] "Outdoors" enter command: -->
Although there are only four application data classes, the object model of the library supports a variety of queries. For example, to find the books that have been borrowed by a volunteer, use the Book extent and the following query string:
borrower.volunteer != null
Your answer for the default object population should be as follows:
Found 2 objects in the results book [OID: 102-13] "Gone Sailing" checked out: Mon Aug 26 08:23:10 EDT 2002 book [OID: 102-12] "Gone Hunting" checked out: Mon Aug 26 08:23:10 EDT 2002
The OID values may very well be different in your datastore, and the date when the books were checked out will certainly be different.
To find all the books that are in categories that interest Harry, use the Book extent, and define the query variables as shown here:
Book b; Category c;
Then use the following query string:
categories.contains(c) && (c.books.contains(b) && b.borrower.name == "Harry")
Your answer for the default object population should be as follows:
Found 1 objects in the results book [OID: 102-16] "Gone to Work" checked out: Mon Aug 26 08:23:10 EDT 2002
Now that you have the general idea, perhaps you are ready for a challenge. Can you find all the categories that have books borrowed by more than one borrower? Hint: output similar to the following is expected from the query when it runs on the default population.
Found 2 objects in the results category [OID: 103-12] "Sportsman" category [OID: 103-11] "Outdoors"