Erro TransactionRequiredException

Erro TransactionRequiredException

1. Visão geral

Neste tutorial, vamos examinar a causa do erroTransactionRequiredException e como resolvê-lo.

2. TransactionRequiredException

This error typically occurs when we’re trying to perform a database operation that modifies the database without a transaction.

Por exemplo, tentando atualizar um registro sem uma transação:

Query updateQuery
  = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3");
updateQuery.setParameter(1, title);
updateQuery.setParameter(2, body);
updateQuery.setParameter(3, id);
updateQuery.executeUpdate();

Irá gerar uma exceção com uma mensagem nas seguintes linhas:

...
javax.persistence.TransactionRequiredException: Executing an update/delete query
  at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1586)
...

3. Fornecimento de uma transação

A solução óbvia é envolver qualquer operação de modificação de banco de dados em uma transação:

Transaction txn = session.beginTransaction();
Query updateQuery
  = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3");
updateQuery.setParameter(1, title);
updateQuery.setParameter(2, body);
updateQuery.setParameter(3, id);
updateQuery.executeUpdate();
txn.commit();

No snippet de código acima, iniciamos e confirmamos manualmente a transação. Emborain a Spring Boot environment, we can achieve this by using the @Transactional annotation.

4. Suporte para transações na primavera

If we want more fine-grained control, we can use Spring’s TransactionTemplate. Porque isso permite ao programador disparar a persistência de um objeto imediatamente antes de prosseguir com a execução do código de um método.

Por exemplo, digamos que queremos atualizar a postagem antes de enviar uma notificação por e-mail:

public void update() {
    entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1")
      // parameters
      .executeUpdate();
    sendEmail();
}

Applying the @Transactional to the method above may cause the email to be sent in spite of an exception in the update process. Isso ocorre porque a transação só será confirmada quando o método for encerrado e estiver prestes a retornar ao chamador.

Portanto, atualizar a postagem dentro deTransactionTemplate evitará este cenário, pois comprometerá a operação imediatamente:

public void update() {
    transactionTemplate.execute(transactionStatus -> {
        entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1")
          // parameters
          .executeUpdate();
        transactionStatus.flush();
        return null;
    });
    sendEmail();
}

5. Conclusão

Em conclusão, geralmente é uma boa prática envolver as operações do banco de dados em uma transação. Ajuda na prevenção de corrupção de dados. O código-fonte completo está disponívelover on Github.