This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
The EntityTransactionContext collects entities that have been created or deleted during the transaction and applies these changes to the session before the transaction is committed.
All new entity instances are tracked automatically and the user of the API does not have to call
(or equivalent) manually to add an entity to the persistence context.
All calls to
Entity#delete() are recorded and then executed before the transaction is committed. The calls
will be reordered so that no constraint violations can occur on the database. That means it does not matter in which
order the entities are deleted by the user. To avoid a datebase timeout the entities are deleted in partitions.
The service is a singleton and all changes are stored in maps using the current Session as key. The values are removed by the TransactionControl after each commit or rollback. To prevent memory leaks the entire operation is wrapped in a try/finally block that clears the values of the current session after the operation has been completed.
In a first step all entities that are created and deleted in the same transaction are removed from the list of entities to be inserted. This is necessary sometimes, as all entities are going to be saved automatically when they are created within a transaction.
After that all newly created entities are passed to the EntityInsertActionResolver which creates a list of EntityInsertAction. The returned list is ordered such that no constraint violations can occur when the insert statements are executed.
All that needs to be done is to make sure that unsaved entities which have non-nullable many-to-one reference to another unsaved entity are saved after the referenced entity. All other cases (many-to-many associations or nullable references) are correctly ordered by hibernate.
Also have a look at the javadoc in EntityTransactionContextTest and EntityInsertActionResolver for more implementation details.
In addition the following listeners are invoked when an entity was instantiated:
EntityFacadeListener#entityCreatingis called for every newly created entity (this is not called for entities that are loaded from the database).
EntityCreationListener are called for every created entity instance (
entityCreated()for new entities and
entityLoaded()for existing entities).
Newly created entities are added to EntityTransactionContext so that they will be persisted at the end of the transaction.
There is a caveat for entities loaded from the database: It is possible that the entity instantiation is actually the initialization of a HibernateProxy. In this case it is important to pass the proxy instance to the listeners (instead of the actual entity instance). Otherwise there are multiple entity instances representing the same database row, which will lead to unexpected side effects.
This listener (comparable to
EntityFacadeListener#entityCreating) is meant to be used by framework
code and will be called before all EntityFacadeListener
which are supposed to be used by business code.
Similar considerations need to be made when deleting multiple entities. Entities that are being referenced by other deleted entities must be deleted first to avoid constraint violation errors (which is the reverse order of the insert).
The deletion is done by the EntityDeletionUtils based on a list of entity models.
A dependency map is created based on the existing relations between the entity models (a
All entities of the key entity models must be deleted after the entities of the value entity models.
The following principles apply:
The entities on the many-to-one side of a bi-directional association need to be deleted first.
The owning side of a many-to-many should be deleted first (because the owning side manages the join table).
If the models depend on each other (many-to-one association from both sides) the side which has the non-nullable foreign key needs to be deleted first.
The inserting and deleting code cannot use the same ordering logic. See comments in the issue TOCDEV-312 for more details.
For performance reasons all deleted entities (either through
Entity#delete() or through the delete query builder)
are collected and then deleted using a single statement.
Based on the ordering explained above, the following is executed per model:
The entities are deleted using a CriteriaDelete query.
Because this deletes the entities directly from the database, we need to remove the deleted entities from the session manually.
First the entities are removed from loaded collections in the session (see
and then the entities itself are detached from the session.
And finally the after commit event must be manually triggered as well (see
Before any delete query is executed, the session must be flushed to make sure that all
UPDATE statements are executed first
(as they might reference an entity that will be deleted in the same transaction and because the CriteriaDelete
queries are executed immediately and not when the session is flushed).
Removal of deleted entities from associations¶
Before an entity is deleted, all nullable references to this entity will be set to
NULL to avoid constraint violations.
This also applies for batch deletions.
This was introduced in order to be compatible with existing code (as it is the default behaviour of the old persistence
For each ‘one to many’ association (whose inverse side is nullable) the following query is executed:
UPDATE nice_entity SET relReverse = NULL WHERE relReverse = IN (:obj) (
:obj are the entities to be deleted).
If the entity model to be deleted has an association onto itself, the rows that will be deleted anyway will not
be updated, as this caused some
CHECK constraints to fail in some special circumstances.
It is important that these queries are executed directly before the delete statements are executed
(instead of for example doing it in DeleteEventListener.)
NULL values might be overridden by an update statement.