Olaru Vlad
Olaru Vlad

Reputation: 277

Persisting ManyToOne/OneToOne relations in Axon aggregate

Cascade types don't seem to work with the entity manager that axon uses. If I use CascadeType.Persist, the second time I try to create an object with the same child I get a duplicate key constraint error. If I use CascadeType.Merge, it won't work if the entries do not exist in the database.

Tried using different CascadeTypes, also the hibernate @Cascade annotations but with no avail. Also tried to use @AggregateMember for the children but the children entities don't seem to be saved before the parent.

@Bean
Repository<OfficialRecordEntity> axonOfficialRecordRepository(EntityManagerProvider emp, ParameterResolverFactory parameterResolverFactory, EventBus eventBus) {
        return GenericJpaRepository.builder(OfficialRecordEntity.class)
                .parameterResolverFactory(parameterResolverFactory)
                .entityManagerProvider(emp)
                .eventBus(eventBus)
                .build();
}
@Aggregate
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"id"}, callSuper = false)
@Builder
public class OfficialRecordEntity {
    @Id
    @AggregateIdentifier
    private String id;

    //many other fields

    @OneToMany(cascade = CascadeType.PERSIST)
    private List<Accessory> accessories;

    @ManyToOne(cascade = CascadeType.PERSIST)
    private ExpeditionType expeditionType;

    //many other commandhandlers/eventhandlers

    @CommandHandler
    public OfficialRecordEntity(CreateOfficialRecordCommand command) {
        apply(new CreateOfficialRecordEvent(command.getId(), command.getOfficialRecordDTO()));
    }

    @EventSourcingHandler
    public void on(final CreateOfficialRecordEvent event) {
        //...some field initializations    
    }
}

Both Accessory and ExpeditionType are entities as well.

I am aware that we are not using the axon framework to it's full extent but by this point I'm just trying to get to save all the children entities before the OfficialRecordEntity properly - was expecting that the cascade type should suffice but due to axon's entity manager provider it doesn't seem to work that way.

Upvotes: 2

Views: 943

Answers (1)

Steven
Steven

Reputation: 7275

Firstly, I would to point I am mostly in the habit of using the Event Sourced approach of storing and retrieving an Aggregate. Nonetheless, I have build a state-stored approach as well, which did include an Aggregate Root with several Aggregate Members under it, in a collection.

The following versions are of interest in deducing whether you have a similar set up as I have:

  • Java - 8
  • Axon Framework - 4.1.1
  • Spring Boot (and Starter Data JPA) - 2.0.5.RELEASE

Then, my Aggregate and Entity looked something like this:

// Aggregate sample
@Entity
@Aggregate
class SomeAggregateRoot {

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "entityId")
    @AggregateMember
    private List<SomeEntity> entities = new ArrayList<>();

    // Some command and event handlers...
}


// Aggregate Member sample
@Entity
public class SomeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String id;

    @EntityId
    private String entityId;

    // Some command and event handlers...
}

This set up, without specifying the SomeAggregateRoot Repository myself, works correctly for me. I thus let the axon-spring-boot-starter dependency auto configure all the necessary Axon components.

I do know this isn't a direct answer to your question. I am however unsure where the issue lies in your set up, so I hope cross referencing your set up with what I just shared will give you an "a ha!" moment.


Update

To test this out with a shareable Github repository, I have set up one which shows the above in working condition, which you can find here.

Upvotes: 2

Related Questions