0
votes

When I try to remove an entity using Outbound Channel Adapter I always get removing a detached instance exception. I know that an entity should be retrieved and deleted in the same transaction to avoid this exception, but how can I achieve it with Spring Integration?

To demonstrate the problem I modified the JPA sample:

PersonService.java

public interface PersonService {
    ...
    void deletePerson(Person person);
}

Main.java

private static void deletePerson(final PersonService service) {
    final List<Person> people = service.findPeople();
    Person p1 = people.get(0);
    service.deletePerson(p1);
}

spring-integration-context.xml

<int:gateway id="personService"
service-interface="org.springframework.integration.samples.jpa.service.PersonService"
    default-request-timeout="5000" default-reply-timeout="5000">
    <int:method name="createPerson" request-channel="createPersonRequestChannel"/>
    <int:method name="findPeople"   request-channel="listPeopleRequestChannel"/>
    <int:method name="deletePerson" request-channel="deletePersonChannel"/>
</int:gateway>

<int:channel id="deletePersonChannel"/>

<int-jpa:outbound-channel-adapter entity-manager-factory="entityManagerFactory"
channel="deletePersonChannel" persist-mode="DELETE" >
    <int-jpa:transactional transaction-manager="transactionManager" />
</int-jpa:outbound-channel-adapter>

When I call deletePerson I get the exception:

Exception in thread "main" java.lang.IllegalArgumentException: Removing a detached instance org.springframework.integration.samples.jpa.Person#1001

UPDATE:

Apparently I should've chosen a sample closer to my actual project, because here you can just create a new transaction programmatically and wrap both retrieve and delete function calls in it, as Artem did.

In my project I have a transformer connected to an outbound-channel-adapter. The transformer retrieves an entity and the outbound-channel-adapter removes it. How can I get the transformer and the outbound-channel-adapter to use the same transaction in this case?

1

1 Answers

2
votes

To get it worked you should wrap all operations in the deletePerson to transaction, e.g.

private static void deletePerson(final PersonService service) {
         new new TransactionTemplate(transactionManager)
       .execute(new TransactionCallbackWithoutResult() {

            protected void doInTransactionWithoutResult(TransactionStatus status) {
                 final List<Person> people = service.findPeople();
                 Person p1 = people.get(0);
                 service.deletePerson(p1);

            }
        });
}

In this case you should somehow provide to your method transactionManager bean too.

UPDATE:

I shown you a sample for use-case in the original question. Now re. <transformer> -> <jpa:outbound-channel-adapter>. In this you should understand where your message flow is started:

  1. If it is <inbound-channel-adapter> with poller, so just make the <poller> <transactional>
  2. If it <gateway>, who call <transformer>, so it's just enough to mark gateway's method with @Transactional
  3. Here is one more transactional advice trick: Keep transaction within Spring Integration flow

In all cases you should get rid of <transactional> from your <jpa:outbound-channel-adapter>