9.10. Frequently Asked Questions

This document contains a list of the most frequently asked questions concerning the WAF persistence layer. For terminology and definitions, please see the Persistence Glossary.

1. General Questions

9.10.1.1. Why does the Red Hat WAF have a persistence layer? Why doesn't it just create the SQL when and where it is needed?

A persistence layer has been developed as part of the WAF for the purpose of encapsulating database access routines. This is desirable for at least two reasons: abstraction and database independence. By encompassing the SQL statements in Java objects, the database access routines are developed in a scalable environment. Also, the applications are isolated from the schema changes (which may occur in future versions) since the database access routines are encapsulated by a standard API. The second reason, database independece, is desirable because it means that the Java code can easily be ported from one database to another by changing a small set of centrally located files (as opposed to modifying every file that interacts with the database).

9.10.1.2. Why does the Red Hat WAF have a persistence layer instead of just using standard EJB? Why is Red Hat reinventing the wheel?

WAF is primarily a framework for building web applications, and Red Hat's reasoning for its current architectural direction is best summarized by Sun's own J2EE Blueprints at http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html.

In addition, there are numerous examples that one could concoct where an EJB server (at least initially) could be deemed to be an overkill given the problem being tackled. This is the sledge-hammer-to-crack-a-nut problem. In essence, the J2EE specification does not mandate a 2, 3, or multitier application model, nor realistically could it do so. The point is that it is important to use appropriate tools for a given problem space.

Given that the most common usage for WAF deployment is, to use Sun's term, the "Web-centric application scenario", Red Hat opted for the "3-tier Web-centric" architecture, in which "[t]he Web container is essentially hosting both presentation and business logic, and it is assumed that JDBC (and connectors in the future) will be used to access the EIS resources."

Coarse-grained interoperation of WAF with other systems would certainly be a sound application of EJB or JMS. However, Red Hat does not currently have clear requirements in this area. More activity is anticipated in this area during future WAF implementations, initiated both by Red Hat and by others building on top of WAF.

(As an aside, commercial J2EE containers such as WebLogic and Oracle's 9i typically make it a goal to provide a "simple mechanism for balancing load across several machines without too much thinking," even when you are only using the Servlet API, as opposed to EJBs.)

Regarding the other reasons for using EJBs outside of distributed objects, the following points should clarify the extent to which Red Hat is "reinventing the wheel":

  1. Persistence: There are two standard types of persistence: Bean-Managed Persistence (BMP) and Container-Managed Persistnce (CMP). BMP does not provide much advantage, and CMP, unfortunately, does not provide automatic SQL generation. In short, the WAF persistence layer is designed to address a different set of requirements than CMP.

  2. Connection pooling: Red Hat is not reinventing connection pooling; rather, its code is writtten against the J2EE-standard API for connection pooling, the JDBC 2.0 Optional Package at http://java.sun.com/products/jdbc/articles/package2.html.

  3. Transaction monitoring: EJB's declarative-style transaction management is one of its more attractive features, but not compelling enough for Red Hat to make it the deciding factor in using EJBs. Red Hat does not "reinvent" transaction management, either; it simply leaves the applications responsible for managing transactions.

Red Hat has found, through input from prospective customers, partners, and analysts, that the market perspective on the necessity and appeal of the "full J2EE architecture" is not clear-cut. Being able to run WAF atop a J2EE web container alone has benefits of its own: simplicity of development and deployment, low run-time overhead, and broad availability of stable open-source/affordable implementations. In other words, 3-tier Web-centric is quite defensibly the appropriate tool for Red Hat's primary problem space.

9.10.1.3. What are the primary benefits/goals of the persistence API?

The persistence API provides the following benefits:

  • It provides an abstraction for physical data models for greater database independence.

  • It isolates the java classes from data model changes (e.g., if a column is renamed, no Java code must be changed).

  • It provides support for user-defined object types.

  • It decreases the amount of time it takes to implement a new object type.

  • It can automatically generate SQL statements that can be run on different databases. Thus, it is possible to write one PDL file that can be used on N databases. Without persistence, a developer may be required to write N different DDL scripts and then N different sets of SQL statements to use the tables.

2. Developer Specific Questions

9.10.2.1. I am new to the persistence API. Where do I start?

Start with this book's table of contents which lists all available persistence documents. In addition, the Section 10.2 Domain Objects Tutorial introduces the API used by the WAF and provides you with code samples for implementing your own Domain Objects.

Additionally, you may want to consult the document on Section 9.2 Beginning With Data Objects, as well as material on Section 9.5 Filtering, Ordering, and Binding Parameters.

9.10.2.2. If I am creating an object type what kind of metadata do I need to define?

The best place to find a full explaination and examples is in the Section 9.2.3 Defining an Object Type in PDL. However, as a short summary, you must specify the following:

  • For each attribute:

    • A mapping of the attribute to the database column in which it resides.

    • The SQL type of the attribute. For instance, if the data type of the attribute is a string, the SQL type might be VARCHAR(20) or VARCHAR(4000) or even CLOB.

  • For each association, it is important to specify the Join Path that the persistence layer will know how to join the SQL tables to each other.

  • For each data query, a mapping from the returned attribute name to the data type of the attribute is required. This allows the code whether an INTEGER SQL type should be an INTEGER data type or actually a BigDecimal data type.

All of the metadata is required so that the persistence layer can successfully generate a data model as well as all of the events to manipuate the object.

9.10.2.3. Where can I find example PDL (Persistence Definition Language) files that define object types?

At the top level of any project you will find a pdl directory. In there, you will find many PDL files that should provide plenty of examples.

9.10.2.4. Where do I put my PDL files?

PDL files should be kept in a directory at the same level as the SQL and src files. If the PDL does not contain any database specific queries (e.g., something containing a connect by statement) then it should end in (.pdl). Otherwise, you should create a copy of the query for each database and name each copy appropriately: .ora.pdl for Oracle or .pg.pdl for PostgreSQL.

9.10.2.5. I want to create a new object type that extends ACSObject. What do I do?

When you extend an object type, the new object type will inherit all the properties (attributes and role references) of the super type. A child object type can override the parent's attributes (but not role references), as long as the new attribute does not violate any constraints placed on the supertype's attribute. For example, suppose that the parties object type defines an email attribute that is required. That is, parties.email has multiplicity of 1..1. You can create a persons object type that extends parties. You cannot make the email address optional in the persons object type because it is already required by the supertype.

Suppose you are defining an object type named "ChewingGum" that extends ACSObject. To specify that an object type extends another, do the following:

  • Before defining your object type but after declaring your model, make sure to import the ACSObject type.

    import com.arsdigita.kernel.*;
  • When defining your object type, specify that the supertype of the "Chewing Gum" object type is "ACSObject" and import the namespace for ACSObject. In the PDL file, you currently write:

    object type ChewingGum extends ACSObject {
        ---
    }    
  • When you define your table in the database, make the primary key reference the acs_objects table. This is only required, however, when you manually specify your SQL. In most cases, defining the PDL will be enough to get your started (you must, however, still load the auto-generated table). The following shows how to manually define the table.

    create table acs_objects (
           object_id         integer primary key
    );
     create table chewing_gum (
           gum_id            integer primary key
                             references acs_objects(object_id)
    );
  • When you create your Java class, you can extend the ACSObject class in the com.arsdigita.kernel package.

    See Section 9.2.6 Object Type Inheritance for a longer example.

9.10.2.6. What is a DataQuery and how do I use it?

A DataQuery encapsulates SQL queries. You can retrieve a DataQuery by name from the PDL file, set filters on its attributes, execute the event, iterate over the result set, and access the attribute values.

Underneath, the actual SQL query and data model used are hidden from application code.

For more information, see the documentation on Section 9.4 Named SQL Events.

9.10.2.7. How do I specify a custom SQL query?

You can create a DataQuery by specifying a SQL query and as many attribute/column mappings as you like. You can then define bind variables or set filters on that query to gain the final query you wish.

9.10.2.8. What is an Object/Reference Key and why do I need one?

The object key (Object Key) declared in the PDL files is used by the persistence system to uniquely identify a particular object in the database. For instance, the object key for acs_object is "id", which is the name of the attribute that maps to the object_id column in the acs_objects table.

Object keys can contain singule values or they can be compound (they can be specified using 1 to N attributes). For more information, see Section 9.2.3.3 Object Key.

9.10.2.9. How do I retrieve a DataQuery, DataCollection, or DataAssociation in the order I want (e.g., there is a sort_order link attribute that I want to sort by)?

To do this, use the setOrder() method. For further information, see Section 9.5.2 Ordering.

9.10.2.10. What is the OID class used for?

The OID class encapsulates the details of the primary key of an object. You use instances of OID for retrieving an object from the database and you set the OID to a known value. You can also get an OID object from a DataObject.

9.10.2.11. I would like to retrieve all instances of a particular object type (e.g., com.arsdigita.kernel.User). How do I do this?

There is a method on com.arsdigita.persistence.Session (http://rhea.redhat.com/doc/waf/6.0/api/com/arsdigita/persistence/Session.html) that retrieves a DataCollection for all instances of a data object type. The DataCollection can be filtered to narrow the results. It is possible to request individual properties of the collection without instantiating an object. You can also instantiate the data object, and if you use the DomainObjectFactory, you can get a DomainObject from the data object. It is recommended, however, that you do not instantiate the object unless absolutely necessary, because creating the object consumes extra memory resources as compared to iterating through the DomainCollection.

As a specific example, to get all users in the system, you can execute the following line:

DataCollection authors =
SessionManager.getSession().retrieve("com.arsdigita.kernel.User");

For more information, see Section 9.2.5 Creating and Retrieving Objects in Java.