public interface UnitOfWork extends MetaInfoHolder, AutoCloseable
A UnitOfWork allows you to access Entities and work with them. All modifications to Entities are recorded by the UnitOfWork, and at the end they may be sent to the underlying EntityStore by calling complete(). If the UoW was read-only you may instead simply discard() it.
A UoW differs from a traditional Transaction in the sense that it is not tied at all to the underlying storage resource. Because of this there is no timeout on a UoW. It can be very short or very long. Another difference is that if a call to complete() fails, and the cause is validation errors in the Entities of the UoW, then these can be corrected and the UoW retried. By contrast, when a Transaction commit fails, then the whole transaction has to be done from the beginning again.
A UoW can be associated with a Usecase. A Usecase describes the metainformation about the process to be performed by the UoW.
If a code block that uses a UoW throws an exception you need to ensure that this is handled properly, and that the UoW is closed before returning. Because discard() is a no-op if the UoW is closed, we therefore recommend the following template to be used:
UnitOfWork uow = module.newUnitOfWork(); try { ... uow.complete(); } finally { uow.discard(); }
This ensures that in the happy case the UoW is completed, and if any exception is thrown the UoW is discarded. After the UoW has completed the discard() method doesn't do anything, and so has no effect. You can choose to either add catch blocks for any exceptions, including exceptions from complete(), or skip them.
Since 2.1 you can leverage Java 7 Automatic Resource Management (ie. Try With Resources) and use the following template instead:
try( UnitOfWork uow = module.newUnitOfWork() ) { ... uow.complete(); }
It has the very same effect as the template above but is shorter.
Modifier and Type | Method and Description |
---|---|
void |
addUnitOfWorkCallback(UnitOfWorkCallback callback)
Register a callback.
|
void |
close()
Discard this UnitOfWork.
|
void |
complete()
Complete this UnitOfWork.
|
Instant |
currentTime()
Current Time is a relative concept in systems capable of event sourcing and other
history-capable systems.
|
void |
discard()
Discard this UnitOfWork.
|
<T> T |
get(Class<T> type,
Identity identity)
Find an Entity of the given mixin type with the give reference.
|
<T> T |
get(T entity)
If you have a reference to an Entity from another
UnitOfWork and want to create a reference to it in this
UnitOfWork, then call this method.
|
boolean |
isOpen()
Check if the UnitOfWork is open.
|
boolean |
isPaused()
Check if the UnitOfWork is paused.
|
ModuleDescriptor |
module()
The Module of the UnitOfWork is defined as the Module the UnitOfWorkFactory belonged to from where the
UnitOfWork was created.
|
<T> T |
newEntity(Class<T> type)
Create a new Entity which implements the given mixin type.
|
<T> T |
newEntity(Class<T> type,
Identity identity)
Create a new Entity which implements the given mixin type.
|
<T> EntityBuilder<T> |
newEntityBuilder(Class<T> type)
Create a new EntityBuilder for an EntityComposite which implements the given mixin type.
|
<T> EntityBuilder<T> |
newEntityBuilder(Class<T> type,
Identity identity)
Create a new EntityBuilder for an EntityComposite which implements the given mixin type.
|
<T> EntityBuilder<T> |
newEntityBuilderWithState(Class<T> type,
Function<PropertyDescriptor,Object> propertyFunction,
Function<AssociationDescriptor,EntityReference> associationFunction,
Function<AssociationDescriptor,Stream<EntityReference>> manyAssociationFunction,
Function<AssociationDescriptor,Stream<Map.Entry<String,EntityReference>>> namedAssociationFunction)
Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given
state.
|
<T> EntityBuilder<T> |
newEntityBuilderWithState(Class<T> type,
Identity identity,
Function<PropertyDescriptor,Object> propertyFunction,
Function<AssociationDescriptor,EntityReference> associationFunction,
Function<AssociationDescriptor,Stream<EntityReference>> manyAssociationFunction,
Function<AssociationDescriptor,Stream<Map.Entry<String,EntityReference>>> namedAssociationFunction)
Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given
state.
|
<T> Query<T> |
newQuery(QueryBuilder<T> queryBuilder)
|
void |
pause()
Pauses this UnitOfWork.
|
void |
remove(Object entity)
Remove the given Entity.
|
void |
removeUnitOfWorkCallback(UnitOfWorkCallback callback)
Unregister a callback.
|
void |
resume()
Resumes this UnitOfWork to again become the current UnitOfWork.
|
void |
setMetaInfo(Object metaInfo)
Sets an arbitrary metaInfo object on this
UnitOfWork which can be used for application-specific
information. |
<T extends HasIdentity> |
toEntity(Class<T> primaryType,
T valueComposite)
Converts the provided Value to an Entity of the same type.
|
<T extends HasIdentity> |
toValue(Class<T> primaryType,
T entityComposite)
Converts the provided Entity to a Value of the same type.
|
<T extends HasIdentity> |
toValueList(ManyAssociation<T> association)
Converts all the entities referenced in the ManyAssociation into a List of values of the same type.
|
<T extends HasIdentity> |
toValueMap(NamedAssociation<T> association)
Converts the
NamedAssociation into a Map with a String key and a ValueComposite as the value. |
<T extends HasIdentity> |
toValueSet(ManyAssociation<T> association)
Converts all the entities referenced in the ManyAssociation into a Set of values of the same type.
|
UnitOfWorkFactory |
unitOfWorkFactory()
Get the UnitOfWorkFactory that this UnitOfWork was created from.
|
Usecase |
usecase()
Get the Usecase for this UnitOfWork
|
metaInfo
UnitOfWorkFactory unitOfWorkFactory()
Instant currentTime()
UnitOfWorkFactory.newUnitOfWork(Instant)
Usecase usecase()
void setMetaInfo(Object metaInfo)
UnitOfWork
which can be used for application-specific
information.
The metaInfo object is retrieved by the MetaInfoHolder.metaInfo(Class)
method on the same UnitOfWork
instance.
metaInfo
- The metaInfo object that can be retrieved with MetaInfoHolder.metaInfo(Class)
.<T> Query<T> newQuery(QueryBuilder<T> queryBuilder)
T
- The resulting type of the query.queryBuilder
- The QueryBuilder holding the query specification/expressionUnitOfWork
<T> T newEntity(Class<T> type) throws NoSuchEntityTypeException, AmbiguousTypeException, LifecycleException
An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and Layers will be considered. If several EntityComposites implement the type then an AmbiguousTypeException will be thrown.
The reference of the Entity will be generated by the IdentityGenerator of the Module of the EntityComposite.
T
- Entity typetype
- the mixin type that the EntityComposite must implementNoSuchEntityTypeException
- if no EntityComposite type of the given mixin type has been registeredAmbiguousTypeException
- If several mixins implement the given typeLifecycleException
- if the entity cannot be created<T> T newEntity(Class<T> type, @Optional Identity identity) throws NoSuchEntityTypeException, AmbiguousTypeException, LifecycleException
T
- Entity typetype
- the mixin type that the EntityComposite must implementidentity
- the reference of the new EntityNoSuchEntityTypeException
- if no EntityComposite type of the given mixin type has been registeredAmbiguousTypeException
- If several mixins implement the given typeLifecycleException
- if the entity cannot be created<T> EntityBuilder<T> newEntityBuilder(Class<T> type) throws NoSuchEntityTypeException, AmbiguousTypeException
T
- Entity typetype
- the mixin type that the EntityComposite must implementNoSuchEntityTypeException
- if no EntityComposite type of the given mixin type has been registeredAmbiguousTypeException
- If several mixins implement the given type<T> EntityBuilder<T> newEntityBuilder(Class<T> type, @Optional Identity identity) throws NoSuchEntityTypeException, AmbiguousTypeException
T
- Entity typetype
- the mixin type that the EntityComposite must implementidentity
- the reference of the new EntityNoSuchEntityTypeException
- if no EntityComposite type of the given mixin type has been registeredAmbiguousTypeException
- If several mixins implement the given type<T> EntityBuilder<T> newEntityBuilderWithState(Class<T> type, Function<PropertyDescriptor,Object> propertyFunction, Function<AssociationDescriptor,EntityReference> associationFunction, Function<AssociationDescriptor,Stream<EntityReference>> manyAssociationFunction, Function<AssociationDescriptor,Stream<Map.Entry<String,EntityReference>>> namedAssociationFunction) throws NoSuchEntityTypeException, AmbiguousTypeException
An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and Layers will be considered.
T
- Entity typetype
- Entity typepropertyFunction
- a function providing the state of propertiesassociationFunction
- a function providing the state of associationsmanyAssociationFunction
- a function providing the state of many associationsnamedAssociationFunction
- a function providing the state of named associationsNoSuchEntityTypeException
- if no EntityComposite type of the given mixin type has been registeredAmbiguousTypeException
- If several mixins implement the given type<T> EntityBuilder<T> newEntityBuilderWithState(Class<T> type, @Optional Identity identity, Function<PropertyDescriptor,Object> propertyFunction, Function<AssociationDescriptor,EntityReference> associationFunction, Function<AssociationDescriptor,Stream<EntityReference>> manyAssociationFunction, Function<AssociationDescriptor,Stream<Map.Entry<String,EntityReference>>> namedAssociationFunction) throws NoSuchEntityTypeException, AmbiguousTypeException
An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and Layers will be considered.
T
- Entity typetype
- Entity typeidentity
- the reference of the new EntitypropertyFunction
- a function providing the state of propertiesassociationFunction
- a function providing the state of associationsmanyAssociationFunction
- a function providing the state of many associationsnamedAssociationFunction
- a function providing the state of named associationsNoSuchEntityTypeException
- If no mixins implements the given typeAmbiguousTypeException
- If several mixins implement the given type<T> T get(Class<T> type, Identity identity) throws NoSuchEntityTypeException, NoSuchEntityException
T
- Entity typetype
- of the entityidentity
- of the entityNoSuchEntityTypeException
- if no entity type could be foundNoSuchEntityException
- if the entity could not be found<T> T get(T entity) throws NoSuchEntityTypeException
T
- Entity typeentity
- the Entity to be dereferencedNoSuchEntityTypeException
- if no entity type could be foundvoid remove(Object entity) throws LifecycleException
entity
- the Entity to be removed.LifecycleException
- if the entity could not be removedvoid complete() throws UnitOfWorkCompletionException, ConcurrentEntityModificationException
UnitOfWorkCompletionException
- if the UnitOfWork could not be completedConcurrentEntityModificationException
- if entities have been modified by othersvoid discard()
void close()
discard()
method and is an
implementation of the AutoCloseable
interface providing Try With Resources
support for UnitOfWork.close
in interface AutoCloseable
boolean isOpen()
boolean isPaused()
pause()
and then resumed by calling
resume()
.void pause()
Calling this method will cause the underlying UnitOfWork to become the current UnitOfWork until the the resume() method is called. It is the client's responsibility not to drop the reference to this UnitOfWork while being paused.
void resume()
void addUnitOfWorkCallback(UnitOfWorkCallback callback)
callback
- a callback to be registered with this UnitOfWorkvoid removeUnitOfWorkCallback(UnitOfWorkCallback callback)
callback
- a callback to be unregistered with this UnitOfWork<T extends HasIdentity> T toValue(Class<T> primaryType, T entityComposite)
All Property values are transferred across as-is, and the Association, ManyAssociation and NamedAssociatino values are kept in the ValueComposite as EntityReferences until they are dereferenced (get() and other methods), and IF a UnitOfWork is present at dereferencing the corresponding EntityCompoiste is retrieved from the EntityStore. If there is not an UnitOfWork present, an exception is thrown.
For this to work, the Composites (both Entity and Value) must not declare the EntityComposite and ValueComposite super types, but rely on the declaration in the assembly, and also extend the Identity supertype.
Example;
public interface Person extends Identity { ... };
public class MyAssembler
{
public void assemble( ModuleAssembly module )
{
module.values( Person.class );
module.entities( Person.class );
}
}
T
- The generic shared typeprimaryType
- The shared type for which the properties and associations will
be converted. Properties outside this type will be ignored.entityComposite
- The entity to be convered.<T extends HasIdentity> List<T> toValueList(ManyAssociation<T> association)
All the referenced entities inside the association will be fetched from the underlying entity store,
which is potentially very expensive operation. Each of the fetched entities will be passed to
toValue(Class, HasIdentity)
, and its associations will NOT be converted into values, but remain
EntityReference
values. Hence there is no problem with circular references.
For this to work, the type <T> must be registered at bootstrap as both an Entity and a Value, and
as seen in the method signature, also be sub-type of HasIdentity
.
T
- The primary type of the association.association
- The association of entities to be converted into values.toValue(Class, HasIdentity)
<T extends HasIdentity> Set<T> toValueSet(ManyAssociation<T> association)
All the referenced entities inside the association will be fetched from the underlying entity store,
which is potentially very expensive operation. However, any duplicate EntityReferences in the association
will be dropped before the fetch occurs. Each of the fetched entities will be passed to
toValue(Class, HasIdentity)
, and its associations will NOT be converted into values, but remain
EntityReference
values. Hence there is no problem with circular references.
For this to work, the type <T> must be registered at bootstrap as both an Entity and a Value, and
as seen in the method signature, also be sub-type of HasIdentity
.
T
- The primary type of the association.association
- The association of entities to be converted into values.toValue(Class, HasIdentity)
<T extends HasIdentity> Map<String,T> toValueMap(NamedAssociation<T> association)
NamedAssociation
into a Map with a String key and a ValueComposite as the value.
A NamedAssociation
is effectively a Map with a String key and an EntityReference as the value. The
EntityReference is fetched from the entity store and converted into a value of the same type.Each of the fetched
entities will be passed to toValue(Class, HasIdentity)
, and its associations will NOT be converted into
values, but remain EntityReference
values. Hence there is no problem with circular references.
For this to work, the type <T> must be registered at bootstrap as both an Entity and a Value, and
as seen in the method signature, also be sub-type of HasIdentity
.
T
- The primary type of the association.association
- The association of entities to be converted into values.toValue(Class, HasIdentity)
<T extends HasIdentity> T toEntity(Class<T> primaryType, T valueComposite)
All Property values are transferred across as-is (no deep copy in case mutable types (DISCOURAGED!) are used), and the Association, ManyAssociation and NamedAssociatino that were in the ValueComposite as EntityReferences are transferred into the EntityComposite correctly, and can be dereferenced.
This method MUST be called within a UnitOfWork.
If an Entity with the Identity in the ValueComposite already exists, then that Entity is updated with the values from the ValueComposite. If an Entity of that Identity doesn't exist a new one is created.
For this to work, the Composites (both Entity and Value) must not declare the EntityComposite and ValueComposite super types, but rely on the declaration in the assembly, and also extend the Identity supertype.
Example;
public interface Person extends Identity { ... };
public class MyAssembler
{
public void assemble( ModuleAssembly module )
{
module.values( Person.class );
module.entities( Person.class );
}
}
T
- The generic shared typeprimaryType
- The shared type for which the properties and associations will
be converted. Properties outside this type will be ignored.valueComposite
- The Value to be convered into an Entity.ModuleDescriptor module()