Anchor | ||||
---|---|---|---|---|
|
In This
...
Topic
Table of Contents | ||
---|---|---|
|
...
The Entity Manager is a service that manages entities. An entity is an object like a Book, Person or Car. Entities, or domain objects, are defined and registered by a component within a plugin. Entities are implemented as a Java class with particular entity annotations and always defines an implementation class and an interface. Both the interface and implementation are registered by the Entity Manager. The Entity Manager supports all CRUD actions on the entity. So it can Create, Retrieve, Update and Delete entities.
The Entity Manager is a required guideline, see /wiki/spaces/PD/pages/24721296 of the guidelines (see G125).
Implementing an Entity
As stated above, the implementation of an entity consists of two Java classes: the entity implementation and the interface. The interface contains the public methods that may be invoked by any external class, even Java classes from another plugin. The interface must extend the Identifiable interface, which holds getters and setters for the WmId
. The WmId
is the unique identifier for the entity inside the Entity Manager’s registry.
...
The entity implementation implements the entity interface described above. To identify that this Java class contains the definition of an Entity, four specific Entity annotations must be added to the header of the class.
takes no arguments.@Entity
: First, the@Entity
annotation must be added to the class header to identify the class as definition of an entity. The annotationcan take one argument:
websiteSpecific
. To make@Entity
website specific, the syntax is:Code Block theme Eclipse @Entity(websiteSpecific=true)
@Interfaces
: Secondly, the interface that is associated with this entity must be defined using the@Interfaces
annotation. It takes the full classname (including package) of the associated interface as argument.@Namespace
: Thirdly, the namespace for this entity must be defined for this entity using the@Namespace
annotation. The annotation takes two arguments, the prefix and the URI.Note The plugin defines one namespace and URI for all classes contained by the plugin, including the entity. Usually the namespace and URI are defined in the Activator of the plugin or a Java class containing all constants, like IDs, namespace and URI.
@Nodetype
: Finally, when the Entity will be stored inside the JCR, the@NodeType
annotation should be used to define the node type,super
type andmixin
node types of the node that represents the entity in the JCR. The@NodeType
annotation takes four arguments;name
,registerNodeType
,supertype
andmixin
.
...
Code Block | ||
---|---|---|
| ||
@Entity @Interfaces("nl.gx.helloWorld.helloWorldPanel.domain.CustomEntity") @Namespace(prefix = Activator.NAMESPACE_PREFIX, URI = Activator.NAMESPACE_URI ) @NodeType(name = Activator.NAMESPACE_PREFIX + ":" + Activator.CUSTOM_ENTITY_NODETYPE, registerNodeType = true, supertype = Constants.NT_BASE_QUALIFIED_NAME, mixin = Constants.MIX_REFERENCEABLE_QUALIFIED_NAME) public class CustomEntityImpl implements CustomEntity,Comparable<CustomEntityImpl>{ private String myName; private String myText; private JcrWmId myId; @Property public String getName() { return myName; } public void setName(String name) { myName = name; } @Property public String getText() { return myText; } public void setText(String text) { myText = text; } public WmId getId() { return myId; } public void setId(WmId id) { myId = (JcrWmId) id; } public int compareTo(CustomEntityImpl otherEntity) { int result = getName().compareTo(otherEntity.getName()); if (result == 0) { result = getId().toString().compareTo(otherEntity.getId().toString()); } return result; } public boolean equals(Object obj) { if (obj instanceof CustomEntityImpl) { return ((CustomEntityImpl) obj).getId().toString().equalsIgnoreCase(getId().toString()); } return false; } } |
...
Registering an Entity
The implementation of the entity does not automatically register the entity with the Entity Manager to be managed by it. To do so, this must explicitly be defined in the component definition that defines the entity contained by the Activator. There are three methods available on the ComponentDefinitionImpl
that are relevant to entity registration.
...
Code Block | ||
---|---|---|
| ||
componentDefinition.setEntityClassNames(new String[] {CustomEntityImpl.class.getName()}); componentDefinition.setEntityFactoryClassName(DefaultJcrEntityFactoryImpl.class.getName()); componentDefinition.setPersistenceManagerClassName(DefaultJcrPersistenceManager.class.getName()); |
Note |
---|
Do not invoke |
...
Using the Entity Manager
The Entity Manager is implemented as a service. This means that the component that uses the Entity Manager must define a required service dependency with the nl.gx.webmanager
.services.entitymanager.EntityManager
interface, therefore the Component definition inside the Activator must contain the following code snippet:
...
As a result, the Entity Manager service will be injected into the component implementation class. Furthermore, the bundle dependency must be defined in the pom.xml
of the plugin. The following code snippet illustrates how this dependency is defined within the <dependencies>
section of the pom.xml
. Note that the <version>
attribute should state the XperienCentral version against which the plugin is built.
...
Code Block | ||
---|---|---|
| ||
<dependency> <groupId>nl.gx.webmanager.bundles</groupId> <artifactId>XperienCentral-entitymanager-bundle</artifactId> <scope>provided</scope> <version>${parent.version}</version> <dependency> <dependency> <groupId>nl.gx.webmanager.bundles</groupId> <artifactId>XperienCentral-entitydomain-bundle</artifactId> <scope>provided</scope> <version>${parent.version}</version> </dependency> |
...
Entity Management in the Controller
Now that the Entity is defined and registered with the Entity Manager, the next step is to implement the create, update and delete actions on the entity. This is typically done in the onSubmit
of the component’s controller. The onSubmit
should handle three different types of actions to be performed on the entities:
...
Code Block | ||
---|---|---|
| ||
public void onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors, ModelAndView modelAndView) throws Exception { super.onSubmit(request, response, command, errors, modelAndView); // Save the entity that is currently being edited if (myCustomTabFBO.getEntity() != null) { CustomEntity entity = myCustomTabFBO.getEntity(); BeanUtils.copyProperties(myCustomTabFBO, entity); myEntityManager.save(entity); } // Check if a new entity must be created if (myCustomTabFBO.getPanelAction().equals("createEntity")) { // Create the entity CustomEntity newEntity = myEntityManager.getInstance(CustomEntity.class); // Persist the entity for the first time myEntityManager.persist(newEntity); // Select the new entity in the panel myCustomTabFBO.setSelectedEntity(newEntity); } // Check if existing entities must be deleted if (myCustomTabFBO.getRemoveEntities() != null) { for (CustomEntity entity : myCustomTabFBO.getRemoveEntities()) { // Delete the entity myEntityManager.delete(entity); } } |
...
Entity Retrieval
Before existing entities can be updated, they must first be retrieved. For this, the Entity Manager supports two methods; find(WmId)
for retrieving entities for which the ID is known and getAll(Class<T>)
to retrieve all entities implementing a particular Entity
interface.
...
Code Block | ||
---|---|---|
| ||
public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { super.initBinder(request, binder); // Register a custom editor for the custom entity binder.registerCustomEditor(CustomEntity.class, new EntityPropertyEditor<CustomEntity>(myEntityManager)); } |
...
Property Annotations
The @Property
annotation before a method indicates that the method is the getter method for a particular property that should be persisted by the Entity Manager. The property may be of a primitive type like String
, boolean
and Integer
. Complex object types are supported including FileResource
. Complex property types are discussed in detail in this sectiontopic.
@Property
When the regular @Property
annotation is used for a complex object type, the property is created as a reference. This means that the physical location of the object in the persistence system (for example the JCR) is not defined; it may be stored anywhere in that persistence system. The property itself only stores a reference to this object (in the JCR, for example, it stores the UUID of the node as String property).
The @Property
annotation has the following properties:
Property | Description |
---|---|
name | The name of this property in the repository. Leave this empty if you want to use the same name as the property in the Java class. |
type | The JSR-170 property type to use. The default is |
| Specifies whether this property is mandatory. The default is |
multiple | Specifies whether the property may have more than one value. The default is |
| Add JSR-170 constraints to the property. By default no constraints are added. |
@Child
The @Child
annotation can be used to indicate that the object should not be stored as a reference property but as a child property of this entity. As a result, the physical location of the object will be such that there will be a parent-child relation between the entity and the object to which is referred by the @Child
annotation.
...
The @Child
annotation has the following properties:
Property | Description |
---|---|
name | The name of this property in the repository. Leave this empty if you want to use the same name as the property in the Java class. |
| Specifies whether this property is mandatory. The default is false. |
allowsSameNameSiblings | Specifies whether this child node can have same-name siblings. In other words, whether the parent node can have more than one child node with the same name. The default is false. |
| Specifies the minimum set of primary node types that the child node must have. The default is {http://www.jcp.org/jcr/nt/1.0}base which is the JSR-170 base type of all node types. |
defaultPrimaryType | Specifies the default primary node type that will be assigned to the child node if it is created without an explicitly specified primary node type. The default is {http://www.jcp.org/jcr/nt/1.0}unstructured which indicates the unstructured node type. |
Note |
---|
|
@Collection
The @Collection annotation can be used for properties of which the object type implements java.util.Collection. This annotation offers more advanced options mainly by supporting so called "lazy collections". A lazy collection is a collection of objects of which the objects themselves are not instantiated upon instantiation of the collection itself. There is therefore a significant difference between a regular collection (that is, a Set) of CustomEntity
objects and a lazy collection of CustomEntity
objects. While the regular collection will contain a list of references to the actual instances of CustomEntity
objects, the lazy collection only contains a list of references without the CustomEntity
objects themselves being loaded upon creation of the collection itself. This provides an important performance gain when such a collection contains many objects and nothing is actually done with the objects in the collection themselves. A lazy collection instantiates the objects within the collection only at the time they are needed.
The @collection
annotation has the following properties:
Property | Description |
---|---|
type | The type of the collection property, which must be either |
| Specifies the class associated with the objects contained by the collection. |
lazy | Specifies whether the collection should use lazy loading or not. The default is |
| Specifies if the objects contained by the reference should be stored by reference or as child objects. The default is by reference. |
Note |
---|
The |
...
Components of the Entity Manager
The Entity Manager comprises several components that together form a layered architecture, as depicted in the image below. Each layer delegates method calls to the appropriate component on the layer below it.
Entity Manager
The Entity Manager is available as a service in the XperienCentral platform. Its function is to hide all underlying layers from the plugin programmer. Internally, the Entity Manager manages a cache for each active session.
Entity Domains
In the above image, two types of Entity Domains are visible directly beneath the Entity Manager. Such Entity Domains represent a namespace for identifying entities. They each provide an implementation of Identifiable specifically for this EntityDomain
. The JcrEntityDomain
and its accompanying implementation of WmId
and JcrWmId
are available. It is the responsibility of the EntityManager
to select the right EntityDomain
for an Entity and then delegate to it.
Entity Factories
Entity Factories comprise the next layer. They provide a mechanism for post-processing entities after they have been constructed. Usually only one type of Entity Factory is used, namely the DefaultJcrEntityFactoryImpl
. Each Entity Factory can handle multiple types of entities. Basically there is one EntityFactory
per Component that handles all entities specified by that Component. It is the responsibility of Entity Domain to delegate to the appropriate Entity Factory for a given type of entity.
Persistence Managers
The last layer within the Entity Manager is formed by the Persistence Managers. It is the responsibility of the Persistence Managers to handle the communication with the underlying persistent storage and construct entities. One Persistence Manager is instantiated per entity type which handles all requests for that type of entity. It is the responsibility of the Entity Factories to manage those Persistence Managers and delegate to them. Typically only one type of Persistence Manager is used, namely the DefaultJcrPersistenceManager
.
...
The Entity Manager Versus JCRUtil
When you generate an element or media item from its archetype you will notice that these components do not use the Entity Manager to persist their properties. The element and media item implementations are marked with the @NodeType
and @Property
annotations but note the missing @Entity
annotation. The missing @Entity
annotation indicates that this class is not managed by the Entity Manager.
...
Persistent classes not managed by the Entity Manager are:
Persistent Class | Description |
---|---|
Custom media item implementation | Class that extends |
Custom element implementation | Class that extends |
A persistent class that is not managed by the Entity Manager may persist its properties using the nl.gx.webmanager.foundation.JcrUtil
class. This class contains methods to read and write values to and from JCR nodes. The getters and setters of an entity managed by the Entity Manager usually only store the value in a class variable and persist the entity by using EntityManager.persist()
. Using JCRUtil
is an alternative to EntityManager.persist()
however it should be invoked to persist each property individually. For example, to persist the company property:
...
Code Block | ||
---|---|---|
| ||
public void setCompany(String company) { JcrUtil.setString(getPrivateNode(), WCBConstants.NAMESPACE_PREFIX + ":company", company); } |
Note |
---|
|