1
votes

I am trying to process a record within a @Transactional annotated method. There are some dependencies from other methods that must be taken care before processing some other secondary (but necessary) business logic. The main record seems to be saved with no problem inside the transaction, but when I try to clean some data by using another service, it throws a JpaEntityNotFoundException.

I used Propagation.REQUIRES_NEW in the method that causes the issue, but with no success. Does anyone have an idea about what I am missing or doing wrong? I must not commit the main record before doing the rest of the transactional operations.

The exception that I am getting is: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find com.myproject.repository.entity.Book with id 5851445; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.myproject.repository.entity.Book with id 5851445...............

Here is an example that shows somehow my issue:

ServiceA class

@Service
public class ServiceA {


    public void nonTransactionalMethodA(Book book) {

        //..... Any independent logic from ServiceB

        updateAuthor(book);

        nonTransactionalMethodB();
    }

    public void nonTransactionalMethodB() {
        //post process logic ......
    }

}

ServiceB Class

  @Service
  public class ServiceB {

        @Autowired
        private BookRepository bookRepository;

        @Autowired
        private OfferRepository offerRepository;

        @Transactional
        private void updateAuthor(Author author) {
            Book book = new Book(1);
            book.setAuthor(author);
            bookRepository.save(book);
            removeUnavailableOffers(book);        
        }

        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void removeUnavailableOffers (Book book) {
            /*throwing org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find com.myproject.repository.entity.Book with id 5851445; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.myproject.repository.entity.Book with id 5851445............*/
            offerRepository.deleteByBookIdIsNullAndBookAuthorIdNot(book.authorId);            
        }
    }   

Any thought on this will be greatly appreciated.

1
What are you trying to achieve with Propagation.REQUIRES_NEW? It's for the partial rollback, and not all databases support it. If you have several transactions your "other" service should be annotated with @Transactional and called from another @Transactional method that calls both services if it wants to see the changes made in transaction. P.S. and @Modus is right about private methods, @Transactional is never expected to do anything with private methodsBoris Treukhov
updateAuthor is private in ServiceB. Where do you call it? It should be called from another class.Modus Tollens
@ModusTollens thanks for the notice. The real implementation has the method with public access.Ara Kokheba
@AraKokheba Ok, but it is still called from a different class, right? That is necessary so the class with the transactional method is proxied.Modus Tollens
@ModusTollens great answer!!! I changed the transactional to a different class and then it worked. Thanks. You should post as answer so I can vote it hee heeAra Kokheba

1 Answers

1
votes

Make sure that the method annotated with @Transactional is declared as public and called by a different class.

A transactional method must be public so that Spring can override it and it must be called from outside the defining class so that the invocation can go through a proxy.

It's one of the common pitfalls when using @Transactional, for more information see https://codete.com/blog/5-common-spring-transactional-pitfalls/