I can not persist events with JPA

I have been struggling all day with this issue and can’t make any headway. I’ve tried so many different things, but I just get 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:282)
at com.sun.proxy.$Proxy147.persist(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.appendEvents(JpaEventStorageEngine.java:360)
at org.axonframework.eventsourcing.eventstore.AbstractEventStorageEngine.appendEvents(AbstractEventStorageEngine.java:112)
at org.axonframework.eventsourcing.eventstore.AbstractEventStore.prepareCommit(AbstractEventStore.java:64)
at org.axonframework.eventhandling.AbstractEventBus.doWithEvents(AbstractEventBus.java:209)
at org.axonframework.eventhandling.AbstractEventBus.lambda$null$4(AbstractEventBus.java:144)
at org.axonframework.messaging.unitofwork.MessageProcessingContext.notifyHandlers(MessageProcessingContext.java:68)
at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.notifyHandlers(DefaultUnitOfWork.java:91)
at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.changePhase(AbstractUnitOfWork.java:221)
at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commitAsRoot(AbstractUnitOfWork.java:82)
at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commit(AbstractUnitOfWork.java:70)
at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.executeWithResult(DefaultUnitOfWork.java:80)
at org.axonframework.commandhandling.SimpleCommandBus.handle(SimpleCommandBus.java:156)
at org.axonframework.commandhandling.AsynchronousCommandBus.lambda$handle$1(AsynchronousCommandBus.java:83)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

`

My (axon 3.1.1 on Spring Boot 1.5.9) application is so far very simple. It just exposes some REST endpoints for the commands and queries. When I have the following EventStorageEngine Bean definition, everything works great:

`

@Bean
public EventStorageEngine eventStorageEngine() {
return new InMemoryEventStorageEngine();
}

`

So I can develop the the business logic, but I need to persist the events for the real system.

So, this is what my spring boot application looks like:

`

package com.myco;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FX {

public static void main(String[] args) {
SpringApplication.run(FX.class, args);
}
}

`

I have the following application.yml configuration:

`

axon:
amqp:
exchange: FXEvents

spring:
rabbitmq:
username: test
password: password
host: localhost

Query side Mongo Database

data:
mongodb:
host: localhost
port: 27017

Event Store Postgres Database

datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5433/fxpostgres
username: fxpostgres
password: supersecret

jpa:
database: POSTGRESQL
show-sql: true
hibernate:
ddl-auto: create

`

I also have the following axon configuration class:

`

package com.myco;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.axonframework.commandhandling.AsynchronousCommandBus;
import org.axonframework.common.caching.Cache;
import org.axonframework.common.caching.WeakReferenceCache;
import org.axonframework.common.transaction.TransactionManager;
import org.axonframework.eventsourcing.eventstore.EventStore;
import org.axonframework.messaging.Message;
import org.axonframework.messaging.annotation.ParameterResolverFactory;
import org.axonframework.messaging.interceptors.BeanValidationInterceptor;
import org.axonframework.spring.eventsourcing.SpringAggregateSnapshotter;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class AxonConfig {

public static final String EXCHANGE_NAME = “FXEvents”;
public static final String QUEUE_NAME = EXCHANGE_NAME;

@Bean
public BeanValidationInterceptor<Message<?>> beanValidationInteceptor() {
return new BeanValidationInterceptor<>();
}

@Bean(destroyMethod = “shutdown”)
public AsynchronousCommandBus commandBus() {
AsynchronousCommandBus cb = new AsynchronousCommandBus();
cb.registerDispatchInterceptor(beanValidationInteceptor());
return cb;
}

@Bean
public Cache cache() {
return new WeakReferenceCache();
}

@Bean
public SpringAggregateSnapshotter snapshotter(ParameterResolverFactory parameterResolverFactory,
EventStore eventStore, TransactionManager transactionManager) {
Executor executor = Executors.newSingleThreadExecutor();
return new SpringAggregateSnapshotter(eventStore, parameterResolverFactory, executor, transactionManager);
}

@Bean
public Exchange exchange() {
return ExchangeBuilder.fanoutExchange(EXCHANGE_NAME).build();
}

@Bean
public Queue queue() {
return QueueBuilder.durable(QUEUE_NAME).build();
}

@Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(exchange()).with("*").noargs();
}

@Autowired
public void configure(AmqpAdmin admin) {
admin.declareExchange(exchange());
admin.declareQueue(queue());
admin.declareBinding(binding());
}
}

`

That’s all there is to the “application” module. It has dependencies on other modules that implement the commands, queries, and some REST endpoints. The only other “configuration” to speak of exists in the externalized command modules (jar files) is the aggregate root repository configuration that looks like this:

`

package com.myco.pkg.command;

import org.axonframework.commandhandling.model.Repository;
import org.axonframework.common.caching.Cache;
import org.axonframework.eventsourcing.AggregateFactory;
import org.axonframework.eventsourcing.CachingEventSourcingRepository;
import org.axonframework.eventsourcing.EventCountSnapshotTriggerDefinition;
import org.axonframework.eventsourcing.Snapshotter;
import org.axonframework.eventsourcing.eventstore.EventStore;
import org.axonframework.spring.eventsourcing.SpringPrototypeAggregateFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class PkgConfig {

@Bean
@Scope(“prototype”)
public PkgAR newPkg() {
return new PkgAR();
}

@Bean
public AggregateFactory pkgAggregateFactory() {
SpringPrototypeAggregateFactory aggregateFactory = new SpringPrototypeAggregateFactory<>();
aggregateFactory.setPrototypeBeanName(“newPkg”);
return aggregateFactory;
}

@Bean
public Repository pkgRepository(EventStore eventStore, Cache cache, Snapshotter snapshotter) {
CachingEventSourcingRepository repository = new CachingEventSourcingRepository<>(
pkgAggregateFactory(), eventStore, cache, new EventCountSnapshotTriggerDefinition(snapshotter, 50));
return repository;
}
}

`

Finally, there is a strange bit regarding the JPA persistence unit (persistence.xml). I initially started with the sample from the docs (https://docs.axonframework.org/part3/repositories-and-event-stores.html#jpaeventstorageengine):

`

<?xml version="1.0" encoding="UTF-8"?> org.axonframework.eventsourcing.eventstore.jpa.DomainEventEntry org.axonframework.eventsourcing.eventstore.jpa.SnapshotEventEntry

`

However, at my application wouldn’t startup. It complained about the following unmapped entities (first one, which I fixed by adding a entry to the persistence unit above, then the other):

  • org.axonframework.eventhandling.saga.repository.jpa.SagaEntry
  • org.axonframework.eventhandling.saga.repository.jpa.AssociationValueEntry

However, now my application doesn’t care what is in that xml file or if it is even there at all. Regardless of persistence unit configuration that I provide, at startup the Hibernate DDL stuff (which I have set to automatically create the schema via: spring.jpa.hibernate.ddl-auto: create) executes to create the event store schema, and it outputs the following to the logs:

`
INFO LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit ‘default’

`

Why does it say “persistence unit ‘default’”? The name of the persistence unit in my application’s persistence.xml is “eventStore”.

Anyone know what’s going on here?

Hi,

the problem is that there is no transaction running. Since you’re defining a Command Bus instance explicitly, you also need to configure the transaction manager on it:

@Bean(destroyMethod = “shutdown”)
public AsynchronousCommandBus commandBus(TransactionManager transactionManager) {
AsynchronousCommandBus cb = new AsynchronousCommandBus();
cb.registerDispatchInterceptor(beanValidationInteceptor());
cb.registerHandlerInterceptor(new TransactionManagingInterceptor(transactionManager));
return cb;
}

Cheers,

Allard

OMG! Thank you!

I have a couple other questions too:

  1. What is the story with src/main/resources/META-INF/persistence.xml? It doesn’t seem to be needed. When I don’t include this file in the application, everything works (that is, now that my command bus has a registered TransactionManagingInterceptor). When I include this file, and the persistence-unit name is “eventStore” (as is documented here: https://docs.axonframework.org/part3/repositories-and-event-stores.html#jpaeventstorageengine) it doesn’t matter what the contents of the file are, it simply doesn’t seem to be picked up. However, when I change the name of the persistence-unit to “default”, the application fails to start until I have mapped the following classes: o.a.e.e.j.DomainEventEntry, o.a.e.e.j.SnapshotEventEntry, o.a.e.s.r.j.SagaEntry and o.a.e.s.r.j.AssociationValueEntry.

  2. When I specify the the spring application property that’s hibernate to just create the schema (spring.jpa.hibernate.ddl-auto: create) it both drops the table and then re-creates them. That is, it behaves as though I had specified create-drop. Is this known/expected behavior?
    Cheers,

Troy

Hi Troy,

both the topics you mention are dealt with by Spring Boot. Axon doesn’t do anything, other than just declare some entities and telling Spring Boot to pick them up.
I haven’t written/used a persistence.xml in ages…
Regarding item 2: create is supposed to create a fresh schema, removing any data if present. Create-drop will also cause the schemas to be deleted on shutdown.

Cheers,

Allard