Spring Framework Parte 04 transações
Spring e transações O uso de transações é recorrente no desenvolvimento de sistema corporativos. Spring provê suporte ao controle de transações de duas maneiras: programática e declarativa. No geral, o gerenciamento declarativo é preferível por ser mais simples. Spring fica responsável pelo trabalho sujo (commit, rollback, start, etc). No gerenciamento programático a responsabilidade fica com o programador. Com Spring podemos usufruir de um ambiente transacional sem a presença de um contêiner JEE. 2
Políticas transacionais De propagação: define os limites de uma transação. De isolamento: como os dados são lidos quando há transações concorrentes. De manipulação de dados: por padrão uma transação é definida como de leitura e escrita de dados, mas é possível indicar que a transação apenas realiza leitura, promovendo uma otimização de desempenho. De tempo de espera: define tempos máximos para execução de transações. De rollback: define quais os tipos de erros que disparam um procedimento de rollback. Por padrão, exceções não checadas geram rollback enquanto que exceções checadas não geram. 3
Políticas de propagação PROPAGATION_MANDATORY: o método só pode ser executado dentro de uma transação. Gera exceção caso não haja uma transação em andamento. PROPAGATION_NEVER: o método não pode ser executado dentro de uma transação. Gera exceção caso haja uma transação em andamento. PROPAGATION_NOT_SUPPORTED: o método não deve ser executado dentro de uma transação. Caso haja uma em andamento, a transação é paralisada durante a execução do método. PROPAGATION_REQUIRED: o método é executado dentro de uma transação. Usa a transação corrente ou cria uma nova caso não exista. É a política padrão. 4
Políticas de propagação PROPAGATION_SUPPORTS: o método pode ser executado ou não dentro de uma transação. Caso seja, fará parte da unidade de trabalho da transação. PROPAGATION_REQUIRES_NEW: o método cria uma nova transação independente. Caso haja uma transação em andamento, esta é paralisada até que a nova transação termine. PROPAGATION_NESTED: a execução do método cria uma nova transação ou uma transação aninhada. Caso haja uma transação anterior, a transação aninhada somente será commitada quando a anterior também for. Caso a transação aninhada falhe, ela não causará rollback na transação anterior. 5
Isolamento e leitura de dados Dependendo da política de isolamento adotada, podem ocorrer as seguintes formas de leitura de dados: Dirty reads: a transação lê dados ainda não commitados por outras transações. Non repeatable reads: execuções da mesma consulta retornam resultados diferentes pois outra transação está alterando os dados. Phantom reads: a transação lê um número de registros maior do que o esperado pois outra transação está incluindo novos registros. 6
Políticas de isolamento ISOLATION_DEFAULT: a transação utiliza o comportamento padrão do banco de dados (política de isolamento default). ISOLATION_READ_UNCOMMITED: permite dirty reads, non repeatable reads e phantom reads. ISOLATION_READ_COMMITED: evita dirty reads mas permite non repeatable reads e phantom reads. ISOLATION_REPEATABLE_READ: evita dirty reads e non repeatable reads mas permite phantom reads. ISOLATION_SERIALIZABLE: evita dirty reads e non repeatable reads mas não permite phantom reads. 7
@Transactional Marca um método ou classe como transacional. As políticas são indicadas através de suas propriedades: propagation: item da enum org.springframework.transaction.annotation.propagation. isolation: item da enum org.springframework.transaction.annotation.isolation. readonly: booleano que indica se a transação é de apenas leitura. Seu valor padrão é falso. timeout: tempo limite, em segundos, para execução da transação. rollbackfor e rollbackforclassname: indicam que classes de exceção causam rollback. norollbackfor e norollbackforclassname: indicam que classes de exceção não causam rollback. 8
Habilitando transações declarativas Declarar um gerenciador de transações, o qual deve ser específico da tecnologia de acesso a dados (e.g., JDBC, JPA, Hibernate). Adicionar a anotação @EnableTransactionManagement nas configurações do Spring. Consulte a classe AppConfig e repare que essas configurações já foram realizadas anteriormente no projeto. 9
Transações e classes de proxy Spring utiliza classes de proxy na mágica das transações declarativas: as classes reais são substituídas por classes criadas em tempo de execução. Essas classes de proxy contém o código responsável pela controle de transações. Existem dois mecanismos para criar proxies de classes que implementam interfaces: mecanismo do JDK (default) ou biblioteca CGLIB. O mecanismo do JDK cria uma nova classe que implementa a interface da classe original. CGLIB cria uma subclasse da classe original. 10
Problema com ModeloDAOTest Na aula anterior ModeloDAO foi anotada com @Transactional. A execução de ModeloDAOTest agora gera um erro (falha na injeção do objeto dao). A classe proxy implementa ModeloRepository e portanto é de tipo incompatível com o atributo dao. Alternativas: Alterar o tipo de dao para ModeloRepository; Ou configurar a aplicação para usar CGLIB: em AppConfig, adicionar a propriedade proxytargetclass=true na anotação @EnableTransactionManagement. 11
Preparação para os exemplos Entidade Compra (1): package cursospring.revenda_veiculos.dominio; import java.math.bigdecimal; import javax.persistence.entity; import javax.persistence.joincolumn; import javax.persistence.manytoone; import javax.persistence.table; @Entity @Table(name="COMPRAS") public class Compra extends Entidade { private BigDecimal valor; 12
Preparação para os exemplos Entidade Compra (2): @ManyToOne @JoinColumn(name="ID_VEICULO") private Veiculo veiculo; public Compra() {} public Compra(Integer id, BigDecimal valor, Veiculo veiculo) { super(id); this.valor = valor; this.veiculo = veiculo; } } //getters e setters 13
Preparação para os exemplos CompraRepository: package cursospring.revenda_veiculos.dominio; public interface CompraRepository { Integer inserir(compra c); } 14
Preparação para os exemplos CompraDAO (1): package cursospring.revenda_veiculos.dao; import org.hibernate.sessionfactory; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.repository; import org.springframework.transaction.annotation.transactional; import cursospring.revenda_veiculos.dominio.compra; import cursospring.revenda_veiculos.dominio.comprarepository; @Repository @Transactional public class CompraDAO implements CompraRepository { @Autowired private SessionFactory sessionfactory; 15
Preparação para os exemplos CompraDAO (2): } @Override public Integer inserir(compra c) { sessionfactory.getcurrentsession().save(c); return c.getid(); } 16
Preparação para os exemplos CompraService (1): package cursospring.revenda_veiculos.dominio; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service; import org.springframework.transaction.annotation.propagation; import org.springframework.transaction.annotation.transactional; import cursospring.revenda_veiculos.dao.compradao; import cursospring.revenda_veiculos.dao.veiculodao; @Service public class CompraService { @Autowired private CompraDAO compradao; @Autowired private VeiculoDAO veiculodao; 17
Preparação para os exemplos CompraService (2): } @Transactional public void registrar(compra c){ veiculodao.inserir(c.getveiculo()); compradao.inserir(c); } 18
Preparação para os exemplos Classe AppConfig: adicionar pacote dominio. package cursospring.revenda_veiculos.config;... @Configuration @ComponentScan(basePackageClasses={FabricanteDAO.class, CompraService.class}) @EnableTransactionManagement(proxyTargetClass=true) public class AppConfig {... } 19
Preparação para os exemplos CompraServiceTest (1): package cursospring.revenda_veiculos.dominio; import java.math.bigdecimal; import org.junit.test; import org.springframework.beans.factory.annotation.autowired; import org.springframework.test.annotation.rollback; import org.springframework.test.context.activeprofiles; import org.springframework.test.context.contextconfiguration; import org.springframework.test.context.junit4.abstracttransactionaljunit4s pringcontexttests; import cursospring.revenda_veiculos.config.appconfig; import cursospring.revenda_veiculos.config.appconfigtest; @ContextConfiguration(classes={AppConfig.class, AppConfigTest.class}) @ActiveProfiles("test") 20
Preparação para os exemplos CompraServiceTest (2): public class CompraServiceTest extends AbstractTransactionalJUnit4SpringContextTests{ @Autowired CompraService service; } @Test @Rollback(false) public void testregistrar(){ Veiculo v = new Veiculo(null, "MNP0001"); v.setanofabricacao(2012); v.setmodelo(new Modelo(1, null, null)); Compra c = new Compra(null, new BigDecimal("23000"), v); //Compra c = new Compra(null, null, v); service.registrar(c); } 21
Preparação para os exemplos CompraServiceTest (2): public class CompraServiceTest extends AbstractTransactionalJUnit4SpringContextTests{ } @Autowired CompraService service; @Test @Rollback(false) public void testregistrar(){ Veiculo v = new Veiculo(null, "MNP0001"); v.setanofabricacao(2012); v.setmodelo(new Modelo(1, null, null)); } Evita rollback ao fim do teste. Desta forma, poderemos visualizar os registros inseridos. Compra c = new Compra(null, new BigDecimal("23000"), v); //Compra c = new Compra(null, null, v); service.registrar(c); 22
Preparação para os exemplos Manualmente, crie a tabela COMPRAS no banco de dados: create table COMPRAS (ID int auto_increment, VALOR decimal not null, ID_VEICULO int not null, primary key (ID), foreign key (ID_VEICULO) references VEICULOS); 23
Teste 1 Execute CompraServiceTest. Verifique que foram inseridos registros nas tabelas VEICULOS e COMPRAS. 24
Teste 2 Manualmente, remova os registros inseridos pelo teste anterior. Em CompraServiceTest, defina o valor da compra como null. Execute CompraServiceTest. Verifique que desta vez não houve a inserção de registros. 25
Teste 3 Em CompraService, adicione propagation=propagation.not_supported na anotação @Transactional. Execute CompraServiceTest. Verifique que apenas a inserção de registro ocorre apenas na tabela VEICULOS. 26
Referências How to use. Spring Proxying Mechanisms. Disponível em <http://how-use.com/spring-proxying-mechanisms>. Johnson, Rod et al. Spring Framework Reference Documentation, 4.2.1 release. Disponível em <http://docs.spring.io/spring/docs/current/springframework-reference/html/>. Souza, Alberto. Spring MVC: domine o principal framework web Java. São Paulo: Casa do Código, 2015. Stack Overflow. Differences between requires_new and nested propagation in Spring transactions. Disponível em <http://stackoverflow.com/questions/12390888/difference s-between-requires-new-and-nested-propagation-inspring-transactions>. 27
Referências Stack Overflow. Spring - how to inject concrete interface implementation?. Disponível em <http://stackoverflow.com/questions/28925311/s pring-how-to-inject-concrete-interfaceimplementation>. Weissmann, Henrique L. Vire o jogo com Spring Framework. São Paulo: Casa do Código, 2013. 28
Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Norte Campus Natal Central Diretoria Acadêmica de Gestão e Tecnologia da Informação Curso de formação em Spring Framework 4 Parte 04 Transações Autor: Alexandre Gomes de Lima Natal, outubro de 2015. 29