Class GenericJpaRepository<T>

Type Parameters:
T - The type of aggregate the repository provides access to
All Implemented Interfaces:
ScopeAware, Repository<T>

public class GenericJpaRepository<T> extends LockingRepository<T,AnnotatedAggregate<T>>
Generic repository implementation that stores JPA annotated aggregates. These aggregates must have the proper JPA Annotations.

Optionally, the repository may be configured with a locking scheme. The repository will always force optimistic locking in the backing data store. The optional lock in the repository is in addition to this optimistic lock. Note that locks on this repository will not be shared with other repository instances.

When this repository is requested to persist changes to an aggregate, it will also flush the EntityManager, to enforce checking of database constraints and optimistic locks.

By default, this repository implementation will generate sequences for the events published by the aggregate. In doing so, the user is capable of querying the events based on the aggregate identifier and order them based on the aforementioned sequence number. The downside of this, is that even if the stored aggregate is removed, the aggregate identifier cannot be reused. This follows from the uniqueness constraint on the Event Store defining that the combination of aggregate identifier to sequence number should be unique. However, sequence number generation can be disabled, through GenericJpaRepository.Builder.disableSequenceNumberGeneration(). When disabled, published events will no longer hold the sequence number nor the aggregate identifier. As such, the aggregate identifier can be reused (after removal of the previous aggregate referring to that identifier). The obvious downside of this is that the events in the store can no longer be queried based on the aggregate identifier.

Since:
0.7
Author:
Allard Buijze
  • Constructor Details

  • Method Details

    • builder

      public static <T> GenericJpaRepository.Builder<T> builder(Class<T> aggregateType)
      Instantiate a Builder to be able to create a GenericJpaRepository for aggregate type T.

      The LockFactory is defaulted to an NullLockFactory, thus providing no additional locking, the identifierConverter to Function.identity(), the RepositorySpanFactory is defaulted to a DefaultRepositorySpanFactory backed by a NoOpSpanFactory, and sequence number generation is enabled.

      A goal of this Builder goal is to create an AggregateModel specifying generic T as the aggregate type to be stored. All aggregates in this repository must be instanceOf this aggregate type. To instantiate this AggregateModel, either an AggregateModel can be provided directly or an aggregateType of type Class can be used. The latter will internally resolve to an AggregateModel. Thus, either the AggregateModel or the aggregateType should be provided.

      Additionally, the EntityManagerProvider and EventBus are hard requirements and as such should be provided.

      Type Parameters:
      T - The type of aggregate to build the repository for
      Parameters:
      aggregateType - The type of aggregate to build the repository for
      Returns:
      a Builder to be able to create a GenericJpaRepository
    • doLoadWithLock

      protected AnnotatedAggregate<T> doLoadWithLock(String aggregateIdentifier, Long expectedVersion)
      Description copied from class: LockingRepository
      Loads the aggregate with the given aggregateIdentifier. All necessary locks have been obtained.
      Specified by:
      doLoadWithLock in class LockingRepository<T,AnnotatedAggregate<T>>
      Parameters:
      aggregateIdentifier - the identifier of the aggregate to load
      expectedVersion - The expected version of the aggregate to load
      Returns:
      a fully initialized aggregate
    • doCreateNewForLock

      protected AnnotatedAggregate<T> doCreateNewForLock(Callable<T> factoryMethod) throws Exception
      Description copied from class: LockingRepository
      Creates a new aggregate instance using the given factoryMethod. Implementations should assume that this method is only called if a UnitOfWork is currently active.
      Specified by:
      doCreateNewForLock in class LockingRepository<T,AnnotatedAggregate<T>>
      Parameters:
      factoryMethod - The method to create the aggregate's root instance
      Returns:
      an Aggregate instance describing the aggregate's state
      Throws:
      Exception - when the factoryMethod throws an exception
    • doSaveWithLock

      protected void doSaveWithLock(AnnotatedAggregate<T> aggregate)
      Description copied from class: LockingRepository
      Perform the actual saving of the aggregate. All necessary locks have been verified.
      Specified by:
      doSaveWithLock in class LockingRepository<T,AnnotatedAggregate<T>>
      Parameters:
      aggregate - the aggregate to store
    • doDeleteWithLock

      protected void doDeleteWithLock(AnnotatedAggregate<T> aggregate)
      Description copied from class: LockingRepository
      Perform the actual deleting of the aggregate. All necessary locks have been verified.
      Specified by:
      doDeleteWithLock in class LockingRepository<T,AnnotatedAggregate<T>>
      Parameters:
      aggregate - the aggregate to delete
    • setForceFlushOnSave

      public void setForceFlushOnSave(boolean forceFlushOnSave)
      Indicates whether the EntityManager's state should be flushed each time an aggregate is saved. Defaults to true.

      Flushing the EntityManager will force JPA to send state changes to the database. Any key violations and failing optimistic locks will be identified in an early stage.

      Parameters:
      forceFlushOnSave - whether or not to flush the EntityManager after each save. Defaults to true.
      See Also:
      • EntityManager.flush()