Using JacksonSerializer to store event payloads

Hi All,

I’m trying to setup a brand new Spring Boot application with Axon 3.0-RC1 and I noticed that by default the event payload is stored as a byte array in the database. I’d like to store it as Json instead. After reading the Axon 3 docs it seems to me I should use the JacksonSerializer and, probably, override DomainEventEntry to have the payload field as a String (instead of a byte array).

I tried to override the serializer implementation by defining a @Configuration class with a “serializer” @Bean method returning an instance of JacksonSerializer. That alone didn’t change anything, but I also tried to add a copy of the DomainEventEntry class and JpaEventStorageEngine using Sting instead of byte[] as the parameterized type. Still not able to get my payloads to be stored in Json.

Has anyone done that before in Axon 3? What’s is the recommended way to accomplish that?

Thank you,
-v

Hi V,

Firstly, you could instead of using 3.0-RC1 move to using 3.0.5, which definitely has benefits over RC1.

Second, in a Spring Boot Axon app on 3.0.5, you can add a @Configuration annotated class and add two @Bean functions:

  1. The JacksonSerializer
  2. A JpaEventStorageEngine which uses the JacksonSerializer

That is how I’ve got it working right now on the project I’m on (running 3.0.4).

The fact it’s not working for you I think is because you’re using release candidate 1.
You shouldn’t have to override the DomainEventEntry class either if I’m correct, not for this at least.

Hope this helps!

Cheers,

Steven

Hey Steven,

I followed your advice and upgraded to 3.0.5 without serialization changes, just to make sure if things would still work. Unfortunately, in 3.0.5 my app breaks because of

org.springframework.amqp.AmqpTimeoutException: java.util.concurrent.TimeoutException

I’m using RabbitMQ and following the Spring Boot autoconfig instructions from Axon 3 docs. Nothing different than that. With 3.0-RC1 it works just fine, I can startup the app and publish messages successfully.

Do you have any idea on what should be changed in regard to the AMQP config from 3.0-RC1 to 3.0.5?

Thank you,
-v

Hi,

a lot of things have changed since 3.0-RC1, and I’m pretty sure some AMQP related things were also affected. But not to the extent that you should get a TimeoutException.

Could you share the stacktrace? That will give a bit more context on where the exception comes from.

Do you use the axon-spring-boot-starter and axon-amqp modules?

Cheers,

Allard

@Allard,

I’ve worked around the timeout by removing/re-creating all exchanges and queues. Don’t know exactly the root cause, but the good thing is I managed to have my app migrated successfully to 3.0.5.

@Steven,

As far as the JacksonSerializer, I’m still not able to make it work. This is my configuration class:

`
@Configuration
public class Axon {

@Autowired
DataSource dataSource;

@Autowired
EntityManagerProvider entityManagerProvider;

@Autowired
TransactionManager transactionManager;

@Bean
Serializer serializer() {
return new JacksonSerializer();
}

@Bean
JpaEventStorageEngine jpaEventStorageEngine() throws SQLException {
return new JpaEventStorageEngine(serializer(), null, dataSource, entityManagerProvider, transactionManager);
}
}
`

And this is how event payloads are serialized in the domain_event_entry table (H2 embedded).

It is still a byte array. I was expecting a Jjson hashmap with the actual data. Is there any other thing you think I could try?

Thanks,
-v

BTW, here is the complete code: https://github.com/vvgomes/event-driven-restaurant

Hi Vinicius,

I was lacking a bit of knowledge here, so I asked Allard for some more specifics.
Being a bit more wiser now, I believe you’ve got two options:

  1. Although it’s stored as a byte[] by default, you’re not necessarily tied to a byte[] type column. You should actually be able to specify the payload column to be something different, like blob or text, and it should be picked up automatically.

  2. As you suggested you are able to provide your own DomainEventEntry with another type for storage. To make it so it’s used, you’d however also have to provide your own JpaEventStorageEngine, where you override the ‘String domainEventEntryEntityName()’ function to return the DomainEventEntry you want.

I’m guessing option one is the least amount of work.
If that doesn’t do the trick I suggest trying option 2.

Hope this helps!

Cheers,
Steven

Hey Steven & Allard,

I may be missing something… Still not able to accomplish Json serialization.

So, I added my own CustomDomainEventEntry mapping to the “domain_event_entry” table and using String instead of byte[] as the parameterized type. Look:
`

@Entity
@Table(name = “domain_event_entry”, indexes = @Index(columnList = “aggregateIdentifier,sequenceNumber”, unique = true))
public class CustomDomainEventEntry extends AbstractSequencedDomainEventEntry {
//…
}
`

Besides that, I also defined a CustomJpaEventStorageEngine that uses my CustomDomainEventEntry by overriding the following methods:

`
public class CustomJpaEventStorageEngine extends JpaEventStorageEngine {

//…

@Override
protected String domainEventEntryEntityName() {
return CustomDomainEventEntry.class.getSimpleName();
}

@Override
protected Object createEventEntity(EventMessage<?> eventMessage, Serializer serializer) {
return new CustomDomainEventEntry(asDomainEventMessage(eventMessage), serializer);
}
}

`

Additionally, I added a configuration class to tell my app to use my custom stuff. Take a look:

`
@Configuration
public class Axon {

@Autowired
EntityManagerProvider entityManagerProvider;

@Bean
public JpaEventStorageEngine jpaEventStorageEngine() {
return new CustomJpaEventStorageEngine(
entityManagerProvider,
NoTransactionManager.INSTANCE
);
}
}
`

As a result, I’m still getting an error:

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: SQL strings added more than once for: domain_event_entry

It seems that both the default configuration using the original entity and my custom entity are being loaded by the Spring Boot app. How could I make it see only my custom stuff?

And BTW, do I really need to add that much extra code and spend that much time debugging my very simple Axon app just to make it serialize payloads as Json? Either I’m really missing a point or this is not something the framework is expecting a user to wish.

Thanks by the support anyways.

-v

Hi Vinicius,

I think two things are being mixed up here. To serialize to JSON, configuring a JacksonSerializer is enough to get that done. It will serialize objects to json, and then store them in the event store.

However, if you also want your database to recognize the field in the table as JSON, that’s when you’ll need to change the entities that Axon uses to store each entry.

Hope this helps.
Cheers,

Allard