When I use a command to add a new mask to the Unit the MaskAddedEvent is applied and the above UnitEntryListener is executed and successfully calls the UnitEntry.addMask method. Even with a freshly created UnitEntry and I were to break at the UserEntry.addMask method then I would see that the masks class attribute would be a Hibernate PersistentSortedSet.
However when I replay the cluster for this listener I receive a NullPointerException on the UserEntry.addMask method. I have tried wrapping the call to startReplay() in a Spring transaction, I’ve added @Transactional to all methods in the listener, set the axon:replay-config commit-threshold to 1. I’m at a loss as to why replaying doesn’t set the UnitEntity.masks to a PersistentSortedSet when running it from a command will.
given you’re doing a replay, it’s likely that there are no ‘masks’ to start with. I don’t know how Hibernate behaves when the are no entries to put in the set.
What I do see in the documentation, is that they recommend initializing collections in the class itself. Hibernate will (when loading instances) replace the default implementation with a Hibernate specific one (when needed).
Your code does contain initializing logic for the masks field. That makes this a very strange issue. Axon’s replay logic already creates the necessary transactions for you, so you don’t need to worry about that. In fact, it can be in your way if you wrap the logic in extra transactions (since the actual commit may be postponed for longer than you’d want it to).
Perhaps you can use a field breakpoint to find out when the field’s value is set to null?
I tried as you suggested but the breakpoint was never triggered.
I am initalising the attribute in the constructor so I put my breakpoint into the default constructor and I discovered strange behaviour again. I cleaned the database to start from scratch.
The first test is to capture when a command to add a mask has been dispatched:
breakpoint added to UnitEntry constructor and addMask method
dispatch command to add mask
IntelliJ breaks on the constructor and the attribute is initialised; pressed F9 to continue running
IntelliJ breaks on the addMask method and the attribute is now a PersistedSortedSet
the addMask method works as desired and is properly persisted
The second test was to run the replay:
breakpoint added to the UnitEntry constructor and addMask method
execute the startReplay on the cluster
IntelliJ did NOT break in the constructor
IntelliJ breaks on the addMask method and the attribute is null
Dispatching a command results in the execution of the default constructor of UnitEntry yet it is not called when replaying. There are no other event listeners in the application that update the UnitEntry.
My only work around now is to separate the masks from UnitEntry by creating another JPA entity and Spring-Data repository. The listener has been updated to use this new repository and I have updated the beforeReplay method to delete all entities from this table and flush.
I’ve built an integration test to see what happens, but I cannot reproduce the problem. Does your UnitEntry class have any other constructors? How do you initialize the UnitEntry on the very first event?
There is another constructor that takes unchangeable identifiers. However the first line in that constructor is to call this(). I did not mention earlier that I even tried setting the class attributes to “newTreeSet()” in the definition, ie: “private SortedSet masks = newTreeSet();”.
I have never had this problem with Hibernate and it is odd that it only happens during replay and not during command execution.
I have committed my code changes using separate entities. Spring-Data repositories are cheap.