Detail Model Persistence

The persister framework is responsible for the physical interaction with the database storage. It loads, saves and deletes business models by mediating them to the corresponding Persistence Model and utilizing the Java Persistence API (JPA) via Hibernate to manipulate the database by corresponding DML statements.
Ususally application developers should never use the persister framework directly, it is used by the Entity Manager of the Business automatically, but it's always good to know how things work in case you need to do some troubleshooting. This section gives an overview of the persister framework and the used algorithms as well as extension possibilities. Please refer to the linked javadoc for details on the available methods and classes.

images/download/attachments/109981518/HPM_EntityPersistence.jpg

Persister

The persister instance for a specific business model can be obtained from the PersisterRegistry which itself must be obtained from the PersistenceComponent. Currently the only available implementation of the persister interface is the ORMPersister which uses the object relational mapping functionality of JPA via Hibernate. Please note that the ORMPersister implementation itself is internal and must not be subclassed or directly used in any kind. It is also not (yet) possible to contribute an own persister, although the platform has been prepared for this.

Persister Interceptors

Currently the HPM platform provides two interceptors which are called (and must be called!) directly within the persister implementation.

  • SavePreCommitInterceptor is being called after the persistence model has been flushed to the database, but before the transaction has been committed! Therefore, any exception thrown in this interceptor will lead to an immediate rollback of the transaction!

  • SavePostCommitInterceptor is being called after the persistence model has been flushed to the database, and the transaction has been committed. You can perform tasks here which must be executed after the transaction has been committed - note that any exception thrown in this interceptor will not affect the written data, a rollback is no longer possible!

Mediators

The core task of a mediator is the mediation between the Persistence Model and the Business Model. Mediators should not execute database operations by themselfes, they should only work with the models provided. It's the persisters task to call the corresponding JPA flush methods etc. Mediators can be contributed for a specific entity or field type (depends on the mediator type) using the com.heiler.ppm.persistence.server.persister extension point. The HPM platform provides generic implementations (GenRootMediator, GenSubMediator, PropertyMediatorImpl) which will always be used as long as no specialized mediator has been contributed.

Mediators can not be exchanged by contribution!

Mediator Session

It is the persister's responsibility to create and initialize the MediatorSession which will be used in every mediator. The MediatorSession provides access to the root entity type, data source, mapping model, log data (user and timestamp) the JPA entity manager as well as the revision which should be used. Additionally the MediatorSession is the only way of communicating between different mediator / interceptor instances by it's get/set data interface. All objects in the persister framework are cached singletons and must not hold any state other then their registries and caches.

Root Mediator

The RootMediator is responsible for root entity types. The root mediator loads and writes the root data and then calls the property and sub mediators for the immediate child entity types and field types.

Sub Mediator

The SubMediator is analyzing the BusinessChangeSummary. Based on this it's determining the corresponding persistence objects and calls the property mediators for each of them. Last but not least it's calling the SubMediator recursively for each of it's own child entity types.

Property Mediator

The PropertyMediator reads the field's persistence value from the persistence object, converts it to the business datatype using the ConvertUtils and applies it to the business object. On the other hand it reads the business value, converts it to the persistence data type and applies it to the Persistence Model. Summarized you could say it reads and writes a single property (or field type).

Persistence Object Deleter

PersistenceObjectDeleter implements the actual mass deletion logic for an entity type. In contrary to the Mediators, the PersistenceObjectDeleter is executing bulk update or delete JPQL statements to do it's work. The generic implementation is building a dynamic JPQL statement, so in most cases it's not neccessary (or even a good idea) to interfere with this.
However, you can still contribute an own PersistenceObjectDeleter using the com.heiler.ppm.persistence.server.persister extension point.

Persistence Object Finder

PersistenceObjectFinder is responsible to find the persistence objects based on a given business object. It's utilized in the mediators and must sometimes be implemented in a special way for some more complex business model aspects. Just like the PersistenceObjectDeleters you should not interfere with those implementations and mostly the generic implementation is just fine.

Mediator Interceptors

The mediators do have a lot of interceptors which can be used to customize their behavior or to add additional, needed persistence logic. Please note that you should not try to implement business logic using the persistence framework at all - business logic should always be implemented using the Command Framework if possible.

  • PostReadInterceptor is being called after the business object for whose entity type the interceptor has been contributed has been read from the Persistence Model (thus standard read operations have been performed and the business model has been filled)

  • PostReadPropertyInterceptor is being called after the business property for which the interceptor has been contributed has been read. This interceptor must be contributed for a single field type.

  • PostWriteInterceptor is being called after the business objects changes have been applied to the persistence object. This interceptor must be contributed for an entity type.

  • PostWritePropertyInterceptor is being called after the business property has been applied to the persistence object. This interceptor must be contributed for a single field type.

  • PreFilterInterceptor is being called before the hibernate persistence filters are being activated and parameterized. Contributors can provide own logic to manipulate this.

Audit Log


Audit logging is an important feature in an application which has a lot of users and data transaction every day. Providing delta reports for 3rd party systems like web-shops was one of the main functional focuses on the audit log implemented in HPM 5.3. Therefore we enhanced the complete Persistence Model and provided also a new extension point specifically designed to be implemented by consulting in order to be prepared for the future and be most flexible in this area.

An audit log must not be mixed up with an audit trail! The audit log functionality stores creation, modification and deletion of objects. But only the time and the user will be stored. Every record/object thus has only one record with these data. In contrary an audit trail functionality stores also the value which has been changed in a historical way. So you can see then not only who and when this record has been changed the last time, but also what has been changed (which column), what was the value before etc. Audit trail is way more complex then audit log and has not yet been implemented in the HPM standard.

Physical audit log

Nearly all persistence objects in HPM support the Auditable base class (starting with HPM 5.3). Therefore all the corresponding database tables have been enhanced accordingly (please see Persistence Model for details). The persister framework checks every persistence object which is going to be saved in the database and updates the creation/modification timestamps automatically. Clients must do just nothing for this feature except supporting the needed columns in their persistence tables.

Logical audit log

The requirements for logical audit logging of root entities is being driven by use cases like "Show all items which have been created by user 'abc' since yesterday", or "Show all items which have been modified since last week". To be able to fulfill these requirements we need to store audit log data not only in every table (like with the physical audit log), but also on the business root object. Unless that, we would need to join nearly all tables for a specific entity item so we can determine if the item has been changed. (Maybe only a price has changed, which would not be reflected in a change of the Article (or ArticleRevision) table. Additionally to that, the definition of "changed, created and deleted" may differ depending on specific scenarios which use this information. For example, regarding an ERP replication process only certain fields may be relevant for replication - therefore as seen from the ERP system, an object has been changed when one of those fields change only.

With HPM 5.3 we returned from the approach of storing this data in a generic table, since in fact, HPM has no dynamic Persistence Model - we only have a dynamic Business Model. Introducing a generic model which stores data for multiple different entity types was confusing and error prone, thus we refactured this part in 5.3 again.

AuditLogWriter

The persister calls the AuditLogWriter after the persistence object has been modified by the mediators, but before flush is being called on the JPA EntityManager. With this we ensure two things:

  • Limit the number of database round trips and provide hibernate with the possibility to streamline the SQL statements it sends to the database.

  • Make sure that the audit log records are written in a synchronous and transaction safe manner. Thus we make sure that the audit log records are not written in case the entity item can not be saved due any reason.

    It's currently not possible to customize the AuditLogWriter itself, please use the AuditLogProvider for this

AuditLogProvider

Since direct interaction with the persistence layer is not a recommended customizing, we provide an extra interface which is specifically designed to be customized. The AuditLogProvider interface provides methods to implement special custom logic. The AuditLogWriter will read the current audit log record and calls then the AuditLogProvider which itself is responsible to set the creation/modification/deletion user and timestamp in the given AuditLogRecord object. The AuditLogProvider implementation however must not save this record to the database in any case! It's the AuditLogWriter which is responsible to build the bridge between Persister and AuditLogProvider and will save the AuditLogRecord in the most effective way. Please note that the AuditLogProvider interface also provides methods for bulk modifications of entity items. These methods will be called in case of bulk updates which occur in deletion scenarios mostly.

An entity item can have multiple logical audit log records which are distinguished by the classifier field. So you could have an audit log record specific for different export channels like ERP system or Web-Shop etc.

AuditLogProviders can be contributed with the com.heiler.ppm.persistence.auditLog extension point. If you want to have multiple audit log records, you will need to contribute also an appropriate classifier in this extension point.

HPM Provider

Since in previous HPM releases we already had a root entity audit log feature enabled for certain entities we needed to provide a default implementation which fulfills this known feature. The default implementation writes a "created, changed and deleted" value based on the physical events. Thus in case of any change, the default implementation will write the modification date/user and in case of delete it will write the deleted date/user.
This default implementation is contributed for all entity types and the classifier: "HPM".

Repository Definition

The types area of the repository has been enhanced to have a 1:n sub-entity for every root entity for all entity types. Logical key will be the mentioned classifier. The custom entity uses an enumeration of available classifiers and a default qualification for this key with "HPM". So the fields can be requalified by the user to show the audit log information for a different classifier. The used classifier enum provider uses the classifier elements from the com.heiler.ppm.persistence.auditLog extension point.