Este artigo descreve o gerenciamento de transações no Spring com exemplos. Compartilhe com todos para sua referência. A análise específica é a seguinte:
Introdução de negócios:
O gerenciamento de transações é uma tecnologia essencial no desenvolvimento de aplicativos empresariais para garantir a integridade e consistência dos dados
Uma transação é uma série de ações tratadas como uma única unidade de trabalho. Ou todas essas ações funcionam ou nenhuma delas funciona
Quatro atributos principais de transações (ACID)
① Atomicidade: Uma operação atômica na sala de transações consiste em uma série de ações. A atomicidade das transações garante que as ações sejam totalmente concluídas ou não tenham nenhum efeito. ② Consistência: Depois que todas as ações da transação forem concluídas, a transação será confirmada. Os dados e recursos estão em um estado consistente que atende às regras de negócios ③ Isolamento: Pode haver muitas transações processando os mesmos dados ao mesmo tempo, portanto, cada coisa deve ser isolada de outras transações para evitar corrupção de dados ④ Durabilidade: Depois que uma transação é concluída , seus resultados não deverão ser afetados, independentemente dos erros do sistema que ocorrerem. Normalmente, os resultados de uma transação são gravados em armazenamento persistente
Gerenciamento de transações no Spring
Como uma estrutura de aplicação corporativa, o Spring define uma camada de abstração sobre diferentes APIs de gerenciamento de transações. Os desenvolvedores de aplicativos não precisam entender a API de gerenciamento de transações subjacente para usar o mecanismo de gerenciamento de transações do Spring.
Spring oferece suporte ao gerenciamento de transações programáticas e ao gerenciamento de transações declarativas.
Gerenciamento de transações programáticas: Incorpore código de gerenciamento de transações em métodos de negócios para controlar o envio e a reversão de transações. Em transações programáticas, um código de gerenciamento de transações adicional deve ser incluído em cada operação de negócios.
Gerenciamento de transações declarativas: Melhor usar do que o gerenciamento de transações programáticas na maioria dos casos. Ele separa o código de gerenciamento de transações dos métodos de negócios e implementa o gerenciamento de transações de maneira declarativa. A gestão de transações, como preocupação transversal, pode ser modularizada através de métodos AOP. Spring oferece suporte ao gerenciamento de transações declarativas por meio da estrutura Spring AOP.
Propriedades de propagação de transações Spring:
Quando um método de transação é chamado por outro método de transação, você deve especificar como a transação deve ser propagada. Por exemplo: o método pode continuar a ser executado na transação existente ou pode iniciar uma nova transação e executar em sua própria transação.
O comportamento de propagação de uma transação pode ser especificado pelo atributo de propagação. Spring define 7 comportamentos de comunicação:
comportamento de comunicação | significado |
PROPAGATION_MANDATORY | Indica que o método deve ser executado dentro de uma transação. Se a transação atual não existir, uma exceção será lançada. |
PROPAGATION_NESTED | Indica que se uma transação existir atualmente, este método será executado em uma transação aninhada. As transações aninhadas podem ser confirmadas ou revertidas independentemente da transação atual. Se a transação atual não existir, o comportamento será o mesmo de PROPAGATION_REQUIRED. Observe que o suporte de cada fabricante para esse comportamento de propagação é diferente. Você pode consultar a documentação dos seus gerenciadores de recursos para confirmar se eles suportam transações aninhadas. |
PROPAGATION_NEVER | Indica que o método atual não deve ser executado em um contexto de transação. Se houver uma transação em execução, uma exceção será lançada |
PROPAGATION_NOT_SUPPORTED | Indica que este método não deve ser executado em uma transação. Se existir uma transação atual, ela será suspensa enquanto este método estiver em execução. Se você usa JTATransactionManager, você precisa acessar o TransactionManager |
PROPAGATION_REQUIRED | Indica que o método atual deve ser executado em uma transação. Se a transação atual existir, o método será executado nessa transação. Caso contrário, uma nova transação será iniciada |
PROPAGATION_REQUIRED_NEW | Indica que o método atual deve ser executado em sua própria transação. Uma nova transação será iniciada. Se existir uma transação atual, ela será suspensa durante a execução deste método. Se você usa JTATransactionManager, você precisa acessar o TransactionManager |
PROPAGATION_SUPPORTS | Indica que o método atual não requer um contexto de transação, mas se houver uma transação atual, o método será executado nesta transação |
Entre eles PROPAGATION_REQUIRED é o atributo de propagação padrão
Problemas causados por transações simultâneas
Muitos problemas inesperados podem surgir quando múltiplas transações no mesmo aplicativo ou em aplicativos diferentes são executadas simultaneamente no mesmo conjunto de dados.
Os problemas causados por transações simultâneas podem ser divididos nas três categorias a seguir:
① Leitura suja: A leitura suja ocorre quando uma transação lê dados que foram reescritos por outra transação, mas ainda não foram confirmados. Se a reescrita for revertida posteriormente, os dados obtidos pela primeira transação serão inválidos.
② Leitura não repetível: A leitura não repetível ocorre quando uma transação executa a mesma consulta duas ou mais vezes, mas obtém dados diferentes a cada vez. Geralmente isso ocorre porque outra transação simultânea atualizou os dados entre as consultas
③ Leitura fantasma: A leitura fantasma é semelhante à leitura não repetível. Acontece quando uma transação (T1) lê algumas linhas de dados e então outra transação simultânea (T2) insere alguns dados. Nas consultas subsequentes, a primeira transação (T1) descobrirá que existem mais alguns registros que não existiam originalmente.
exemplo de código
Primeiro está a tabela do banco de dados:
Incluindo book(isbn, book_name, price), account(username, balance), book_stock(isbn, stock)
Então as classes usadas:
BookShopDao
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
interface pública BookShopDao {
//Obtém o preço unitário do livro com base no número do livro
public int findBookPriceByIsbn(String isbn);
//Atualiza o inventário do livro para que o inventário correspondente ao número do livro seja -1
public void updateBookStock(String isbn);
//Atualiza o saldo da conta do usuário: faça o preço do saldo do nome de usuário
public void updateUserAccount (String nome de usuário, preço interno);
}
BookShopDaoImpl
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.jdbc.core.JdbcTemplate;
importar org.springframework.stereotype.Repository;
@Repositório("bookShopDao")
classe pública BookShopDaoImpl implementa BookShopDao {
@Autowired
privado JdbcTemplate JdbcTemplate;
@Substituir
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECIONE preço DO livro WHERE isbn = ?";
retornar JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Substituir
public void updateBookStock(String isbn) {
//Verifica se o inventário contábil é suficiente, caso contrário, lança uma exceção
String sql2 = "SELECIONE estoque FROM book_stock WHERE isbn = ?";
estoque interno = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if (estoque == 0) {
throw new BookStockException("Estoque insuficiente!");
}
String sql = "ATUALIZAR book_stock SET estoque = estoque - 1 WHERE isbn = ?";
JdbcTemplate.update(sql, isbn);
}
@Substituir
public void updateUserAccount(String nome de usuário, preço interno) {
//Verifica se o saldo é insuficiente, caso contrário, lança uma exceção
String sql2 = "SELECIONE saldo DA conta WHERE nome de usuário =?";
saldo interno = JdbcTemplate.queryForObject(sql2, Integer.class, nome de usuário);
if (saldo < preço) {
throw new UserAccountException("Saldo insuficiente!");
}
String sql = "ATUALIZAR conta SET saldo = saldo - ? WHERE nome de usuário = ?";
JdbcTemplate.update(sql, preço, nome de usuário);
}
}
Serviço de livraria
Copie o código da seguinte forma: package com.yl.spring.tx;
interface pública BookShopService {
compra pública nula (String nome de usuário, String isbn);
}
BookShopServiceImpl
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.stereotype.Service;
importar org.springframework.transaction.annotation.Isolation;
importar org.springframework.transaction.annotation.Propagation;
importar org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
classe pública BookShopServiceImpl implementa BookShopService {
@Autowired
BookShopDao privado bookShopDao;
/**
* 1. Adicione anotações de transação
* Use a propagação para especificar o comportamento de propagação da transação, ou seja, como usar a transação quando o método de transação atual for chamado por outro método de transação.
* O valor padrão é REQUIRED, o que significa usar a transação que chama o método
* REQUIRES_NEW: Use sua própria transação e a transação do método de transação chamado será suspensa.
*
* 2. Use isolamento para especificar o nível de isolamento da transação. O valor mais comumente usado é READ_COMMITTED.
* 3. Por padrão, a transação declarativa do Spring reverte todas as exceções de tempo de execução, que também podem ser definidas por meio das propriedades correspondentes. Normalmente, o valor padrão é suficiente.
* 4. Use readOnly para especificar se a transação é somente leitura. Indica que esta transação apenas lê dados, mas não os atualiza, o que pode ajudar o mecanismo de banco de dados a otimizar a transação. Se for realmente um método que apenas lê o banco de dados, readOnly=true deverá ser definido.
* 5. Use timeOut para especificar o tempo que uma transação pode levar antes de forçar uma reversão.
*/
@Transacional(propagação=Propagação.REQUIRES_NEW,
isolamento = Isolamento.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly = verdadeiro, tempo limite = 3)
@Substituir
compra pública void(String nome de usuário, String isbn) {
//1. Obtenha o preço unitário do livro
preço int = bookShopDao.findBookPriceByIsbn(isbn);
//2. Atualizar inventário de livros
bookShopDao.updateBookStock(isbn);
//3. Atualizar saldo do usuário
bookShopDao.updateUserAccount(nome de usuário, preço);;
}
}
BookStockException
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
classe pública BookStockException estende RuntimeException {
/**
*
*/
privado estático final longo serialVersionUID = 1L;
public BookStockException() {
super();
// TODO stub do construtor gerado automaticamente
}
public BookStockException (String arg0, Throwable arg1, boolean arg2,
booleano arg3) {
super(arg0, arg1, arg2, arg3);
// TODO stub do construtor gerado automaticamente
}
public BookStockException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO stub do construtor gerado automaticamente
}
public BookStockException(String arg0) {
super(arg0);
// TODO stub do construtor gerado automaticamente
}
public BookStockException(Arremessável arg0) {
super(arg0);
// TODO stub do construtor gerado automaticamente
}
}
UserAccountException
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
classe pública UserAccountException estende RuntimeException {
/**
*
*/
privado estático final longo serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO stub do construtor gerado automaticamente
}
public UserAccountException (String arg0, Throwable arg1, boolean arg2,
booleano arg3) {
super(arg0, arg1, arg2, arg3);
// TODO stub do construtor gerado automaticamente
}
public UserAccountException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO stub do construtor gerado automaticamente
}
public UserAccountException(String arg0) {
super(arg0);
// TODO stub do construtor gerado automaticamente
}
public UserAccountException(Throwable arg0) {
super(arg0);
// TODO stub do construtor gerado automaticamente
}
}
Caixa
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
importar java.util.List;
interface pública Caixa {
checkout público vazio (String nome de usuário, List<String>isbns);
}
CaixaImpl. CashierImpl.checkout e bookShopService.purchase testam em conjunto o comportamento de propagação da transação
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
importar java.util.List;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.stereotype.Service;
importar org.springframework.transaction.annotation.Transactional;
@Service("caixa")
classe pública CashierImpl implementa Cashier {
@Autowired
bookShopService privado bookShopService;
@Transacional
@Substituir
public void checkout(String nome de usuário, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase (nome de usuário, isbn);
}
}
}
Aula de teste:
Copie o código do código da seguinte forma:
pacote com.yl.spring.tx;
importar java.util.Arrays;
importar org.junit.Test;
importar org.springframework.context.ApplicationContext;
importar org.springframework.context.support.ClassPathXmlApplicationContext;
classe pública SpringTransitionTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
Caixa particular caixa = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
caixa = ctx.getBean(Caixa.class);
}
@Teste
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Teste
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Teste
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Teste
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Teste
public void testTransactionPropagation(){
caixa.checkout("AA", Arrays.asList("1001", "1002"));
}
}
Espero que este artigo seja útil para o design de programação Spring de todos.