Class | Description |
---|---|
ERXPartial<T extends ERXGenericRecord> |
For overview information on partials, read the
package.html in
er.extensions.partials . |
ERXPartialGenericRecord |
For overview information on partials, read the
package.html in
er.extensions.partials . |
ERXPartialInitializer |
For overview information on partials, read the
package.html in
er.extensions.partials . |
A review of the features of the ERXPartial
implementation has been partly funded by Logic Squad. The review documents
and an example/test application are available in
Examples/ERXPartials
directory. While not everything
discussed in the review document has been completed, the example
application is designed to provide a starting point for validating
the features and functions of this package.
-- David Aspinall, Global Village Consulting
Sept 19, 2012
Partial Entities provide a mechanism for defining a single entity whose definition spans across multiple EOModels along with tools to interact with these partial definitions in a type-safe way.
Partial entities are specifically designed to allow reuse and
extension of existing models. A very common case where this becomes
useful is that of a Person
entity. Person
is an entity that is used in many different scenarios, each of which
requires additional attributes and relationships. For instance, you
may have a task management application and you want to embed your
calendaring framework into it. The common Person
base
entity may have username
and password
attributes, the tasking application may add an
activeTasks
relationship, and the calendaring framework
may add a scheduledEvents
relationship as well as
dayStartTime
and dayEndTime
attributes.
Note that partials are not designed to address the issue of "roles,"
where different people in the system may have different roles at
different times. Partial entities are designed to address the issue
of combining high level modules together to form more complex static
entity declarations.
Traditionally, this presents some complicated problems. How do you
add these relationships onto your Person
entity?
Additionally, these different views of a Person
technically represent the same underlying object, and ideally we
want to be able to fetch these views without having to always
traverse additional relationships for every view type. For
instance, if CalendarPerson
is a "role" of
Person
and stored as a separate entity (table) in the
database, it would require traversing a relationship every time you
need to reference calendar-specific attributes of a
Person
, which can be a serious performance issue if you
need to show CalendarPerson
attributes of multiple
Person
objects at time. Prefetching can alleviate some
of this overhead, but semantically speaking, Person
,
CalendarPerson
, and TaskPerson
are the same entity, just as seen from different parts of
your system. EOGenericRecord
allows a certain amount of
flexibility, but in exchange for a loss of type-safety. Java's type
system does not allow mixins like Ruby, which means that you cannot
just "inject" the get/set methods into the Person
class
and actually be able to get compile-type checking on these methods.
Enter Partial Entities. With partial entities, you can declare a
base Person
entity, a TaskPerson
partial
entity that adds the activeTasks
relationship, and a
CalendarPerson
partial entity that adds the
scheduledEvents
relationship and the
dayStartTime
and dayEndTime
attributes.
At runtime, the base and partial entities are merged together such
that the entity named Person
is the union of all of the
attributes and relationships of all of its partials. In fact, the
"partial entities" are not entities at all, but rather exist only as
special cover classes that are generated by VelocityEOGenerator to
allow compile-time type checking and get/set method access. The
ability of partials to merge into a single entity means that the
underlying representation in the database is such that a single row
represents the union of all of the attributes of Person
+ CalendarPerson
+ TaskPerson
. The makes
for fairly optimal fetching behavior.
The design of the Java side of partial entities is inspired by the
concept of an IAdaptable
in Eclipse. Eclipse has
similar problem design constraints in that it provides extension
mechanisms for many types of classes in the system. Because Java
lacks the concept of categories or mixins, there is no formal
mechanism to extend an existing class with a compile-type-checked
interface. IAdaptable
in Eclipse provides a very
simple interface to ask a class to adapt itself into another
interface, which can consult a factory to perform that lookup. A
common example of this is that you may ask a Project
for its JavaProject
interface, which provides
additional capabilities that are specific to a
JavaProject
. In Partial Entities, we use the
capabilities of EOF to provide the underlying flexible datastore for
the entity state (that is, EOF doesn't care if we tell it that
Person
now has 3 new relationships and 4 new attributes
as long as the underlying database definition agrees when it comes
time to fetch or save). We use the IAdaptable
concept
to address type-safe Java interfaces to this state. Velocity
EOGenerator has been extended to support the generation of special
"partial entity" templates. These templates are very similar to
normal EO templates except that where normal EO's extend
EOGenericRecord
(or
ERXPartialGenericRecord
in the case of partial base
entities), the partials themselves extend ERXPartial
,
which is not a subclass of EOEnterpriseObject
.
It is an important concept that the partials are only a
type-safe wrapper around a single EO. Person
is the
only EO in the above example. CalendarPerson
and
TaskPerson
are simply type-safe "views" of
Person
.
In code there are two ways to access the partial covers for an EO. In Java, you will want to use the programmatic interface, and in components you will want to use the binding method. The two styles look like:
Person person = ...; // fetch a Person EO from the database
CalendarPerson calendarPerson = person.partialForClass(CalendarPerson.class);
calendarPerson.setDayStartTime(new NSTimestamp());
System.out.println(calendarPerson.dayStartTime());
TaskPerson taskPerson = person.partialForClass(TaskPerson.class);
System.out.println(taskPerson.activeTasks());
or
DayStartTime : WOString {
value = person.@CalendarPerson.dayStartTime;
dateformat = "%m/%d/%y";
}
So how do you take advantage of partials? There are several pieces. Entities in your models need to be annotated as partials, the runtime needs to be notified of them, and Java code needs to be generated to take care of the tedium of repetitive implementation. Partial entities require the use of Project Wonder, the latest WOLips/Entity Modeler, and the latest Velocity EOGenerator (which is built into WOLips).
In Entity Modeler, there is a new option under the Advanced tab of
an Entity that allows the selection of a "Partial Entity". The
Partial Entity is the base entity that a particular entity will
augment. For instance CalendarPerson
's Partial Entity
is Person
. Currently, a partial cannot augment another
partial. For instance, TaskPerson
cannot be a partial
entity for CalendarPerson
, rather they can only be a
partial of the base Person
entity.
Any attribute of the base entity can be duplicated in the partials if it makes creating relationships easier—these will just be skipped when the entities are merged at runtime. For instance, it may be easier to setup your partial's relationships if you duplicate the PK's into the partial entities.
Lastly, in your model, you should specify the class name of each
partial entity. For instance, CalendarPerson
may be
com.mdimension.calendar.model.CalendarPerson
, whereas
TaskPerson
might be
com.mdimension.tasking.model.TaskPerson
. "External
name" on partial entities is currently ignored, but should be set to
be the same as the base entity (for instance,
CalendarPerson
's external name should be
Person
, to imply that the CalendarPerson
entity will be in the Person
table).
Currently, only the Velocity EOGenerator in WOLips is able to
generate code for partial entities. In the Defines section of your
model's EOGenerator configuration, you should specify that your
EOGenericRecord
class should be
er.extensions.partials.ERXPartialGenericRecord
(rather
than er.extensions.eof.ERXGenericRecord
like you
normally would). The Velocity templates will automatically make the
superclass of partial classes
er.extensions.partials.ERXPartial
, but the
ERXPartial partialForClass(Class)
method is only on
ERXPartialGenericRecord
.
SQL Generation with partials is a complicated subject, but ideally
you should use the ERMigrations APIs to create your tables. With
migrations, you can have your base framework create the generic
Person
table, the Calendar framework can add the
appropriate columns for CalendarPerson
, and the Tasking
application can add the appropriate columns for tasking. If you
generate SQL directly out of Entity Modeler, you will likely get
multiple definitions for the same table names, which would need to
be manually merged.
To enable the use of partials at runtime, you should set the property:
er.extensions.partials.enabled=true
This will trigger the runtime system to do entity merging for you. To use the partials at runtime, refer back to the Examples section above.
Copyright © 2002 – 2024 Project Wonder.