This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Abstract entity base class

All (non-transient) entities inherit from AbstractPojoEntity. This class provides all functionality required by the Entity interface.

Primary Key

In hibernate the primary key is simply a property (typically a Long) annotated with Id. In the old API the primary key value was encapsulated behind the PrimaryKey interface.

An instance of PrimaryKey is created when getKey() or requireKey() is called for the first name. The key is cached so that always the same instance is returned, which is expected by some of the business code.

Accessing values

PropertyAccessorService

The PropertyAccessorServiceImpl efficiently reads and writes entity properties.

Different strategies are used depending on the property type. For all persistent properties, the calls are delegated to Hibernate’s EntityPersister.

Transient properties are manually accessed using reflection.

Warning

It is important that all accessors are cached for performance reasons.

Reading values

All calls to the different Entity#getValue() methods are delegated to AbstractHibernateEntity#internalGetValue(), where the actual field is resolved and read using the PropertyAccessorService.

For backwards compatibility, the resulting value is passed to TypeManager#isolate() before it is returned (which creates a copy of Binary instances).

It is also attempted to convert the value to the requested type.

Writing values

All calls to the different Entity#setValue() methods are delegated to AbstractHibernateEntity#internalSetValue(), where the actual field is resolved and updated using the PropertyAccessorService.

At first the value is converted to the required target type (if this is not already the case and a suitable Converter exists).

The resulting value is then compared to the old value - if they are the same, the method silently returns. After the value has been set, a EntityFacadeListener#entityChanging() event will be fired.

Warning

Calling internalGetValue(), internalSetValue() bypasses the entity interceptors (security, localization etc). Therefore passing the field name to EntityInterceptor#accessField() is normally required before calling these methods. It may be omitted for certain internal calls where the interceptors are not required.

When Hibernate internally reads or writes properties of an entity, the field is accessed directly and no additional code is executed.

If no transaction is running when setValue() is called (or a relation is changed) an exception will be thrown, because otherwise these changes would be silently lost.

Resolving relations

An association in hibernate is simply an instance of the referenced type (or a collection if it’s a to-many relation). In the old API it was required to ‘resolve’ a relation ( Entity#resolve() ) to a RelationQuery. This relation query can then be executed to get an instance of Relation.

To-One relations

All to one associations are explicitly configured to be loaded lazily (JPA default is eager).

ToOneRelationQueryAdapter is the implementation of RelationQuery used for to-one associations. It does not contain any special logic, it simply delegates the calls to the wrapped entity.

ToOneRelationAdapter is the implementation of Relation for to-one associations. This class implements getting, setting and removing the associated instance.

All access (read or write) goes through the RelationInterceptor, this allows other modules to add functionality (for example security checks). In order to enforce cleaner code, methods that were meant for to-many associations (for example RelationInterceptor#addEntity()) are not supported.

The ToOneRelationAdapter provides the last interceptor in the chain, which actually accesses the underlying entity.

  • Reading the value means simply calling Entity#getValue() on the source entity. Internally this calls the generated getter for the association.

  • When a value is written some more actions are performed (if the new value is the same as the current one, the call is silently ignored):

    • The updated value is set to the source entity using Entity#setValue()

    • As all associations in nice2 are bi-directional, the inverse association (in this case always a one-to-many association) needs to be updated. The previous value (if present) needs to be removed, and the new value (if not null) needs to be added from/to the inverse association.

    • The EntityFacadeListener#entityRelationChanging() event is fired.

      • If the previous value was not null, an event is fired for removing the old value (the adjusting flag is true if the new value is not null)

      • If the new value is not null, an event is fired for adding the new value.

To-many relations

Collections are loaded lazily by default. We use a special implementation of the PersistentSet that supports reloading a collection from the database.

See Collection reloading for further information.

Every time a to-many relation is resolved, it should be reloaded from the database (because this is the behaviour of the old persistence implementation).

ToManyRelationQueryAdapter is the implementation of RelationQuery used for to-many associations. It mainly delegates to the wrapped collection of entities. However hibernate does not support pagination or (dynamic) sorting of associations, therefore these cases have to be implemented specifically: If a relations needs to be resolved with a specific ordering or pagination an additional query will be executed to get the desired results (the collection won’t be touched). The results are returned as an unmodifiable collection, because changes to this collection would be ignored (as it is unknown to hibernate).

ToManyRelationAdapter is the implementation of Relation for to-many associations. This class implements applying modifications to the underlying collection.

Like its ‘to-one’ counterpart it implements the final RelationInterceptor that actually accesses the underlying collection and also enforces the usage of the correct methods.

If an operation (addEntity or removeEntity) causes a change:

  • The underlying collection is modified

  • The inverse association (in this case one-to-many or a many-to-many) is adjusted

  • The EntityFacadeListener#entityRelationChanging() event is fired.

    • An event is fired if an entity has been added or removed (the adjusting flag is always false as there is only one event)

    • If all values are replaced using setEntities(), first an event is fired for all removed entities. After that an event is fired for all newly added entities. If an entity is part of the collection before and after the operation, no add or remove event should be fired for this entity. The adjusting is always false, except for the very last event.

Note

size() does not initialize the collection, but executes a COUNT query. This is important if the collection is large. However this means that size() should not be called when the collection is going to be initialized anyway (for example when iterator() or toList() is called), because that would lead to an unnecessary query.

Syncing inverse associations

When the user changes an association, the other side should be updated automatically by the framework, as all associations are bi-directional at the moment.

When doing this, care must be taken not to unnecessarily initialize lazy collections, as this would have a negative performance impact. On the other hand, sometimes this is necessary in many to many associations, when the user did not update the owning side (see Entity class generation).

  • If the reverse side is a many-to-one association it can just be updated without any performance penalty (it is also necessary to do so because the many-to-one side is always the owning side).

  • If the reverse side is a one-to-many or inverse many-to-many association, the collection may be updated but the addition may be queued if the collection is not initialized yet.

  • If the reverse side is the owning side of many-to-many association, the collection must always be updated (and perhaps initialized). Otherwise the changes would not be persisted by hibernate.

See Delayed operation for further information about queued operations.

Note

In the future it might be worth to check if we want to explicitly map the mapping table with an entity class. This would allow using many-to-one/one-to-many associations and avoid unnecessary collection initialization.

Entity state

The states are checked in the following order (important):

  • PHANTOM

    • The phantom state is tracked by the wasDeleted field. This is necessary because of two reasons. First, the actual delete query is not immediately fired (but just before the transaction is committed, to make sure that all delete statements are executed in the correct order), but the state has to be PHANTOM immediately after the delete() method was called. Second, after the session is flushed, the deleted entity is no longer in Hibernate’s persistence context, so it would not be possible to tell if an entity is deleted using EntityEntry#getStatus().

  • CONCEPTION

    • If an entity has a primary key which is auto-generated by the database and this key is null, the state of the entity must be conception. For primary keys which are generated by the user (for example strings) this does not work, instead it is checked whether an EntityEntry for this entity exists.

    • Additionally if an entity has its primary key already set and its EntityEntry status is SAVING the entity is also in conception state. This can happen when Entity#getState() is called from inside a validator (see ValidationInterceptor#onSave()).

  • INVALID

    • If there is no EntityEntry for an entity and it is not in conception state or deleted, it must be invalid.

  • DIRTY

  • CLEAN

    • If all other states do not apply, the entity must be clean (that means persisted and unchanged).

Dirty checking

The Entity interface differentiates between touched and changed properties. A field is touched when setValue() has been called at least once for that field, even if the value is still the same. As this distinction rarely makes sense, we no longer support it - only changed fields are returned from the dirty checking methods (for example Entity#getChangedFields() or Entity#getTouchedFields()).

The dirty fields are managed by the abstract base class AbstractDirtyCheckingEntity in the changedFields property. All calls to the setter methods are intercepted using a custom PropertyAccessorService. If the value to be set is different from the Old value, the field is marked as changed.

To check for modified collections (to-many relations) we can simply use the isDirty() method of the PersistentCollection.

The list of changed fields needs to be reset when the changes are flushed to the database. This is done by the ValidationInterceptor after the entity validation has been completed.

Note

Instead of manually keeping track of all the changes it would be possible to just always compare the current value with the old value, when we need the changed fields. However this is a bit of a performance problem, because the changed fields are needed quite often, especially by Entity#getState() to check if the current state is DIRTY.

Old value

The Entity interface allows to query for the old value. This is the value of a certain property when it was loaded from the database at the beginning of the transaction, ignoring all uncommitted changes.

This is achieved by checking the ‘loaded state’ of the EntityEntry, which can be retrieved from the PersistenceContext. This is where hibernate stores the state of the entity when it is loaded and this state is also used for hibernate’s default dirty checking mechanism.

EntityInterceptor

The EntityInterceptor interface allows customizing the core entity functionality. The following functions can be intercepted:

  • Reading and writing fields

  • Deleting entities

  • Modifying relations

An entity interceptor instance is injected into every entity by the EntityFactoryImpl. The instance is created by the EntityInterceptorFactoryImpl which combines all interceptor contributions into an interceptor chain. The inner most interceptor (which actually accesses the entity fields and so on) is provided by the entity itself (AbstractHibernateEntity#getInnerInterceptor()).

Note

The inner interceptor is wrapped in a LazyInterceptor to avoid recursive proxy initialization (Entity#getValue() -> proxy initialization -> EntityFactory#createInstance() -> Entity#getInnerInterceptor() -> proxy initialization …).

Accessing values

The method EntityInterceptor#accessField() can be used to intercept read or write access to a field. It is always called when a value is accessed by the entity (typically when Entity#get/setValue() is called).

The default inner interceptor simply resolves the field name using the FieldResolver. If write access is requested it additionally checks if the field is not a primary key or other generated field.

The SecurityEntityInterceptorContribution uses this method to check the read or write permission of the given field. If the given field is a localized field, the base field (label instead of label_de) is used to check permissions.

Deleting entities

EntityInterceptor#deleteEntity() is called when an entity is deleted (Entity#delete()). The inner interceptor fires an EntityFacadeListener#entityDeleting() event and (unless the entity is unsaved) schedules the entity for deletion with the EntityTransactionContext.

In addition the SecurityEntityInterceptorContribution checks if the delete permission is granted for the current user.

Modifying relations

A RelationInterceptor can be obtained from the entity interceptor using createRelationInterceptor(). The relation interceptor can be used to intercept Relation modifications.

The inner interceptors are provided by the AbstractRelationAdapter implementations. These update the relation value or collection and fire an EntityFacadeListener#entityRelationChanging() event.

In addition the SecurityEntityInterceptorContribution checks if the current user is allowed to modify a relation.

The BusinessUnitEntityInterceptor checks if the business unit of an entity may be manually changed by the user (only business unit types MANUAL_SET and NONE may be changed by the user).

FieldResolver

The FieldResolverImpl resolves a property name to the name of the corresponding entity field. Usually the property name is equal to the entity field name, however there are two exceptions:

  • Localized fields: if the base field of a localized field is requested (e.g. label) it is resolved to the field of the current locale (e.g. label_de).

  • When java reserved words are used as a field name in the entity model, the field name needs to be adjusted (see PojoUtils.normalizeFieldName()).

It is called whenever a field is accessed or referenced by name, for example when reading or writing fields or when compiling queries.