java.lang.Object
org.axonframework.modelling.saga.repository.jpa.JpaSagaStore
All Implemented Interfaces:
SagaStore<Object>

public class JpaSagaStore extends Object implements SagaStore<Object>
JPA implementation of the SagaStore. It uses an EntityManager to persist the actual saga in a backing store in serialized form.

After each operation that modified the backing store, EntityManager.flush() is invoked to ensure the store contains the last modifications. To override this behavior, see setUseExplicitFlush(boolean)

This store manages no transaction of its own. It takes an EntityManager from the EntityManagerProvider it was given, so its changes commit together with whatever else the surrounding transaction covers. Give it the same provider instance the transaction manager was given, and use a provider that resolves the EntityManager of the calling thread's transaction on every call, as a container-managed shared-EntityManager proxy does. A fixed SimpleEntityManagerProvider is suitable only where a single thread is involved, such as in a test.

Such a provider finds the transaction by thread, so the store has to be called on the thread that began it. A TransactionManager declaring requiresSameThreadInvocations() arranges that, and the ones shipped for Spring and JPA do. A custom transaction manager that leaves that method at its default of false does not, and neither does handler code that moves work onto a thread of its own.

Saga handling should run inside a transaction. JPA enforces that itself: without an active one these operations fail rather than write partial state.

The saga type column holds the saga class name and is matched against it. Axon Framework 4 derived both through its Serializer, which for the default configuration also produced the class name, so an existing table reads and writes unchanged. An application that mapped its saga classes to some other type name, an XStream alias being the usual way, has rows this store cannot route to and must rewrite that column to the class name first.

The saga state itself is converted with the configured Converter. Axon Framework 4 defaulted to the XStreamSerializer unless the application configured another serializer, so the stored state of such an application is XStream XML, which a JSON-based converter cannot read: loading such a saga fails. Give this store an XStreamConverter if you are migrating such a scenario to read that state and to write it back in the same form, which also lets Axon Framework 4 nodes keep handling the same sagas during a rolling upgrade.

Since:
3.0
Author:
Allard Buijze
  • Constructor Details

  • Method Details

    • builder

      public static JpaSagaStore.Builder builder()
      Instantiate a Builder to be able to create a JpaSagaStore.

      The EntityManagerProvider and Converter are hard requirements and as such should be provided.

      Returns:
      a Builder to be able to create a JpaSagaStore
    • loadSaga

      public <S> @Nullable SagaStore.Entry<S> loadSaga(Class<S> sagaType, String sagaIdentifier)
      Description copied from interface: SagaStore
      Loads a known saga SagaStore.Entry instance with given sagaType and unique sagaIdentifier.

      Due to the concurrent nature of Sagas, it is not unlikely for a Saga to have ceased to exist after it has been found based on associations. Therefore, a repository should return null in case a Saga doesn't exists, as opposed to throwing an exception.

      Specified by:
      loadSaga in interface SagaStore<Object>
      Type Parameters:
      S - the specific saga type returned
      Parameters:
      sagaType - The type of the returned saga entry
      sagaIdentifier - The unique identifier of the returned saga entry
      Returns:
      The saga entry, or null if no such saga exists
    • loadAssociationValues

      protected Set<AssociationValue> loadAssociationValues(jakarta.persistence.EntityManager entityManager, Class<?> sagaType, String sagaIdentifier)
      Loads the association values of the saga with given sagaIdentifier and sagaType.
      Parameters:
      entityManager - the entity manager instance to use for the query
      sagaType - the saga instance class
      sagaIdentifier - the saga identifier
      Returns:
      the associations of the given saga
    • removeAssociationValue

      protected void removeAssociationValue(jakarta.persistence.EntityManager entityManager, Class<?> sagaType, String sagaIdentifier, AssociationValue associationValue)
      Removes the given associationValue of the saga with given sagaIdentifier and sagaType.
      Parameters:
      entityManager - the entity manager instance to use for the query
      sagaType - the saga instance class
      sagaIdentifier - the saga identifier
      associationValue - the association value to remove
    • storeAssociationValue

      protected void storeAssociationValue(jakarta.persistence.EntityManager entityManager, Class<?> sagaType, String sagaIdentifier, AssociationValue associationValue)
      Stores the given associationValue of the saga with given sagaIdentifier and sagaType.
      Parameters:
      entityManager - the entity manager instance to use for the query
      sagaType - the saga instance class
      sagaIdentifier - the saga identifier
      associationValue - the association value to add
    • findSagas

      public Set<String> findSagas(Class<?> sagaType, AssociationValue associationValue)
      Description copied from interface: SagaStore
      Returns identifiers of saga instances of the given sagaType that have been associated with the given associationValue.
      Specified by:
      findSagas in interface SagaStore<Object>
      Parameters:
      sagaType - The type of the returned sagas
      associationValue - The value that the returned sagas must be associated with
      Returns:
      A set of identifiers of sagas having the correct type and association value
    • deleteSaga

      public void deleteSaga(Class<?> sagaType, String sagaIdentifier, Set<AssociationValue> associationValues)
      Description copied from interface: SagaStore
      Deletes a Saga with given sagaType and sagaIdentifier and all its associations. For convenience all known association values are passed along as well, which has the advantage that the saga store is not required to keep an index of association value to saga identifier.
      Specified by:
      deleteSaga in interface SagaStore<Object>
      Parameters:
      sagaType - The type of saga to delete
      sagaIdentifier - The identifier of the saga to delete
      associationValues - The known associations of the saga
    • updateSaga

      public void updateSaga(Class<?> sagaType, String sagaIdentifier, Object saga, AssociationValues associationValues)
      Description copied from interface: SagaStore
      Updates a given Saga after its state was modified, applying the association values added and removed since it was last stored.
      Specified by:
      updateSaga in interface SagaStore<Object>
      Parameters:
      sagaType - The type of the Saga
      sagaIdentifier - The identifier of the Saga
      saga - The Saga instance
      associationValues - The association values of the Saga, carrying the additions and removals to apply
    • insertSaga

      public void insertSaga(Class<?> sagaType, String sagaIdentifier, Object saga, Set<AssociationValue> associationValues)
      Description copied from interface: SagaStore
      Adds a new Saga and its initial association values to the store.
      Specified by:
      insertSaga in interface SagaStore<Object>
      Parameters:
      sagaType - The type of the Saga
      sagaIdentifier - The identifier of the Saga
      saga - The Saga instance
      associationValues - The initial association values of the Saga
    • setUseExplicitFlush

      public void setUseExplicitFlush(boolean useExplicitFlush)
      Sets whether or not to do an explicit EntityManager.flush() after each data modifying operation on the backing storage. Default to true
      Parameters:
      useExplicitFlush - true to force flush, false otherwise.
    • createSagaEntry

      protected SagaEntry<?> createSagaEntry(Object saga, String sagaIdentifier, Converter converter)
      Intended for clients to override. Defaults to SagaEntry.
      Parameters:
      saga - The Saga instance
      sagaIdentifier - The identifier of the Saga
      converter - the converter used to convert the saga to the form stored in SagaEntry.getSerializedSaga()
      Returns:
      An instanceof @SagaEntry
    • sagaEntryEntityName

      protected String sagaEntryEntityName()
      Intended for clients to override. Defaults to 'SagaEntry'.
      Returns:
      the name of the Jpa event entity