spring transaction - rollback only na true w prywatnej metodzie

0

Hej,
Podczas próby zapisu dostaję wyjątek rzucany przez org.springframework.orm.jpa.JpaSystemException dotyczący ze jest zaznaczony parametr rollback only. Wiem z czym jest błąd związany ale nie wiem jak mogę to obejść.
Błąd występuje dlatego, że metoda execute() rzuca wyjątkiem
Poniżej mój psełdokod:

public class MyService {
    MyJpaRepository myJpaRepository;
    EntityManager entityManager;
  
        @Transactional
        @Override 
        public void doSomething(){

               MyObject myObject  = myJpaRepository.findOne(1L);               
               Try<Integer>result = updateChild(myObject.getchild());
               myObject .addResult(result .get());

                myJpaRepository.save(myObject );
  
      
        }

       Try<Integer>updateChildren(MyObjectChilden child) {
            String query = child.prepareQuery("xxx");
            Try<Integer> result = Try.of(() -> execute(query));

            if(result.isFailure()){
                  String query = child.prepareQuery("YYY");  //jak rzuci błąd unique constraint validation zmieniam na "YYY"
                  result = Try.of(() -> execute(query ));
             }
             return result
     }
   
    private Integer execute(String query) {
            final Query nativeQuery = entityManager.createNativeQuery(query); //Może rzucić błąd unique constraint validation  
            return (Integer) nativeQuery.getSingleResult();
    }
}

Ogólnie idea jest taka ze wydziełiłem execute() zeby móc ją wielokrotnie wykorzystać ale z innym query, niestety ta metoda rzuca wyjątek przez co zaznaczony jest parametr rollback only i nie ma możliwości zapisu do bazy

0

Dziwne. Wyjątek nie powinien być widziany przez Spring magię.
Coś chyba innego się dzieje.
Zrób w metodzie doSomething : entityManager.flush() opakuj w try catch i zobacz co się dzieje.

0

@Transactional(noRollbackFor={JpaSystemException.class})

0

Po wywołaniu entityManager.flush() nic się nie dzieje, natomiast jak się kończy transakcja (metoda doSomething()) dostaję wyjątek: Transaction was marked for rollback only; cannot commit; nested exception is org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit - znacznik rollback only ustawiony jest na true podczas rzucenia wyjątku 'unique constraint' w metodzie 'execute(String query)'

@Transactional(noRollbackFor={JpaSystemException.class}) - po ustawieniu tego parametru też leci ten sam wyjątek

Problem rozwiązałem poprzez wyniesienie metody execute(String query) do osobnej transakcji

0

Wydaje mi się, że skupiasz się na złym komunikacie. Szukałbym pod spodem kolejnego stack trace'u z zawiniętym wyjątkiem (cause). Czasem takie piętra wyjątków mają wysokość 5 i najciekawsze jest na samym dole. Typowy błąd:

throw new RuntimeException(e.getMessage())

zamiast

throw new RuntimeException(e)

To drugie zachowuje cause.

1

Wyjaśnione.

W skrócie rację miał @Koziołek, a ja nie wiedziałem jednej zabawnej rzeczy.
**Hibernate **faktycznie sam sobie pomarkuje transackcje jako rollback only, nawet jeśli obsłużymy Exception. Dotyczy to niektórych rodzajów exceptionów...nawet takich co nie widać w przetwarzaniu typu: StaleStateException (i dzieje się przy OptimisticLockException)

Elegancka metoda ze źródeł hibernate :-) ponizej:

Elegancja

@Override
	public void handlePersistenceException(PersistenceException e) {
		if ( e instanceof NoResultException ) {
			return;
		}
		if ( e instanceof NonUniqueResultException ) {
			return;
		}
		if ( e instanceof LockTimeoutException ) {
			return;
		}
		if ( e instanceof QueryTimeoutException ) {
			return;
		}

		try {
			markForRollbackOnly();
		}
		catch ( Exception ne ) {
			//we do not want the subsequent exception to swallow the original one
			LOG.unableToMarkForRollbackOnPersistenceException(ne);
		}
}

Generalnie oznacza to też, że hibernate + spring nie daje szansy na ludzką obsługę błędów z użyciem Try. Jeśli złapiemy wyjątek i oddamy Try z failem to interceptor springowy z radością stwierdzi, że nie było błędu (nie poleciał exception) czyli trzeba zatwierdzić transakcję commitem.... co się nie uda, bo jest ustawiony rollbackonly i leci nowy exception. Nawet mam taki case na produkcji teraz, chociaz jedyny problem to smiecenie logów. Ale może być niebezpiecznie. Magia k...a.

0

Taki problem, jak pokazał OP, rozwiązuje się chyba wewnętrznymi transakcjiami. NESTED albo REQUIRES_NEW. Rollback jest na miejscu, ale nie chcemy go do głównej transakcji, tylko do wewnętrznej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1