This chapter provides information on using the persistence framework, which moves program data (in memory objects) to and from a permanent data store (the database). The persistence framework also manages the database and manages the mapping between the database and the objects.
You use the persistence framework when extending Oracle Communications Unified Inventory Management (UIM). For example, custom rulesets or custom web services typically have code that reads or updates the database, which is done using the persistence framework. So, UIIM custom code developers need to be familiar with the contents of this chapter.
The persistence framework is built on top of EclipseLink, which implements Java Persistence API (JPA) technology. Functional extensions employ standard Java practices.
This chapter does not replace the EclipseLink or JPA development guides. Both technologies are covered in greater detail at the following websites:
JPA Specifications
http://wiki.eclipse.org/EclipseLink/Specs
EclipseLink
http://wiki.eclipse.org/EclipseLink
JPA
http://www.eclipse.org/eclipselink/#jpa
Note:
Documentation on third-party software products is limited to the information needed to use the UIM persistence framework. If you need additional information on a third-party software application, consult the documentation provided by the product's manufacturer.The persistence framework employs the concepts of eager and lazy fetching and of managed and non-managed entities, as described in the following sections.
Note:
Information on eager and lazy fetching can be found on the Oracle Technology Network website at:http://www.oracle.com/technetwork/articles/javase/index-138213.html
A fetchType of eager means that a persistence provider loads the attribute of an entity along with the entity, while a fetchType of lazy is a hint to the provider that the attribute need not be fetched along with the entity. This means that even though you may specify the fetchType as lazy, the persistence provider may choose to load the attribute eagerly.
By default, all relationships are configured as lazy loading in the metadata, and all basic attributes are configured as eager fetched in the metadata. To configure an attribute as lazy loading in the metadata, set the <lazy> attribute to true. For example:
<entity type="cim:DataTypes"
interface="oracle.communications.platform.entity.DataTypes">
<attribute name="clobString" lazy="true"/>
</entity>
The result is a generated annotation within the DataTypesDAO.java class. For example:
@Basic(fetch=FetchType.LAZY) private java.lang.String clobString;
If a field is configured as lazy loading, and you want to eager fetch it when the entity is retrieved from the database, use the Finder.addEagerFetchField() method. For example:
finder = PersistenceHelper.makeFinder();
finder.setResultClass(TelephoneNumber.class);
finder.setJPQLFilter("o.id = :tnId");
finder.addParameter("tnId","88888888");
finder.addEagerFetchField(EagerFetch.LEFT_FETCH,"o.specification");
Collection<TelephoneNumber> tns = finder.findMatches();
finder.close();
The previous example shows a fetch mode of LEFT_FETCH. The persistence framework supports the following fetch modes for an eager fetch:
BATCH: Batch reading may require more than one trip to the database but is usually more efficient than a join fetch, especially join fetches that involve collection relationships. Batch reading configures the query to optimize the retrieval of the related objects, and the related objects for all the resulting objects are read in a single query (instead of multiple queries).
FETCH: This fetch mode uses an inner join.
LEFT_FETCH: This fetch mode uses an outer join.
A persistence context is a set of entities such that for any persistent identity there is a unique entity instance. Within a persistence context, entities are managed. An entity manager controls life cycles and accesses data store resources.
When a persistence context ends, previously managed entities become non-managed. A non-managed entity is no longer under the control of the entity manager and no longer has access to data store resources. The major difference between an entity that is managed and an entity that is non-managed is:
When an entity is managed, the object is connected to the database and changes made to the object are reflected in the database when committed, or flushed in a transaction.
When an entity is non-managed, the object is not connected to the database, so changes are never applied to the database.
The non-managed object can be stale, which can cause you to receive the OptimisticLockingException when calling the EclipseLink attach() method. In this case, discard the stale non-managed object, retrieve the new object from the data store, and perform any update operation against the new version.
When a transaction already exists and an entity is explicitly retrieved from the database, the entity is managed. There is no need to eager fetch the entity attributes because the attributes and relationships can be lazy-loaded while the transaction is still active.
When no transaction exists, the entity becomes non-managed. EclipseLink supports the lazy loading of relationships of a non-managed entity. EclipseLink also supports the lazy loading of primitive attributes such as String.
Note:
If an entity is serialized and then deserialized, such as sent through a remote EJB interface or web service, the relationships cannot be lazy loaded, and an eager fetch must be used to access the relationships. Alternatively, the application can start a transaction to make the entity managed. When the entity is managed, the attributes and relationships can be accessed directly, and the eager fetch is not required.When an entity is created, it is neither managed nor non-managed. The entity status is Transient because there is no representation of the entity in the database yet. All relational and collection attributes on a transient entity are available (that is, if it is null, then it is really null). When the transient entity is passed to a ManagerX.createX(..) method, the entity is persisted into the database. The entity is persisted by reference. The entity life-cycle status is changed from Transient to Persistent-New. There is a copy of the entity created. When the transaction is committed, the entity becomes non-managed.
Usually, the UI code keeps the non-managed entity in the session so that it can be updated. Sometimes, the UI code does an explicit Find to retrieve and store a non-managed entity in a session. You can store the non-managed entity in a session to avoid table locks on the database. The client code performs a set on the detached entity. When the updated entity is passed into the ManagerX.updateX(..) method, the manager persists the changes by using the EclipseLink attach() method. The current implementation of EclipseLink attach makes a copy of the entity and persists the changes. The new copy represents the managed entity, making the non-managed entity obsolete. For example, the createEntity() and updateEntity() methods each return a new copy of the entity. The returned copies are managed entities, making the original non-managed entity obsolete.
A = createEntity(A) A = updateEntity(A)
All of the persistence framework classes covered in this section expose API methods that you can use when extending UIM. For example, you may want to add additional validations to existing UIM functionality, or add additional processing.
Note:
For information on the classes described in this section, including a listing of method names, arguments, and returns, see the Javadoc. For instructions on how to access the Javadoc, see "Javadoc Documentation".Package: oracle.communications.platform.persistence
PersistenceManager generically manages entities defined in the *-entities.xml files. This manager provides methods to:
Create, update, and delete an entity or entities
Check whether an EntityManager is invoked from a Java Transaction API (JTA) context
(JTA specifies standard Java interfaces between a transaction manager and the parties involved in a distributed transaction system: The resource manager, the application server, and the transactional applications. A JEE application may use JTA, but a standalone JSE application does not.)
Set and get the logging level
This class is a wrapper for the methods defined in the standard javax.persistence.EntityManager class.
Package: oracle.communications.platform.persistence
TypeRegistry is a generated class that extends TypeRegistryBase. As part of the entity code generation process, each entity is added to a class list managed by TypeRegistry. TypeRegistry provides convenient methods to get a data access object (DAO) implementation class for each entity. Table 3-1 contains a list of the methods defined in the TypeRegistryBase class.
Table 3-1 TypeRegistryBase APIs
API | Description |
---|---|
classFor(Class) |
Gets the concrete class, which implements the given inventory entity class. |
interfaceFor(Class) |
Gets the inventory entity interface, based on the concrete class. |
classForDiscriminator(String discriminator) |
Gets the entity implementation class, based on the discriminator. |
discriminatorForClass(Class) |
Gets the discriminator, based on the implementation class. |
Package: oracle.communications.platform.persistence
Finder provides methods for querying entities based on simple or complex search criteria. It has convenience methods that set up query parameters and fetch properties. Convenient find methods are provided; however, a complex Java persistence query language (JPQL) query can also be built iteratively.
Finder provides the most frequently used query mechanism. Additional query complexity that can be reused should be incorporated into the entity managers instead. The entity managers should then use Finder for building the queries, or use JPA directly. See "Entity Managers" for more information.
Finder defines methods that enable you to:
Get an entity based on the entity key
Refresh an entity or a collection of entities
Find an entity or entities based on various options, such as name, entity, or ID
Define a JPQL statement
Run the defined JPQL statement
Reset the Finder, which resets all query parameters to null
These methods are further explored in the following sections.
The Finder class provides numerous methods that you can use to define a JPQL statement. By using these methods you can:
Set the result class to query
Add a join expression
Set filters, such as a where clause or min/max
Add an attribute to specify the result set be returned in ascending or descending order
Set a range to filter the result set
Add and set parameters
Declare variables
Add and set variables
Add hints, which are JPA-specific
Add eager fetch fields
Clear eager fetch fields
The Finder.find(Class<E> candidateType, String filter) method is a convenient method to use because it does not consider any parameters you set on Finder before you call that method. To include parameters, use the Finder.find(Class< E > candidateType, String filter, String [] paramNames, Object [] params) method.
Alternatively, you can build the parameters list using Finder beforehand, then use the findMatches() method. The findMatches() method uses the parameters you set.
Table 3-2 lists some of the commonly used methods defined in the Finder class.
API | Description |
---|---|
find |
Overloaded method that finds an entity or entities based on various arguments, such as entity type, the current filter setting on Finder, a list of the current parameters set on Finder, etc. |
findByName, findById, findByEntity |
Various methods that find an entity or entities based on name, ID, or entity type. |
findMin and findMax |
Finds the minimum or maximum value based on entity type and the value of min or max, which is used by the method to call Finder.setJPQLFilter(). |
findMatches |
Overloaded methods that finds an entity or entities based on various arguments, such as an Oracle Text search String, and other arguments that you set. |
findByJPQL |
Finds a result set based on a String argument representing a JPQL statement that you define. |
executeUpdateJPQL |
Executes an update based on a String argument representing a JPQL statement that you define. This method returns the number of updated entities. |
findByNativeSQL |
Finds a result set based on a String argument representing a native SQL statement that you define. |
executeUpdateNativeSQL |
Executes an update based on a String argument representing a native SQL statement that you define. This method returns the number of updated entities. |
get |
Overloaded method that gets an entity based on entity type and entity key, or based on entity type, entity key, and whether or not the entity is a valid entity. |
refresh |
Overloaded method that refreshes the given entity, or the given collection of entities. |
The basic differences between PersistenceManager.refresh(), PersistenceManager.attach(), and PersistenceManager.connect() are:
Refresh() refreshes the entity content back to the state of the database, and discards any changes made to the entity. If the entity is managed, the refresh API retrieves a copy from the database to refresh the managed entity. If the entity is non-managed, the refresh API makes the entity managed. Any changes previously made to the managed or non-managed entity are discarded. The refresh API returns the reference to the managed entity.
Attach() makes the non-managed entity managed, and retains any changes made to the entity. If the entity is already managed, the attach API does nothing in terms of attaching the entity to the database. If the entity is non-managed, the attach API makes the entity managed. Any changes previously made to the managed or non-managed entity are sent to the database by EclipseLink when the transaction is committed or flushed. The attach API returns the reference to the managed entity.
Connect() makes the non-managed entity managed, and discards any changes made to the entity. If the entity is already managed, the connect API does nothing in terms of connecting the entity to the database. If the entity is non-managed, the connect API makes the entity managed. Any changes previously made to the managed or non-managed entity are discarded. The connect API returns the reference to the managed entity.
Refresh() does a get from the database. refresh() takes a detached entity, connects it to the database, but does not merge the entity attribute into the database. Refresh() re-retrieves the entity even when it is already attached.
Attach() takes a detached entity and merges its data into the database. The operation fails if the detached entity is stale. When attach attaches the detached entity to the database, it also merges the entity attribute values into the database. Attach() ignores the entity if it is already attached.
If you do not intend to merge the entity attributes of an entity in the database, do not use attach(). If you do, you may be updating an attribute in the database. Also, the last modified fields for the entity are updated, and the entity version is updated.
Note:
Using attach() may cause an OptimisticLockVerificationException because it tries to merge values in the database. If the detached entity is a stale entity (some other code thread has modified the same entity and has incremented the entity version), using attach again causes this exception.Package: oracle.communications.inventory.api.framework.persistence
InventoryFinder extends Finder and provides a few additional methods, as described in Table 3-3.
Package: oracle.communications.platform.persistence
PersistenceHelper is a generated class that provides factory methods to get an instance of an entity manager, the TypeRegistry, a Finder, an InventoryFinder, or the PersistenceManager.
Package: oracle.communications.platform.persistence
All persistent entities implement the Persistent API. It provides convenience methods for determining the state of an entity. These methods are all read-only; so, the methods can run whether or not there is an active transaction. The following methods are defined in the Persistent API and are available on all entities.
public Class getEntityType(); public String getOid(); public long getEntityId(); public String getEntityClass(); public int getEntityVersion(); public boolean isEntityIdValid(); public Identifier makeIdentitifer(); public void makeTransient(); public boolean isPopulated(String fieldName); public void unpopulate(String fieldName); public boolean isTransient(); public boolean isPersistent(); public boolean isTransactional(); public boolean isNew(); public boolean isDirty(); public boolean isDeleted(); public boolean isDetached(); public <E extends Persistent> E connect(); public <E extends Persistent> E refresh(); public <E extends Persistent> E attach(); public String getEntityDescription();
Entity managers are not part of the persistence framework: they are additional managers that use the persistence framework to support the overall application logic. An entity manager manages the database tables for a specific functional area. For example, EquipmentManager manages the Equipment table, but it also manages EquipmentHolder, PhysicalPort, PhysicalConnector, PhysicalDevice, and so forth.
Entity managers are defined in the metadata by the <manager> element and <interface> attribute. Example 3-1 is an excerpt from the uim-equipment-entities.xml file:
Example 3-1 uim-equipment-entities.xml
<manager interface="oracle.communications.inventory.api.equipment.EquipmentManager" class="oracle.communications.inventory.api.equipment.impl.EquipmentManagerImpl"/>
Every entity manager defined in the metadata has a corresponding entity manager and implementation of the manager. So, based on Example 3-1, the following classes exist:
EquipmentManager
EquipmentManagerImpl
Entity managers are not generated classes; however, the factory methods in PersistenceHelper that allow for the instantiation of the managers are generated. These factory methods are generated based on the metadata definition.
Note:
Entity managers are provided for all entities defined in the metadata. If the database is extended to define new entities, the entity manager and implementation of the manager must be written.The relationship of entity to entity manager is not one-to-one. For example, in ocim-equipment-entities.xml file, there are a number of entities defined, and each entity defines its own entity interface (which differs from a manager interface). An entity interface defines the getter and setter methods for data defined for the entity. Example 3-2 is an excerpt from the ocim-equipment-entities.xml file that shows two entity definitions. The definitions include the interface that is defined for an entity (not for a manager).
The PersistenceManagerBean class is the common base class for all entity manager implementations, and all entity manager implementations extend BaseInvManager. TransitionManagerBean is another layer of inheritance. The inheritance structure of all entity manager implementations is shown below. The following sections discuss each of these classes.
PersistenceManagerBean
|
TransitionManagerImpl
|
BaseInvManager
|
EntityNameManagerImpl
Note:
In some cases, there are additional layers between BaseInvManager and EntityNameManagerImpl, but these four layers of inheritance are always present. An example that has additional layers is LogicalDeviceManagerImpl.Specifying the <managedBy> attribute for an entity in the metadata allows the entity manager to override the default behavior of the following methods:
TransitionManager.transition(LifeCycleManaged, Object)
PersistenceManagerBean.completeCreate(Persistent)
PersistenceManagerBean.completeUpdate(Persistent)
PersistenceManagerBean.completeDelete(Persistent)
Package: oracle.communications.platform.persistence.impl
PersistenceManagerBean is the common base class for all entity managers. It provides convenient create, read, update, and delete (CRUD) methods for managing entity persistence. It also provides methods to attach an object to the persistence engine, and methods to test for object equality. Developing entity managers requires the use of the PersistenceManagerBean class. It defines all the persistence-related methods used by entity managers, it hides the JPA standard PersistenceManager, and it wraps the persistence logic required.
Package: oracle.communications.inventory.api.common.impl
TransitionManagerImpl transitions an entity's business and object states, which is only applicable for entities defined as life-cycle managed in the metadata. This layer of inheritance is always in place, but it is used only by life-cycle managed entities. See Chapter 5, "Extending Life Cycles" for more information.
This section provides some JPQL query examples that show common UIM search scenarios.
Example 3-3 Custom Object Search
// This example shows a search for a custom object based on the name of the custom // object. SELECT COUNT(DISTINCT o) FROM CustomObject o WHERE UPPER(o.name) LIKE UPPER(:nameParam) escape '\' AND o.specification = :specParam AND (o.objectState = oracle.communications.inventory.api.ObjectState.ACTIVE OR o.objectState = oracle.communications.inventory.api.ObjectState.INACTIVE)
Example 3-4 Physical Port Search
// This example shows a search for a physical port based on physical device name // and physical port specification name. SELECT COUNT(DISTINCT o) FROM PhysicalPort o WHERE o.id is not null AND o.specification = :specParam AND UPPER(o.physicalDevice.name) LIKE UPPER(:pdNameParam) escape '\' AND (o.objectState = oracle.communications.inventory.api.ObjectState.ACTIVE OR o.objectState = oracle.communications.inventory.api.ObjectState.INACTIVE)
Example 3-5 Physical Device Search
// This example shows a search for a physical device based on a characteristic. SELECT COUNT(DISTINCT o) FROM PhysicalDevice o JOIN o.characteristics chars0var WHERE o.id is not null AND (chars0var.name = :pCharName0Param AND UPPER(chars0var.value) LIKE :pCharValue0Param escape '\' ) AND o.specification = :specParam AND (o.objectState = oracle.communications.inventory.api.ObjectState.ACTIVE OR o.objectState = oracle.communications.inventory.api.ObjectState.INACTIVE)