Prefácio
O Spring especifica 7 tipos de comportamentos de propagação de transação na interface da transação. O comportamento de propagação da transação é um recurso de aprimoramento da transação exclusivo para a estrutura da mola e não pertence ao comportamento do banco de dados do provedor real da transação. Esta é uma caixa de ferramentas poderosa que a primavera nos fornece, e o uso de linhas de propagação de transações pode fornecer muitas conveniências para nossos esforços de desenvolvimento. Mas as pessoas têm muitos mal -entendidos sobre isso, e você deve ter ouvido o boato de que "o negócio do método de serviço é melhor não ser aninhado". Para usar as ferramentas corretamente, você deve primeiro entender as ferramentas. Este artigo apresenta os sete comportamentos de propagação da transação em detalhes e apresenta os principais exemplos de código do conteúdo.
Conceitos básicos
1. O que é o comportamento de comunicação da transação?
O comportamento da propagação da transação é usado para descrever como as transações são propagadas quando os métodos modificados por um determinado comportamento de propagação da transação são aninhados em outro método.
Use pseudo-código para explicar:
public void Metoda () {MethodB (); // dosomething} @transaction (propagação = xxx) public void methodB () {// doSomething} methodA() no código chama methodB() em aninhado, e o comportamento de propagação da transação do methodB() é determinado pela configuração @Transaction(Propagation=XXX) . Deve -se notar aqui que methodA() não inicia a transação e o método de modificar um determinado comportamento de propagação da transação não precisa ser chamado no método periférico de iniciar a transação.
2. Sete comportamentos de propagação de transações na primavera
| Tipo de comportamento de propagação da transação | ilustrar |
|---|---|
| Propagation_required | Se não houver transação atualmente, crie uma nova transação e, se já houver uma transação, adicione -a à transação. Esta é a escolha mais comum. |
| Propagation_supports | Suporta a transação atual e, se atualmente não houver transação, ela será executada de maneira não transacional. |
| Propagation_mandatory | Use a transação atual e faça uma exceção se atualmente não houver transação. |
| Propagation_requires_new | Crie uma nova transação. Se a transação existir atualmente, suspenda a transação atual. |
| Propagation_not_supported | Executar operações de maneira não transacional e, se houver atualmente uma transação, a transação atual será suspensa. |
| Propagation_never | Executa de maneira não transacional e lança uma exceção se existir atualmente uma transação. |
| Propagation_nested | Se atualmente existir uma transação, ela será executada dentro de uma transação aninhada. Se atualmente não houver transação, execute uma operação semelhante ao propagation_required. |
A definição é muito simples e fácil de entender. Vamos para a seção de teste de código para verificar se nosso entendimento está correto.
Verificação de código
O código deste artigo é apresentado em duas camadas em uma estrutura tradicional de três camadas, a saber, o serviço e a camada DAO. A primavera é responsável pela injeção de dependência e gerenciamento de transações de anotação. A camada DAO é implementada por Mybatis. Você também pode usar qualquer método favorito, como Hibernate, JPA, JDBCTemplate, etc. O banco de dados usa o banco de dados MySQL e também pode usar qualquer banco de dados habilitado para transações, que não afetará os resultados da verificação.
Primeiro, criamos duas tabelas no banco de dados:
usuário1
Criar tabela `user1` (` id` inteiro não assinado NÃO NULL AUTO_INCREMENT, `name` Varchar (45) não padrão nulo '', chave primária (` id`)) mecan
usuário2
Criar tabela `user2` (` id` inteiro não assinado NÃO NULL AUTO_INCREMENT, `name` Varchar (45) não padrão nulo '', chave primária (` id`)) mecanismo = innodb;
Em seguida, escreva o código de camada de feijão e Dao correspondente:
Usuário1
classe pública user1 {private inteiro id; nome de string privado; // Os métodos de obtenção e definição são omitidos ...}Usuário2
classe pública user2 {private inteiro id; nome de string privado; // Os métodos de obtenção e definição são omitidos ...}User1mapper
interface pública user1mapper {int insert (user1 registro); User1 selectByPrimaryKey (ID inteiro); // Outros métodos são omitidos ...}User2mapper
interface pública user2mapper {int insert (user2 registro); User2 selectByPrimaryKey (ID inteiro); // Outros métodos são omitidos ...}Finalmente, o código de verificação específico é implementado pela camada de serviço e o listaremos nas seguintes situações.
1.Propagation_Required
Adicionamos Propagation.REQUIRED Atributos RECENDIDOS AO MÉTODOS CORRESSOS DE USUDE1SERVICE E UUSE2SERVICE.
Método User1Service:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omita outros ... @Override @Transactional (propagação = propagação.required) public void addRequired (usuário1 usuário) {user1mapper.insert (usuário); }}Método User2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omita outros ... @Override @Transactional (propagação = propagation.required) public void addRequired (usuário de usuário2) {user2mapper.insert (usuário); } @Override @Transaction (propagação = propagação.Required) public void addRequiredException (usuário2 usuário) {user2mapper.insert (usuário); lançar novo RuntimeTeException (); }} 1.1 Cena 1
Esse cenário método periférico não permite transações.
Método de verificação 1:
@Override public void nottransaction_exception_required_required () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addrequired (user2); lançar novo RuntimeTeException (); }Método de verificação 2:
@Override public void nottransaction_required_required_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiredException (User2); }Executar métodos de verificação separadamente e os resultados:
Análise dos resultados do banco de dados de número de série do método de verificação
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" e "Li si" são inseridos. | O método periférico não iniciou a transação e a inserção dos métodos "Zhang San" e "Li Si" é executada de forma independente em suas próprias transações. O método periférico anormal não afeta a inserção interna dos métodos "Zhang San" e "Li si". |
| 2 | "Zhang San" é inserido, mas "Li Si" não é inserido. | O método periférico não possui transações, e os métodos de inserção de "Zhang San" e "Li si" são executados independentemente em suas próprias transações, portanto, a inserção do método "Li Si" apenas reverterá o método "Li si" e a inserção do método "Zhang San" não será afetado. |
Conclusão: Através desses dois métodos, provamos que o método interno modificado pela propagação. Reabrigada abrirá recentemente suas próprias transações quando o método periférico não abrir a transação e as transações abertas são independentes uma da outra e não interferem entre si.
1.2 Cena 2
O método periférico inicia a transação, que é um cenário com uma taxa de uso relativamente alta.
Método de verificação 1:
@Override @Transaction (propagação = propagação.required) public void transaction_exception_required_required () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addrequired (user2); lançar novo RuntimeTeException (); }Método de verificação 2:
@Override @Transaction (propagação = propagação.required) public void transaction_required_required_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiredException (User2); }Método de verificação 3:
@Transaction @Override public void transaction_required_required_exception_try () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); tente {user2service.addrequiredException (user2); } catch (Exceção e) {System.out.println ("Rollback do método"); }}Executar métodos de verificação separadamente e os resultados:
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" e "Li Si" não foram inseridos. | O método periférico inicia a transação, o método interno une a transação do método periférico, o método periférico volta e o método interno também precisa ser revertido. |
| 2 | "Zhang San" e "Li Si" não foram inseridos. | O método periférico abre a transação, o método interno adiciona a transação do método periférico, o método interno lança uma reversão de exceção e o método periférico percebe a exceção, fazendo com que a transação geral seja revertida. |
| 3 | "Zhang San" e "Li Si" não foram inseridos. | O método periférico abre a transação, o método interno une a transação do método periférico e o método interno lança uma reversão de exceção. Mesmo que o método seja capturado e não seja percebido pelo método periférico, toda a transação ainda é revertida. |
Conclusão: Os resultados experimentais acima mostram que, quando o método periférico abre a transação, os métodos internos modificados pela Propagation.REQUIRED SERÁ adicionado à transação do método periférico. Todos os métodos internos e métodos periféricos modificados por Propagation.REQUIRED pertencem à mesma transação. Enquanto um método voltar, toda a transação será revertida.
2.Propagation_ReQuires_New
Adicionamos o atributo Propagation.REQUIRES_NEW aos métodos correspondentes do User1Service e User2Service.
Método User1Service:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omita outros ... @Override @Transactional (propagação = propagação.requis_new) public void addRequisnew (user1 user) {user1mapper.insert (user); } @Override @Transaction (propagação = propagação.Required) public void addRequired (usuário1 usuário) {user1mapper.insert (usuário); }}Método User2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omita outros ... @Override @Transactional (propagação = propagação.requis_new) public void addRequisnew (usuário user2) {user2mapper.insert (usuário); } @Override @Transaction (propagação = propagação.ReQuires_New) public void addRequisnewException (usuário2 usuário) {user2mapper.insert (user); lançar novo RuntimeTeException (); }} 2.1 Cena 1
O método periférico não permite transações.
Método de verificação 1:
@Override public void nottransaction_exception_requiiresnew_requitesnew () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequiiresnew (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiiresnew (user2); lançar novo RuntimeTeException (); }Método de verificação 2:
@Override public void nottransaction_requiiresnew_requiiresnew_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequiiresnew (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiiresnewException (user2); }Executar métodos de verificação separadamente e os resultados:
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" é inserido e "Li Si" é inserido. | O método periférico não tem transações. A inserção dos métodos "Zhang San" e "Li Si" é executada de forma independente em suas próprias transações. A reversão de exceção do método periférico não afetará o método interno. |
| 2 | "Zhang San" é inserido, "Li si" não é inserido | O método periférico não inicia a transação. A inserção do método "Zhang San" e a inserção do método "Li si" iniciam suas próprias transações, respectivamente. A inserção do método "Li Si" lança uma reversão de exceção e outras transações não são afetadas. |
Conclusão: Através desses dois métodos, provamos que o método interno modificado pela Propagation.REQUIRES_NEW iniciará recentemente suas próprias transações, e as transações abertas são independentes uma da outra e não interferem entre si.
2.2 Cena 2
O método periférico inicia a transação.
Método de verificação 1:
@Override @Transaction (propagação = propagação.Required) public void transaction_exception_required_requiiresnew_requiiresnew () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiiresnew (user2); User2 user3 = new user2 (); user3.setName ("Wang Wu"); user2service.addrequisnew (user3); lançar novo RuntimeTeException (); }Método de verificação 2:
@Override @Transaction (propagação = propagação.Required) public void transaction_required_requiiresnew_requiiresnew_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiiresnew (user2); User2 user3 = new user2 (); user3.setName ("Wang Wu"); user2Service.addReQuiresNewException (User3); }Método de verificação 3:
@Override @Transaction (propagação = propagação.required) public void transaction_required_requisnew_requiiresnew_exception_try () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2Service.addrequiiresnew (user2); User2 user3 = new user2 (); user3.setName ("Wang Wu"); tente {user2service.addrequisnewException (user3); } catch (Exceção e) {System.out.println ("rollingback"); }}Executar métodos de verificação separadamente e os resultados:
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" não foi inserido ", Li Si" foi inserido e "Wang Wu" foi inserido. | O método periférico inicia a transação, insere uma transação do método "Zhang San" e do método periférico, insere o método "Li Si" e o método "Wang Wu", respectivamente, na transação independente recém -criada. O método periférico lança uma exceção e só volta a mesma transação que o método periférico, de modo que o método de inserção do método "Zhang San" volta. |
| 2 | "Zhang San" não foi inserido ", Li Si" foi inserido e "Wang Wu" não foi inserido. | O método periférico inicia a transação, insere uma transação do método "Zhang San" e do método periférico, insere o método "Li Si" e o método "Wang Wu" em novas transações independentes. Quando o método "Wang Wu" é inserido, a transação que é inserida no método "Wang Wu" é revertida. A exceção continua sendo jogada e é percebida pelo método periférico. A transação do método periférico também é revertida, de modo que o método "Zhang San" também é revertido. |
| 3 | "Zhang San" é inserido ", Li Si" é inserido e "Wang Wu" não é inserido. | O método periférico inicia a transação, insere uma transação do método "Zhang San" e do método periférico, insere o método "Li Si" e o método "Wang Wu" em novas transações independentes. O método "Wang Wu" é inserido e a transação que insere o método "Wang Wu" é revertida. A exceção é capturada e não será percebida pelo método periférico. A transação do método periférico não é revertida; portanto, a inserção do método "Zhang San" é inserida com sucesso. |
Conclusão: Quando o método periférico abre a transação, o método interno modificado pela Propagation.REQUIRES_NEW ainda abrirá transações independentes separadamente e também é independente das transações de métodos externos. O método interno, o método interno e as transações de método externo são independentes umas das outras e não interferem entre si.
3.Propagation_nested
Adicionamos Propagation.NESTED Atributos de origem aos métodos correspondentes do User1Service e User2Service.
Método User1Service:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omita outros ... @Override @Transactional (propagação = propagação.nested) public void addnested (usuário do usuário1) {user1mapper.insert (usuário); }}Método User2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omita outros ... @Override @Transactional (propagação = propagação.nested) public void addnested (usuário do usuário2) {user2mapper.insert (usuário); } @Override @Transaction (propagação = propagação.nested) public void addNestedException (usuário2 usuário) {user2mapper.insert (usuário); lançar novo RuntimeTeException (); }} 3.1 Cena 1
Esse cenário método periférico não permite transações.
Método de verificação 1:
@Override public void nottransaction_exception_nested_nested () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addnested (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addnested (user2); lançar novo RuntimeTeException (); }Método de verificação 2:
@Override public void nottransaction_nested_nested_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addnested (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addnestedException (user2); }Executar métodos de verificação separadamente e os resultados:
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" e "Li si" são inseridos. | O método periférico não iniciou a transação e a inserção dos métodos "Zhang San" e "Li Si" é executada de forma independente em suas próprias transações. O método periférico anormal não afeta a inserção interna dos métodos "Zhang San" e "Li si". |
| 2 | "Zhang San" é inserido, mas "Li Si" não é inserido. | O método periférico não possui transações, e os métodos de inserção de "Zhang San" e "Li si" são executados independentemente em suas próprias transações, portanto, a inserção do método "Li Si" apenas reverterá o método "Li si" e a inserção do método "Zhang San" não será afetado. |
Conclusão: Através desses dois métodos, provamos que Propagation.REQUIRED Propagation.NESTED . Os métodos internos modificados iniciarão suas próprias transações novamente e as transações abertas são independentes uma da outra e não interferem entre si.
3.2 Cena 2
O método periférico inicia a transação.
Método de verificação 1:
@Transaction @Override public void transaction_exception_nested_nested () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addnested (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addnested (user2); lançar novo RuntimeTeException (); }Método de verificação 2:
@Transaction @Override public void transaction_nested_nested_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addnested (user1); User2 user2 = new user2 (); user2.setName ("li si"); user2service.addnestedException (user2); }Método de verificação 3:
@Transaction @Override public void transaction_nested_nested_exception_try () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addnested (user1); User2 user2 = new user2 (); user2.setName ("li si"); tente {user2service.addnestedException (user2); } catch (Exceção e) {System.out.println ("Rollback do método"); }}Executar métodos de verificação separadamente e os resultados:
| Método de verificação Número de série | Resultados do banco de dados | Análise de resultados |
|---|---|---|
| 1 | "Zhang San" e "Li Si" não foram inseridos. | O método periférico inicia a transação e a transação interna é uma sub-transação da transação periférica. O método periférico volta e o método interno também precisa ser revertido. |
| 2 | "Zhang San" e "Li Si" não foram inseridos. | O método periférico inicia a transação e a transação interna é uma sub-transação da transação periférica. O método interno lança uma reversão de exceção e o método periférico percebe a exceção, causando uma reversão da transação geral. |
| 3 | "Zhang San" é inserido e "Li Si" não é inserido. | O método periférico inicia a transação e a transação interna é uma sub-transação da transação periférica. Insira o método interno "Zhang San" para lançar uma exceção, e a transação infantil pode ser revertida separadamente. |
Conclusão: Os resultados do teste acima mostram que, quando o método periférico abre a transação, o método interno modificado pela Propagation.NESTED pertence à sub-transação da transação externa. A transação principal periférica volta e a sub-transação deve reverter. A sub-transação interna pode ser revertida separadamente sem afetar a transação principal periférica e outras sub-transações.
4. Similaridades e semelhanças de necessidade, requer_new, aninhado
A partir da comparação de "1.2 cena 2" e "3.2 cena 2", podemos ver:
Os métodos internos modificados por aninhados e necessários são transações de métodos periféricos. Se o método periférico lançar uma exceção, as transações de ambos os métodos serão revertidas. No entanto, o requerimento se une às transações do método periférico, por isso pertence à mesma transação que as transações periféricas. Depois que a transação necessária lança uma exceção e é revertida, as transações do método periférico também serão revertidas. O aninhado é uma sub-transação do método periférico e possui um ponto de salvamento separado; portanto, o método aninhado lança uma exceção e é revertido, o que não afetará a transação do método periférico.
A partir da comparação de "2.2 cena 2" e "3.2 cena 2", podemos ver:
Ambos aninhados e requer_new podem reverter as transações de método interno sem afetar as transações de métodos periféricos. No entanto, como o aninhado é uma transação aninhada, após o método periférico ser revertido, as sub-transações que são transações de método periférico também serão revertidas. Requer_new é implementado abrindo uma nova transação. Transações internas e transações periféricas são duas transações. A reversão da transação periférica não afetará as transações internas.
5. Outros comportamentos de propagação da transação
Em vista da questão do comprimento do artigo, os testes de outros comportamentos de propagação da transação não serão descritos aqui. Os leitores interessados podem procurar o código de teste correspondente e explicações de resultados no código -fonte. Portal: https: //github.com/tmtse/tran ...
Casos de uso de simulação
Depois de introduzir tantos comportamentos de comunicação de transações, como os aplicamos em nosso trabalho real? Deixe -me dar um exemplo:
Suponha que tenhamos um método registrado, no qual o método de adição de pontos é chamado. Se quisermos adicionar pontos para não afetar o processo de registro (ou seja, a reversão não conseguiu adicionar pontos não pode fazer com que o método de registro também seja revertido), escreveremos isso:
@Service public class UserServiceImpl implementa UserService {@Transactional public void Register (usuário do usuário) {Try {MembersHippointService.addpoint (ponto de ponto); } catch (Exceção e) {// omita ...} // omita ...} // omita ...} Também estipulamos que a falha de registro afetará addPoint() (o reversão do método de registro também requer reversão), portanto, addPoint() precisa ser implementado assim:
@Service Public Class Associando o ServiceImpl implementa a associação de pontos de vista {@Transaction (propagação = propagação.nested) public void addPoint (ponto de ponto) {try {Recordservice.addrecord (registro registro); } catch (Exceção e) {// omita ...} // omita ...} // omita ...} Percebemos que addRecord() addPoint() , que é usado para gravar logs. Sua implementação é a seguinte:
@Service public class RecordserviceImpl implementa Records Service {@Transactional (propagação = propagação.not_supported) public void addrecord (registro de registro) {// omita ...} // omit ...} We noticed propagation = Propagation.NOT_SUPPORTED in addRecord() method, because it is not accurate to the log, and one can be more or less, so addRecord() method itself and the peripheral addPoint() method will not cause addRecord() method to roll back, and addRecord() method to throw an exception will not affect the execution of the peripheral addPoint() method.
Através deste exemplo, acredito que todos têm uma compreensão mais intuitiva do uso do comportamento da comunicação de transações. A combinação de vários atributos pode realmente tornar nossa implementação comercial mais flexível e diversificada.
para concluir
Através da introdução acima, acredito que todos têm uma compreensão mais profunda do comportamento da comunicação de transações de primavera, e espero que seu trabalho diário de desenvolvimento seja útil.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.