Método @Transactional chamando outro método sem anotação @Transactional?

89

Eu vi um método em uma classe de serviço que estava marcado como @Transactional, mas também estava chamando alguns outros métodos dessa mesma classe que não estavam marcados como @Transactional.

Isso significa que a chamada para métodos separados está fazendo com que o aplicativo abra conexões separadas com o banco de dados ou suspenda a transação pai, etc?

Qual é o comportamento padrão de um método sem anotações que é chamado por outro método com @Transactionalanotação?

goe
fonte

Respostas:

119

Quando você chama um método fora de @Transactionalum bloco de transação, a transação pai continuará com o novo método. Ele usará a mesma conexão do método pai (com @Transactional) e qualquer exceção causada no método chamado (sem @Transactionalfará com que a transação seja revertida conforme configurado na definição da transação.

Se você chamar um método com uma @Transactionalanotação de um método com@Transactional a mesma instância, o comportamento transacional dos métodos chamados não terá nenhum impacto na transação. Mas se você chamar um método com uma definição de transação a partir de outro método com uma definição de transação e eles estiverem em instâncias diferentes, o código no método chamado seguirá as definições de transação fornecidas no método chamado.

Você pode encontrar mais detalhes na seção Gerenciamento de transações declarativas da documentação de transações de primavera .

O modelo de transação declarativo Spring usa proxy AOP. portanto, o proxy AOP é responsável pela criação das transações. O proxy AOP estará ativo apenas se os métodos na instância forem chamados de fora da instância.

Arun P Johny
fonte
é esse o comportamento padrão da primavera?
goe
Sim. É o comportamento padrão.
Arun P Johny
2
@Tomasz Sim. Mas também deve ser mencionado que alterar a propagação da transação em um método que é chamado de outro método @Transactional não terá efeito.
Fil
1
@Tomasz, foi isso que eu quis dizer will follow the transaction definitions given in the called method. Porém, se a chamada vier da mesma instância do objeto, ela não terá nenhum efeito, pois a chamada não se propagará pelos proxies aop que são responsáveis ​​pela manutenção da transação.
Arun P Johny
5
@Filip, Isso não está completamente correto, Se você chamar um método com uma @Transactionaldefinição de um objeto / instância diferente, então, embora o método de chamada tenha @Transactionalatributos diferentes , o método chamado seguirá a sua própria definição de transação.
Arun P Johny
24
  • Isso significa que a chamada para métodos separados está fazendo com que o aplicativo abra conexões separadas com o banco de dados ou suspenda a transação pai, etc?

Isso depende de um nível de propagação . Aqui estão todos os valores de nível possíveis .

Por exemplo, no caso de um nível de propagação ser NESTED, uma transação atual será "suspensa" e uma nova transação será criada ( nota: a criação real de uma transação aninhada só funcionará em gerenciadores de transação específicos )

  • Qual é o comportamento padrão de um método sem anotações que é chamado por outro método com anotação @Transactional?

O nível de propagação padrão (o que você chama de "comportamento") é REQUERIDO . Caso seja chamado um método "interno" que @Transactionalcontenha uma anotação (ou transacionado declarativamente via XML), ele será executado dentro da mesma transação , por exemplo, "nada novo" é criado.

Tolitius
fonte
E as subchamadas de NOT_SUPPORTED que não têm nenhuma anotação? Ele herda NOT_Supported ou eles abriram uma nova transação porque REQURED é o padrão? Por exemplo: f1.call () {f2 ()} com a anotação NOT_SUPPORTED para f1 e não para f2.
Dave
8

@Transactional marca o limite da transação (início / fim), mas a própria transação está ligada ao encadeamento. Depois que uma transação é iniciada, ela se propaga pelas chamadas de método até que o método original retorne e a transação seja confirmada / revertida.

Se outro método for chamado com uma anotação @Transactional, a propagação dependerá do atributo de propagação dessa anotação.

sourcedelica
fonte
As 3 respostas entram em conflito entre si em algum grau, não tenho certeza de qual é mais precisa.
Trump 2020 - Justiça virá
1
@EricWang Só queria compartilhar que testei esse cenário hoje e a resposta de Arun P Johny (com comentários) é a mais precisa para esse cenário de invocações internas .
Vinay Vissh
3

O método interno afetará o método externo se o método interno não estiver anotado com @Transactional.

Caso o método interno também seja anotado com @Transactional com REQUIRES_NEW, o seguinte acontecerá.

...
@Autowired
private TestDAO testDAO;

@Autowired
private SomeBean someBean;

@Override
@Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
  testDAO.insertUser(user);
  try{
    someBean.innerMethod();
  } catch(RuntimeException e){
    // handle exception
  }
}


@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
  throw new RuntimeException("Rollback this transaction!");
}

O método interno é anotado com REQUIRES_NEWe lança uma RuntimeException, de forma que definirá sua transação para rollback, mas NÃO EFEITARÁ a transação externa. A transação externa é PAUSADA quando a transação interna é iniciada e, em seguida, RETOMA APÓS a transação interna ser concluída. Eles são executados independentemente um do outro, então a transação externa PODE ser confirmada com sucesso.

saran3h
fonte
1
Para esclarecer para iniciantes, tenho certeza de que innerMethod () precisa estar em um bean diferente (também conhecido como objeto java gerenciado por Spring) do outerMethod (). Se ambos estiverem no mesmo bean, não acho que o innerMethod realmente usará o comportamento Transacional declarado em sua anotação. Em vez disso, ele usará o que está declarado na declaração outerMethod (). Isso ocorre por causa de como o Spring lida com AOP, que é usado para suas anotações @Transactional ( docs.spring.io/spring/docs/3.0.x/spring-framework-reference/… )
johnsimer