2
respostas

Escopo de Transações com EntityManager Gerenciado pelo Spring

No primeiro curso de JPA fica claro, nos exemplos, qual é o escopo de uma transação do EntityManager, uma vez que obtemos o mesmo através de um EntityManagerFactory, explicitamente chamamos um begin() e um commit() ao final da operação. Quando isto é feito com o EntityManager sendo criado dentro da DAO, cada operação em cada DAO tem, portanto, seu escopo próprio de transação. Há situações em que uma operação exige a utilização de mais de uma DAO, dentro da mesma transação (por exemplo, em um caso de uso hipotético de venda o cadastro da venda, e a baixa do estoque, precisam ser feitos na mesma transação). Nestes casos uma possível alternativa é criar o EntityManager no objeto que chama as DAOs, passando a mesma instância do EM para cada uma, e deixando por conta do objeto chamador a responsabilidade por iniciar e finalizar a transação, fechando o EM ao final.

No Spring...

Como este cenário acima fica no Spring? No exemplo deste segundo curso (aliás, nos exemplos de outros cursos que vejo aqui no Alura) o EntityManager é injetado em cada DAO. Neste caso, cada operação nas DAOs tem seu próprio escopo de transação, ou o Spring se encarrega de iniciar e finalizar a transação a requisição, e os EM compartilham a mesma transação?

No caso do uso do padrão OpenEntityManagerInView, o escopo da transação é único para cada requisição? E se for necessário ter mais de um escopo em uma requisição, por questões de regra de negócio?

E se for necessário ter mais de um datasource (diferentes bancos de dados) e consequentemente mais de um EntityManager por requisição, como fazer?

Existe uma forma mais explícita de demarcar o escopo transacional?

Obrigado!

2 respostas

Olá Luiz,

Assim como os EJB`s os spring tem um conjunto de escopos que podem ser definidos na anotação @Transactional.

Segue abaixo alguns exemplos:

@Transactional(propagation = Propagation.MANDATORY)
  @Transactional(propagation = Propagation.NESTED)
  @Transactional(propagation = Propagation.NEVER)
  @Transactional(propagation = Propagation.NOT_SUPPORTED)
  @Transactional(propagation = Propagation.REQUIRED)
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  @Transactional(propagation = Propagation.SUPPORTS)

Imagine que um método precise sempre ser executado em uma transação isolada, você pode anota-lo com REQUIRED_NEW, nesse caso o método sempre abrirá uma nova transação.

Um outro caso, seria o exemplo que você indicou, onde toda a regra de negocio deveria ser executada na mesma transação, nesse caso voce poderia fazer algo parecido com o abaixo:

@Transactional(propagation = Propagation.REQUIRED)
public void primeiraTarefa(){
segundaTarefa();
}

@Transactional(propagation = Propagation.MANDATORY)
public void segundaTarefa(){

}

No exemplo acima, o primeiro método vai abrir uma transação, e o segundo método só será executado se já existir uma transação aberta, caso contrário ele lançará uma exceção, garantindo dessa forma que o método sempre será executado com a mesma transação de quem chama-lo.

O assunto de gerenciamento de transações do spring abrange bastante coisa, eu recomendo você pesquisar um pouco mais sobre isso na internet pois é um tema bastante importante. Deve ter algum curso aqui no Alura que explica com mais detalhes esse assunto.

Olá Denis.

Nestes cenários então, se eu fizer a correta demarcação de escopo transacional com as anotações, qualquer EntityManager que eu criar /for injetado dentro do contexto, estarão compartilhando a mesma transação?

Obrigado pelo retorno!