What Is "Generic Persistent Class"

Each kind of persistent objects (such as "Artist" or "Painting") is described in Cayenne by a single ObjEntity. The most common and useful scenario is mapping an ObjEntity to a "specialized" Java class, one class per entity. But there is an alternative - use a single generic persistent object class to map any entity.

Generic DataObject hints
  • Accessing generic objects is done via DataObject API (see examples below).
  • The simplest "generic" object class for all practical purposes is CayenneDataObject.
  • "Concrete" DataObject classes are by definition "generic" as they implement DataObject.
When to Use Generic Objects
Generic objects are not type-safe and are not convenient for manual coding. Most applications should stick to concrete classes. Generic objects are useful in cases when all persistent properties are not fully known at compile time. It is also possible to use a hybrid approach when new persistent properties are added to the existing concrete class at runtime.

Mapping in CayenneModeler

If you don't enter anything for Java Class of an ObjEntity, Cayenne assumes generic mapping and uses the following implicit rules to determine a class of a generic object. First it will check whether a DataMap "Custom Superclass" is set. If so, runtime uses this class to instantiate new objects. If not, org.apache.cayenne.CayenneDataObject is used.

CayenneModeler and Ant class generators skip ObjEntities that are mapped to CayenneDataObject explicitly or have no class mapping (i.e. implicitly mapped to a generic class).

How to Use Generic DataObjects

One difference between using a specific class per entity and using generic objects is that a String entity name is used in places where you would normally use a Java class. The examples below demonstrate this:

Create a new object:

DataContext context = ...;
DataObject author = (DataObject) context.newObject("Person");

Create a new object when using a generic ObjectContext that does not define "newObject(String)" method. In this case we must set ObjectId explicitly - this will tell Cayenne what entity the generic object belongs to:

ObjectContext context = ...;
DataObject author = new CayenneDataObject();
author.setObjectId(new ObjectId("Person"));

Build SelectQuery:

Expression e = ExpressionFactory.likeIgnoreCaseExp("subject", "%first%");
SelectQuery q = new SelectQuery("Message", e);

Use DataObject API to read/write the values instead or getters/setters.

Determine object entity name:

DataObject object = ...;
String entityName = object.getObjectId().getEntityName();

Read a simple property value (attribute or relationship):

String subject = (String) object.readProperty("subject");

Read a "nested" property value spanning a chain of DataObjects:

String name = (String) object.readNestedProperty("author.lastName");

Modify a property value:

object.writeProperty("subject", "Post On Topic");