Você está na página 1de 29

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; Evita rollback ao fim do teste. Desta
forma, poderemos visualizar os registros
@Test inseridos.
@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);
}
} 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/spring-
framework-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-in-
spring-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-interface-
implementation>.
• 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