No EntityManager with actual transaction

Hello, i’m using axon 4.0.3 + Spring Boot 2 default configuration.

Having published event to the EventStore and waiting for it to be catched by @SagaEventHandler I received the following exception:

javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process ‘persist’ call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:292) ~[spring-orm-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at com.sun.proxy.$Proxy104.persist(Unknown Source) ~[na:na]
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:1.8.0_191]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_191]
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_191]
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:1.8.0_191]
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_191]
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) ~[na:1.8.0_191]
at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.appendEvents(JpaEventStorageEngine.java:276) ~[axon-eventsourcing-4.0.3.jar:4.0.3]
at org.axonframework.eventsourcing.eventstore.AbstractEventStorageEngine.appendEvents(AbstractEventStorageEngine.java:98) ~[axon-eventsourcing-4.0.3.jar:4.0.3]

What additional configuration is required for the EventStore to handle this case.

PS. Adding @Transactional on the method solves this problem, but i dont understand why this is neccessary.

My code (the following endpoint 127.0.0.1:8080/1 is worked but the other one 127.0.0.1:8080/1 is not)

@SpringBootApplication
class TestAxonApplication

class UserId(val userId: String = IdentifierFactory.getInstance().generateIdentifier()) : Serializable

class TestCommand(@TargetAggregateIdentifier val userId: UserId)

class TestedEvent(val userId: UserId)

fun main(args: Array<String>) {
    runApplication<TestAxonApplication>(*args)
}

@RestController
@RequestMapping
class Controller(var commandGateway: CommandGateway, var eventStore: EventStore) {

    @GetMapping("/1")
    fun done(): UserId? {
        return commandGateway.sendAndWait<UserId>(TestCommand(UserId()))
    }

    @GetMapping("/2")
    fun failure() {
        eventStore.publish(
                GenericEventMessage.asEventMessage<Void>(
                        TestedEvent(UserId())
                )
        )
    }

}

@Aggregate
class User() {

    @AggregateIdentifier
    private lateinit var userId: UserId

    @CommandHandler
    constructor(cmd: TestCommand) : this() {
        AggregateLifecycle.apply(TestedEvent(cmd.userId))
    }

    @EventHandler
    fun on(event: TestedEvent) {
        this.userId = event.userId
    }

}

@Saga
@ProcessingGroup("mySaga")
class MySaga {

    @StartSaga
    @SagaEventHandler(associationProperty = "userId")
    fun start(event: TestedEvent) {
        println("DONE ${event.userId.userId}")
    }

}

Hi Sergey,

We noticed this issue of yours and pin pointed this to be an issue with the JpaEventStorageEngine.

Sadly, the JpaEventStorageEngine wasn’t performing the appendEvents(List<Object>) operation within a transaction of the configured TransactionManager.
This issue has been described by Allard here and resolved by me here.

The resolution is in Axon Framework release 4.1.1, so if you’d like to leverage our fix, you’d have to update your version.
If you update your version to 4.1.1, you’ll also be able to use the EventGateway instead of the EventBus directly to publish messages, as it gives you a cleaner API to do so.

If that’s not an option, I think you can either (1) add @Transactional to the method where you’re publishing the event or (2) pull in the TransactionManager in that controller yourself to execute the given operation in a Transaction.

Hope this helps!

Cheers,
Steven