ejb3 templates

Domain model description options

  • (multiple) inheritance
  • declarative constraints
  • programmed constraints
  • int, double, string, blob, clob fields
  • fields with multiple values
  • hard linked records (normal db links)
  • field values can be limited (choices) either statically (xml description) or programmatically (validation)
  • for removing records cascade-delete, cascade-keep (don't delete when there are references to the record) or normal null all references behaviour can be used
  • field (type) templates can be used

Implementation

  • powerful proxies are used which make objects available remotely. Similar to detached objects, but completely transparent (no need to instantiate)
  • lazy collection instantiation, when a selector contains lots of objects, the results are still batched (and counting works too)
  • inheritance sensitive selectors

As a bonus, when your client only uses the proxies, it is straightforward to replace the backend (this way the data could for example be based on webservices instead of EJB3 entity beans).

Using the generated proxies

As part of the generated classes, some powerful data access objects (DAO's) are generated. These proxies assure that access to the objects always behaves consistently, independent of whether access is local, remote, using a UserTransaction or not.
The proxies have the following advantages and behaviour.

  • All multiple linked fields are lazy loaded. They are instaniated when the collection is first accessed.
  • Proxies can be saved and updated (using equandaUpdate() in place, the actual object stays the same though the data in it may change. However, linked objects which are updated because of casading update are replaced (see example velow)!
  • All multiple fields in the proxies are always not-null. When there are no values in it, an empty collection is returned.
  • All updates to proxies cascade. The linked records will also be updated when they are changed (though this only follows a path of changed objects, when object A links to object B which links to C and A and C are modified but B is not, then an update on A will not update C).
  • Multiple fields in proxies are intelligent. They record which elements are removed or updated and will not touch unmodified linked objects. If however, you assign a new collection to these fields, then part of the intelligence is no longer possible, making updates somewhat less efficient. Therefor it is recommend to not assign new collections.
  • Proxies are only marked as modified if something has actually changed. Just assigning the current value in a field will not mark that field as modified.
  • The "equandaCreate()" static method allows instantiation of a new object with all default values filled in.
  • All selectors are available through static method on the proxy class. The results are lazy collections which are instantiated and refreshed automatically.
  • The proxies allow direct invocation of the actions which are defined on the objects (in the domain model). You can choose whether the system should implicitly do a equandaUpdate() before calling the action (using "setEquandaUpdate()", default is false).
  • You can choose whether to check for changes to fields which are not part of the object according to the inheritance (templates) in the domain model. Using "equandaUpdate( false )" you can assure the updates are only attempted on fields which are part of the object based on the equandaType. This can for example be very practical for interacting with user interfaces.
  • To test whether a proxy already exists in the database, check whether "getId()" is not-null.
  • To check whether a proxy is modified locally, you can use "isModified()". This accepts a parameter (similar to "equandaUpdate()") to limit the test to equandaType specific fields.

Some things to watch out for. In code like the following (excerpt from the testcases).


Car car = Car.equandaCreate();
car.setBrand( "CarBrand 1" );
UniqueFields uf = UniqueFields.equandaCreate();
uf.setUniqueImmutableInt( 1 );
uf.setLinkedCar( car );
uf.equandaUpdate();

The "equandaUpdate()" will create both the "UniqueFields" and the "Car" records (because of cascading updates).
However, the update will replace the "Car" instance inside the "UniqueFields" object and not do an explicit update on the "car" object. Therefor "car.getId()" will still return null and "car.isModified()" will be true. To get the saved version, use "car = uf.getLinkedCar()".

How the domain model is generated

A whole set of classes are being generated. To handle the inheritance, the inheritance tree is flattened and a "root-table" is created which contains all fields, actions, selectors for all the tables in the inhertance tree. When is distinction needs to be made, this is handled in the code.

The preferred way to access the data is through the proxies. These have names which equal the names of the root tables and allow remote access to the data, the selectors and actions while hiding the details of looking up beans etc. They are built to transparently instantiate fields when required.

Alternatively, you can access the data though a set of session beans for each root table. One is the /ObjectSelectorBean/ which is stateless and allows executing the selectors which are defined. The second is the /ObjectBean/ which is a stateful session bean façade for the entity bean. To make accessing these beans slightly easier, there is a /EquandaGlobal/ class which (amongst other things) allows you to retrieve the beans.

For constraint checking a smart set of intermediate classes called "mediators" are used. These are built in such a way to assure the declarative constraints can be handled and updated when the generation is re-run, but constraints added by manual coding are also maintained. For this a /MediatorRoot/ is used to start by constraining all access (nothing allowed). Then for each class there is a /MediatorBase/ which contains the declarative constraints and a /Mediator/ which is supposed to be filled in by the used and contains the programmatic constraints.

This results in the following classes. The classes you are most likely to use/need are indicated in bold. Where a name contains "Object" this should be replaced with the table name.

Global

  • LazyEJBList.java : lazy list with beans, generated to allow package private access to some methods.
  • LazyProxyList.java : lazy list with proxies, generated to allow package private access to some methods.
  • ObjectUpdater.java : used internally for updating the proxies.
  • EquandaGlobal.java : utility class which allows getting instance of the beans, and some extra utility methods including access to the base selector EJBQL qeries.

For each root table

  • ObjectSelectorBean.java : stateless session bean, allows access to the selectors
  • ObjectBean.java : stateful session bean, providing access to the entity bean with all the constraints checking through the mediators
  • DMNameBean.java : ejb3 entity bean
  • ObjectSelectorEJB.java : selector bean interface
  • ObjectEJB.java : (remote) interface for the ObjectBean
  • ObjectEJBLocal.java : local interface for the ObjectBean
  • Object : the proxy
  • ObjectState.java : the proxy state, use internally in the proxy to allow updating without replacing the actual proxy
  • ObjectMediatorRoot : root of the mediator object tree,
  • ObjectConstants : contains the constants like type identifiers and possibe values for choice fields
  • ObjectSelectorHelperBase : base for the ObjectSelectorHelper
  • ObjectSelectorHelper : allows code to be inserted for the builders.
  • DMObjectSelector : class for handling selections at the entity bean level
  • ObjectBase : shared interface between ObjectEJB and ObjectEJBLocal

For each table

  • ObjectMediatorBase : contains the programmatic constraints
  • ObjectMediator : empty file, should be used for programmatic constraints and defining the actions
  • ObjectType : type handling and testing
  • ObjectTypeChoice : validator to possible types

For each multiple field

  • DMObject_FieldBean : entity bean which represents the multiple field values

They are generated in three different packages, of which you define the prefix using the following settings in "dm.ini".


[ejb3]
;package=org.equanda.test.dm

[extra]
ejb-package=org.equanda.test.dm

Note that the "package" setting is commented. WHen both settings are specified they should be equals or you will get compilation problems. However the ejb3 package setting defaults to the "ejb-package" setting in the "extra" section.

  • /prefix/.client : contains all the classes which may be required for remote access to the data.
  • /prefix/.server : contains the classes which should only be available inside the application server. The client does not need access to these.
  • /prefix/.server/dm : separate package for the ejb3 entity beans (which are server-side only).

Customizing the generated domain model

...

Generated database structure

All table and field names have prefixes.

Tables names :

  • T_x : normal table; details can be found in table "x" in the domain model.
  • M_x_y : multiple field, definition can be found in the table "x", the field "y".
  • L_x_y : link table, for the link defined in table "x", the field "y". Table "x" is the owner side of the link.

All fields which are defined in the domain model have "F_" as prefix.

The following fields are additionally defined :

  • For normal tables :
    • EQUANDA_UOID Char(14) NOT NULL : a db-wide unique key which is use to link records together, has to be 14 characters in length
    • EQUANDA_TYPE Char(4) : type indicator. In the portfolio product this is always ' ' (four spaces). For tables with have templates (inheritance) this makes a distinction between the instance used. The value is defined in the table definition as the "type" attribute.
    • EQUANDA_MODIFIED Timestamp : timestamp of mast modification
    • EQUANDA_VERSION Integer default 0 NOT NULL : record version number, incremented on each change, used for optimistic locking
    • EQUANDA_STATUS Char(1) : status flag for the record. This is usable for bulk/batch export. On each change in the record, the field is set to "M".
    • EQUANDA_CREATION Timestamp : timestamp of record creation
  • For tables representing multiple fields :
    • EQUANDA_UOID Char(14) NOT NULL : db-wide unique key for this record, has to be 14 characters in length
    • EQUANDA_CREATION Timestamp : timestamp of record creation
    • EQUANDA_PARENT_UOID Char(14) : key for the record this value belongs to.
    • F_y : actual field value, "y" if the field name (as from "M_x_y")
  • For link tables :
  • x_UOID Char(14) NOT NULL : "x" is the table name (as from "L_x_y"), key for the record on the owner side of the link, in table "x"
  • F_y_UOID Char(14) NOT NULL : key for the other side of the link, for field "y"
  • 1. ejb3 templates
  • 1.1. Using the generated proxies
  • 1.2. How the domain model is generated
  • 1.3. Customizing the generated domain model
  • 1.4. Generated database structure