Spring batch chicken and egg: transient JPA reference

Viewed 261

I have a Spring-Batch process which Reads from Bar to create a new Foo:

/* My Foo Step */
class FooStepConfig {
    ItemReader<Bar> reader; // Reads records from Bar
    ItemProcessor<Bar, Foo> processor; // Determines whether to create a new Foo or update an existing Foo
    ItemReader<Foo> writer; // Created/Updated Foos are persisted
    // ...
}

Additionally, when creating or updating a Foo, I need to also save a FooNotification that references the Foo:

// During processor step, this is saved
@Entity
public FooNotification {
    @Id Long id;
    @ManyToOne Foo foo; // @Entity class Foo { ... };
    @Enumerated NotificationType nt; // new, updated, removed, etc... 
                                     // (removal is a soft delete with "Foo.isActive" column) 
}

ItemProcessor:

class MyItemProcessor extends ItemProcessor<Foo, Bar> {
    ...
    public Foo process(Bar bar) { 
        Foo foo = fromBar(bar); // some supporting method defined elsewhere.
        ...
        // does not work as foo is still transient at this point and id is null as it is generated on persist from a Database Sequence.
        fooNotificationRepository.save(new FooNotification(foo,...));
        ...
        return Foo;
    }
}

As described in the comments above, when the batch process runs through, I receive the following exception:

java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException:↩ 
  ↪object references an unsaved transient instance - save the transient↩ 
  ↪instance before flushing : com.example.FooNotification.foo -> com.example.Foo

Both Foo creation/mutation and FooNotification creation should be atomic. I don't want either to exist without the other, so spliting the two into seperate steps isn't ideal. Also the FooNotification can contain information about what fields changes and previous values, so calculating that at the time of the update is much more efficient.

I know I can solve this by creating an ItemProcessor<Bar, FooNotification> and ItemWriter<FooNotification> as the saves would cascade with appropriate configuration and the Foo should always exist for each type. conceptually though, this isn't the purpose of the step and feels awkward. FooNotifications are a side product of the main Foo creation and not the main batch step's objective.

  • Is there another more sensible way I can ensure the creation of the Foo before the FooNotification?
  • Do I need to 'pre-create' the Foo unfinished in order for the reference to be a valid orm managed entity?
1 Answers

Two solutions:

  1. A listener
  2. Move notification creation to writing phase: use a single writer or a composite one; the first for for Foo and latest for FooNotification
Related