Você está na página 1de 47
Spring Framework Parte 02 – acesso a dados e testes de integração

Spring Framework

Parte 02 acesso a dados e testes de

integração

Spring Framework Parte 02 – acesso a dados e testes de integração

H2 Database

H2 é um SGBDR escrito em Java que pode atuar tanto no modo embutido como na forma cliente-

servidor.

Após instalação, executar o script h2.bat, ou h2.sh, presente no diretório bin. Em seguida, usar o navegador para acessar o endereço

http://192.168.56.1:8082/.

Dependências

<dependency>

<groupId>com.h2database</groupId>

<artifactId>h2</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

</dependency>

Classes de configuração

Classe AppConfig: configurações gerais da aplicação que não possuem relação com a parte

web.

Class AppConfigTest: configurações específicas para execução de testes automatizados.

Classes de configuração: AppConfig

package cursoSpring.revenda_veiculos.config;

import javax.sql.DataSource; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration public class AppConfig {

@Bean public DataSource dataSource(){ DriverManagerDataSource ds = new DriverManagerDataSource();

ds.setDriverClassName("org.h2.Driver");

ds.setUrl("jdbc:h2:tcp://localhost/~/curso_spring");

ds.setUsername("sa"); ds.setPassword(""); return ds;

}

}

5

Classes de configuração: AppConfig

package cursoSpring.revenda_veiculos.config;

de desenvolvimento. Para produção, deve-se utilizar uma implementação que trabalhe com pool de conexões tal
de desenvolvimento. Para produção, deve-se utilizar
uma implementação que trabalhe com pool de
conexões tal como Apache Commons DBCP ou C3P0.
DriverManagerDataSource ds = new DriverManagerDataSource();

ds.setDriverClassName("org.h2.Driver");

ds.setUrl("jdbc:h2:tcp://localhost/~/curso_spring");

ds.setUsername("sa"); ds.setPassword(""); return ds;

import javax.sql.DataSource; DriverManagerDataSource é uma implementação de

import org.springframework.context.annotation.Configuration; DataSource que deve ser utilizada apenas em tempo

import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration public class AppConfig {

@Bean public DataSource dataSource(){

}

}

6

Classes de configuração: AppConfigTest

package cursoSpring.revenda_veiculos.config;

import org.springframework.context.annotation.Profile;

Crie esta classe na source folder src/test/java.

@Configuration public class AppConfigTest {

@Bean

@Profile("test") public DataSource dataSource(){ DriverManagerDataSource ds = new DriverManagerDataSource();

ds.setDriverClassName("org.h2.Driver");

ds.setUrl("jdbc:h2:tcp://localhost/~/curso_spring_test");

ds.setUsername("sa"); ds.setPassword(""); return ds;

}

}

7

Entidade genérica

package cursoSpring.revenda_veiculos.dominio;

import java.io.Serializable;

public abstract class Entidade implements Serializable{ private Integer id; public Entidade() {} public Entidade(Integer id) {

super(); this.id = id;

}

public Integer getId() {

}

Use o assistente do Eclipse para criar os métodos getId, setId, equals e hashCode.

public void setId(Integer id) { @Override public int hashCode() {

@Override public boolean equals(Object obj) {

}

}

}

}

Entidade Fabricante

package cursoSpring.revenda_veiculos.dominio;

public class Fabricante extends Entidade{ private String descricao; public Fabricante() {} public Fabricante(Integer id, String descricao) { super(id); this.descricao = descricao;

}

}

public String getDescricao() {

public void setDescricao(String descricao) {

}

}

FabricanteRepository

package cursoSpring.revenda_veiculos.dominio;

import java.util.List;

public interface FabricanteRepository { List<Fabricante> todos(); Fabricante getPorId(Integer idFabricante); Integer inserir(Fabricante f); void atualizar(Fabricante f); void excluir(Integer idFabricante);

}

Spring JDBC

Spring oferece classes que, se comparadas ao JDBC puro,

tornam mais simples o código de interação com banco de dados. Além disso, provê uma hierarquia de exceções não

verificadas que facilitam o tratamento de exceções.

Algumas classes:

JdbcTemplate: facilita a manipulação de registros (select, insert, update e delete).

SimpleJdbcInsert: facilita a inserção de registros.

BeanPropertyRowMapper: mapeamento pré-definido entre

atributos de um objeto e as colunas da tabela, facilitando as operações de consulta.

EmptyResultDataAccessException: exceção indicativa de que uma consulta vazia, ou seja, sem registros.

Spring JDBC: hierarquia parcial de

exceções

Spring JDBC: hierarquia parcial de exceções 12

FabricanteDAO (1)

package cursoSpring.revenda_veiculos.dao;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository;

import cursoSpring.revenda_veiculos.dominio.Fabricante; import cursoSpring.revenda_veiculos.dominio.FabricanteRepository;

@Repository public class FabricanteDAO implements FabricanteRepository {

@Autowired private DataSource dataSource;

FabricanteDAO (1)

package cursoSpring.revenda_veiculos.dao; @Repository marca import FabricanteDAO javax.sql.DataSource; como um bean
package cursoSpring.revenda_veiculos.dao;
@Repository marca
import FabricanteDAO javax.sql.DataSource; como um bean
Spring da camada de persistência.
import org.springframework.beans.factory.annotation.Autowired;
@Autowired indica uma injeção por
Isto faz com que as exceções
import org.springframework.dao.EmptyResultDataAccessException;
tipo. Assim, o contexto do Spring irá
lançadas por FabricanteDAO sejam
import org.springframework.jdbc.core.BeanPropertyRowMapper;
buscar um bean do tipo DataSource
encapsuladas em exceções do tipo
import org.springframework.jdbc.core.JdbcTemplate;
para injetar neste atributo.
DataAccessExcpetion, tornando as
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
mensagens de erro mais claras.
import org.springframework.stereotype.Repository;
import cursoSpring.revenda_veiculos.dominio.Fabricante;
import cursoSpring.revenda_veiculos.dominio.FabricanteRepository;
@Repository
public class FabricanteDAO implements FabricanteRepository {
@Autowired
private DataSource dataSource;

FabricanteDAO (2)

@Override public List<Fabricante> todos() { String sql = "select * from FABRICANTES";

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

return jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(Fabricante.class));

}

FabricanteDAO (2)

@Override public List<Fabricante> todos() { String sql = "select * from FABRICANTES"; JdbcTemplate
@Override
public List<Fabricante> todos() {
String sql = "select * from FABRICANTES";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate.query(sql,
BeanPropertyRowMapper.newInstance(Fabricante.class));
}
O método query varre os registros obtidos na consulta e retorna
uma lista parametrizada pelo RowMapper<T> utilizado como
parâmetro. BeanPropertyRowMapper é uma implementação
disponibilizada pelo Spring que casa o nome de um atributo (e.g,
numeroPlaca) com a coluna de mesmo nome (e.g., NUMEROPLACA
ou NUM_PLACA).

Consulta com JDBC puro

try{ Connection con = getConnection(); String sql = "select * from fabricantes";

PreparedStatement prep = con.prepareStatement(sql);

ResultSet rs = prep.executeQuery();

List<Fabricante> fabricantes = new ArrayList<Fabricante>(); while(rs.next()){ Fabricante f = new Fabricante(); f.setId(rs.getInt("ID"); f.setDescricao(rs.getString("DESCRICAO"));

fabricantes.add(f);

}

return fabricantes; }catch(SQLException ex){

}

FabricanteDAO (3)

@Override public Fabricante getPorId(Integer idFabricante) { String sql = "select * from FABRICANTES where ID = ?";

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

try{ return jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(Fabricante.class), idFabricante); }catch(EmptyResultDataAccessException ex){ return null;

}

}

FabricanteDAO (4)

@Override public Integer inserir(Fabricante f) { SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource) .withTableName("FABRICANTES")

.usingGeneratedKeyColumns("ID"); Map<String, Object> params = new HashMap<>(); params.put("DESCRICAO", f.getDescricao()); Integer id = jdbcInsert.executeAndReturnKey(params).intValue(); f.setId(id); return id;

}

FabricanteDAO (5)

@Override public void atualizar(Fabricante f) { String sql = "update FABRICANTES set DESCRICAO=? where ID=?"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.update(sql, f.getDescricao(), f.getId());

}

@Override public void excluir(Integer idFabricante) { String sql = "delete from FABRICANTES where ID = ?";

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.update(sql, idFabricante);

}

}

Atualizando AppConfig

Adicionando o pacote cursoSpring.revenda_veiculos.dao ao contexto

do Spring:

package cursoSpring.revenda_veiculos.config;

@Configuration @ComponentScan(basePackageClasses={FabricanteDAO.class}) public class AppConfig {

}

Testes de integração

Os testes de integração devem se comunicar com o banco de dados. Para tal, é preciso que o código

de testes tenha acesso ao contexto do Spring.

Spring oferece suporte aos frameworks JUnit e TestNG.

Vamos utilizar os recursos do Eclipse, ao invés do Maven, para execução dos testes JUnit. Portanto, devemos adicionara a biblioteca do JUnit 4

através do build path do projeto.

Testes de integração

Para que uma classe de testes do JUnit tenha acesso ao contexto do Spring, podemos marcá-la

com @RunWith(SpringJUnit4ClassRunner.class)

ou defini-la como subclasse de

AbstractTransactionalJUnit4SpringContextTests.

A anotação @ActiveProfiles será utilizada para

indicar o profile utilizado nos testes. Dessa forma, os testes de integração utilizarão o DataSource

definido em AppConfigTest.

Scripts SQL

Criar os seguintes scripts em src/test/java:

create-schema.sql

create table FABRICANTES (ID int auto_increment, DESCRICAO varchar(40) unique not null, primary key(ID)); insert into FABRICANTES (ID, DESCRICAO) values (1, 'CHEVROLET/GM'); insert into FABRICANTES (ID, DESCRICAO) values (2, 'FIAT'); insert into FABRICANTES (ID, DESCRICAO) values (3, 'FORD'); insert into FABRICANTES (ID, DESCRICAO) values (4, 'VOLKSWAGEN'); insert into FABRICANTES (ID, DESCRICAO) values (5, 'RENAULT');

drop-schema.sql

drop table FABRICANTES;

FabricanteDAOTest (1)

package cursoSpring.revenda_veiculos.dao;

import org.springframework.test.context.ActiveProfiles;

import org.springframework.test.context.ContextConfiguration; import

org.springframework.test.context.junit4.AbstractTransactionalJUnit4S

pringContextTests;

@ContextConfiguration(classes={AppConfig.class,

AppConfigTest.class}) @ActiveProfiles("test") public class FabricanteDAOTest extends

AbstractTransactionalJUnit4SpringContextTests{

@Autowired private FabricanteDAO dao;

FabricanteDAOTest (2)

@Before public void setUp(){ executeSqlScript("classpath:/create-schema.sql", false);

}

@After public void tearDown(){ executeSqlScript("classpath:/drop-schema.sql", false);

}

@Test public void testTodos() { List<Fabricante> lista = dao.todos(); Assert.assertEquals(5, lista.size());

}

FabricanteDAOTest (3)

@Test public void testGetPorId_1(){ Fabricante f = dao.getPorId(1);

Assert.assertEquals(1, f.getId().intValue()); Assert.assertEquals("CHEVROLET/GM", f.getDescricao());

}

@Test public void testGetPorId_2(){ Fabricante f = dao.getPorId(20); Assert.assertNull(f);

}

@Test public void testInserir(){ Fabricante f = new Fabricante(null, "TESTE"); Integer id = dao.inserir(f); Assert.assertNotNull(id);

}

27

FabricanteDAOTest (4)

@Test public void testAtualizar(){ Integer idFabricante = 3;

String novaDescricao = "NISSAN"; Fabricante f = new Fabricante(idFabricante, novaDescricao);

dao.atualizar(f); String descricao = jdbcTemplate.queryForObject( "select DESCRICAO from FABRICANTES where ID = ?", String.class, idFabricante); Assert.assertEquals(novaDescricao, descricao);

}

@Test public void testExcluir(){ Integer idFabricante = 2; dao.excluir(idFabricante); Fabricante f = dao.getPorId(idFabricante); Assert.assertNull(f);

}}

28

DAO com Hibernate

Adicionar as dependências.

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

</dependency>

Configurando Hibernate: AppConfig (1)

import

org.springframework.orm.hibernate4.HibernateTransactionManager;

import org.springframework.orm.hibernate4.LocalSessionFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManageme nt;

@Configuration @ComponentScan(basePackageClasses={FabricanteDAO.class}) @EnableTransactionManagement public class AppConfig {

Configurando Hibernate: AppConfig (2)

@Bean public LocalSessionFactoryBean sessionFactory(){

LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource()); sessionFactory.setPackagesToScan ("cursoSpring.revenda_veiculos.dominio"); sessionFactory.setHibernateProperties(hibernateProperties()); return sessionFactory;

}

Configurando Hibernate: AppConfig (3)

private Properties hibernateProperties(){ Properties props = new Properties(); props.put("hibernate.dialect",

"org.hibernate.dialect.H2Dialect");

props.put("hibernate.show_sql", "true"); props.put("hibernate.format_sql", "true"); return props;

}

@Bean public PlatformTransactionManager transactionManager(){ return new HibernateTransactionManager( sessionFactory().getObject());

}

}

Scripts SQL

Adicionar instruções:

create-schema.sql

create table MODELOS (ID int auto_increment, DESCRICAO varchar(40) not null, ID_FABRICANTE int not null, primary key(ID), foreign key (ID_FABRICANTE) references FABRICANTES); insert into MODELOS (ID, DESCRICAO, ID_FABRICANTE) values (1, 'CORSA', 1); insert into MODELOS (ID, DESCRICAO, ID_FABRICANTE) values (2, 'GOL',

4);

insert into MODELOS (ID, DESCRICAO, ID_FABRICANTE) values (3, 'PALIO', 2);

drop-schema.sql

drop table MODELOS;

33

Atualizando classe Entidade

import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass;

@MappedSuperclass public abstract class Entidade implements Serializable{

@Id

@GeneratedValue(strategy=GenerationType.AUTO)

private Integer id;

}

Atualizando classe Fabricante

import javax.persistence.Entity; import javax.persistence.Table;

@Entity @Table(name="FABRICANTES") public class Fabricante extends Entidade{

}

Entidade Modelo (1)

package cursoSpring.revenda_veiculos.dominio;

import javax.persistence.Entity;

import javax.persistence.JoinColumn;

import javax.persistence.ManyToOne; import javax.persistence.Table;

@Entity

@Table(name="MODELOS")

public class Modelo extends Entidade {

private String descricao;

@ManyToOne @JoinColumn(name="ID_FABRICANTE") private Fabricante fabricante;

Entidade Modelo (2)

public Modelo() {}

public Modelo(Integer id, String descricao, Fabricante fabricante)

{

super(id); this.descricao = descricao; this.fabricante = fabricante;

}

public String getDescricao() {

}

public void setDescricao(String descricao) {

public Fabricante getFabricante() {

}

}

public void setFabricante(Fabricante fabricante) {

}

}

Interface ModeloRepository

package cursoSpring.revenda_veiculos.dominio;

import java.util.List;

public interface ModeloRepository {

List<Modelo> todos();

Modelo getPorId(Integer idModelo);

Integer inserir(Modelo m);

void atualizar(Modelo m);

void excluir(Integer idModelo);

}

Classe ModeloDAO (1)

package cursoSpring.revenda_veiculos.dao;

import java.util.List;

import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository;

import cursoSpring.revenda_veiculos.dominio.Modelo; import cursoSpring.revenda_veiculos.dominio.ModeloRepository;

@Repository public class ModeloDAO implements ModeloRepository {

@Autowired private SessionFactory sessionFactory;

Classe ModeloDAO (2)

@Override public List<Modelo> todos() {

Session session = sessionFactory.getCurrentSession();

return session.createQuery("from Modelo").list();

}

@Override public Modelo getPorId(Integer idModelo) {

Session session = sessionFactory.getCurrentSession();

return (Modelo)session.get(Modelo.class, idModelo);

}

@Override public Integer inserir(Modelo m) { sessionFactory.getCurrentSession().save(m); return m.getId();

}

Classe ModeloDAO (3)

@Override public void atualizar(Modelo m) { sessionFactory.getCurrentSession().merge(m);

}

@Override public void excluir(Integer idModelo) { String hql = "delete Modelo where id = :idModelo";

Session session = sessionFactory.getCurrentSession();

Query q = session.createQuery(hql) .setParameter("idModelo", idModelo); q.executeUpdate();

}

}

Classe ModeloDAOTest (1)

package cursoSpring.revenda_veiculos.dao;

@ContextConfiguration(classes={AppConfig.class, AppConfigTest.class}) @ActiveProfiles("test") public class ModeloDAOTest extends

AbstractTransactionalJUnit4SpringContextTests{

@Autowired private ModeloDAO dao;

@Autowired private SessionFactory sessionFactory;

Classe ModeloDAOTest (2)

@Test public void testTodos() { List<Modelo> lista = dao.todos();

Assert.assertEquals(3, lista.size());

}

@Test public void testGetPorId_1(){ Integer idModelo = 2;

Modelo m = dao.getPorId(idModelo); Assert.assertEquals(idModelo, m.getId()); Assert.assertEquals("GOL", m.getDescricao());

}

@Test public void testGetPorId_2(){ Modelo m = dao.getPorId(20); Assert.assertNull(m);

Classe ModeloDAOTest (3)

@Test public void testInserir(){ Fabricante f = new Fabricante(1, null);

Modelo m = new Modelo(null, "TESTE", f);

Integer id = dao.inserir(m); Assert.assertNotNull(id);

}

@Test

public void testAtualizar(){ Integer idModelo = 1; String novaDescricao = "CELTA"; Fabricante f = new Fabricante(1, null); Modelo m = new Modelo(idModelo, novaDescricao, f); dao.atualizar(m); Modelo m2 = (Modelo)sessionFactory.getCurrentSession() .get(Modelo.class, idModelo); Assert.assertEquals(novaDescricao, m2.getDescricao());

Classe ModeloDAOTest (4)

@Test public void testExcluir(){ Integer idModelo = 3;

dao.excluir(idModelo); Modelo m = dao.getPorId(idModelo); Assert.assertNull(m);

}

@Before

public void setUp(){ executeSqlScript("classpath:/create-schema.sql", false);

}

@After public void tearDown(){ executeSqlScript("classpath:/drop-schema.sql", false);

}

}

Referências

Deinum, Marten et al. Spring Recipes: a problem- solution approach, 3ª ed. Apress, 2014.

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.

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 02 acesso a dados e testes de integração

Autor: Alexandre Gomes de Lima Natal, outubro de 2015.