Back-End

17 jul, 2013

Série Spring: CRUD Spring 3 + Hibernate + Unit Tests

Publicidade

Olá, pessoal! No artigo de hoje veremos como fazer um simples CRUD usando o Spring, integrando com Hibernate e testando via unit tests com JUNIT4. No último artigo vimos apenas uma integração com o Hibernate e também um CRUD usando HibernateTemplate. Hoje veremos que na versão 3 do Spring não precisamos mais do HiberanteTemplate e vamos testar nosso código com unit test.

Pra começar, vou seguir supondo que você já tem o .jars necessário para o ambiente. Já que esse não é o primeiro artigo da série (caso contrário terá que baixar os .jars para: driver Mysql 5, Hibernate 3.6, AOP, AspectJ, Spring 3.x).

Antes de começarmos a desenvolver, vamos primeiro estruturar nosso projeto e packages conforme a imagem a seguir:

springrentcarproject

Nosso CRUD será o cadastro  de veículos  que serão alugados. Claro que há muito mais regras de negócios do que a que veremos no exemplo a seguir, porém o nosso objetivo é explorar o framework Spring e não tratar todas as regras de negócio em um artigo.

  1. Crie a estrutura conforme a imagem acima;
  2. Crie o arquivo de configuração de Spring, o qual chamei de springconfiguration.xml, e coloque dentro do package config;
  3. Agora vamos colocar a carne no nosso XML.

O cabeçalho fica assim:

<beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:context=“http://www.springframework.org/schema/context”
xmlns:tx=“http://www.springframework.org/schema/tx”
xmlns:aop=“http://www.springframework.org/schema/aop”
xsi:schemaLocation=“http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd”>

Estarei comentando mais na frente ou in line apenas o que  ainda não foi abordado nos posts anteriores.

O cara que busca os beans:

<context:component-scan base-package=“*”/>

Colocando translation:

<!– esse cara faz as traduções das exception checked para as unchecked do Spring –>
<bean class=“org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor”/>

Data source

<!– pelo nome já diz, data source para conexão com o DB –>
<bean id=“dataSource” class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>
<property name=“driverClassName” value=“com.mysql.jdbc.Driver”/>
<property name=“url” value=“jdbc:mysql://localhost/test”/>
<property name=“username” value=“root”/>
<property name=“password” value=“camilo2593″/>
</bean>

Configurando a SessionFactory

<!– Aqui estamos definido como será a parte de persistêcia, e dizemos que faremos via annotation –>
<bean id=“sessionFactory” class=“org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”>
<!– informando a quem estaremos conectado –>
<property name=“dataSource” ref=“dataSource”/>
<!– dizendo onde estão as classes dominio, ele saberá devido anotação @Entity –>
<property name=“packagesToScan” value=“br.com.camilolopes.rentcar.domain.bean”/>
<!– configurando coisas especificas do Hibernate –>
<property name=“hibernateProperties”>
<props>
<prop key=“hibernate.dialect”>org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key=“hibernate.hbm2ddl.auto”>update</prop>
</props>
</property>
</bean>

Definindo Transaction

<!– definindo quem vai gerenciar as transaction, ou seja, será o Hibernate –>

<bean id=“transactionManager” class=“org.springframework.orm.hibernate3.HibernateTransactionManager”>
<property name=“sessionFactory” ref=“sessionFactory”/>
</bean>
</beans>

Há duas formas de declararmos as transaction no Spring por XML ou Annotation. Veremos as duas formas, no nosso caso, de usarmos o XML com AOP e você entenderá o motivo.

Via XML

<!– todo medoto que começa com add é required uma transaction –>
<tx:advice id=“txAdvice”>
<tx:attributes>
<tx:method name=“add*” propagation=“REQUIRED”/>
<tx:method name=“update*” propagation=“REQUIRED”/>
<tx:method name=“*” propagation=“SUPPORTS” read-only=“true”/>
</tx:attributes>
</tx:advice>
<!– toda classe que extends a interface terá uma referência para o advisor –>
<aop:config>
<aop:advisor pointcut = “execution(* *..RentCar.*(..)))” advice-ref=“txAdvice”/>
</aop:config>

Em alguns contextos é bem mais prático que usar Annotations.

Via annotation

Remove todo o código XML acima e apenas adiciona a seguinte linha:

<tx:annotation-driven/>

Claro que teremos que anotar nas classes/métodos como @Transaction e informar como esse deve se comportar, seguindo o exemplo do XML acima seria algo assim:

@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class myClass{ }

Normalmente, você colocaria isso na camada de serviço. Mas, se você está brincando e não tem uma camada de serviço, pode colocar direto no DAO. E o método que faria uma das operações de banco ficaria assim:

@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
public void save(Car car) {
getCurrentSession().save(car);}

No nosso exemplo não vamos usar Annotations e exploraremos o benefício de usar a versão do XML conforme acima, se você deixar a tag para annotation no arquivo de configuração, o Spring não vai se importar com isso.

Desenvolvimento

Após toda essa configuração, vamos agora desenvolver nossas classes .java. Começando pela classe de domínio:

@Entity
@Table(name=”CARS”)
public class Car implements Serializable{
private static final long serialVersionUID = -2896368465389020843L;
@Id
@GeneratedValue
private Long id;
private String manufacter;
private String description;
private BigDecimal marketValue;
//getters/setters omitidos

Basicamente isso.

Interface DAO

public interface RentCarDAO {
void save(Car car);
List<Car> findAll();
List<Car> findAllByDescription(String description);
void update(Car car);
void delete();
}

Implementação da interface:

@Repository
public class RentCarDAOImpl implements RentCarDAO {
@Autowired
private SessionFactory sessionFactory;
public Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
@Override
public void save(Car car) {
getCurrentSession().save(car);
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public List<Car> findAll() {
return getCurrentSession().createCriteria(Car.class).list();
}
@Override
public List<Car> findAllByDescription(String description) {
Criteria createCriteria = getCurrentSession().createCriteria(Car.class);
createCriteria.add(Restrictions.ilike(“description”, description));
return (List<Car>) createCriteria.list();
}
@Override
public void update(Car car) {
getCurrentSession().update(car);
}
@Override
public void delete() {
Query query = getCurrentSession().createQuery(“delete from Car where id >0″);
query.executeUpdate();
}

Interface de serviço:

@Service
public interface RentCar {
void addNewCar(Car car);
List<Car> findAll();
List<Car> findCarByDescription(String description);
void updateCar(Car car);
void delete();
}

Classe que implementa o serviço:

@Service
public class RentCarServiceImpl implements RentCar {
@Autowired
private RentCarDAO rentCarDAO;
@Override
public void addNewCar(Car car) {
rentCarDAO.save(car);
}
public void setRentCarDAO(RentCarDAO rentCarDAO) {
this.rentCarDAO = rentCarDAO;
}
@Override
public List<Car> findAll() {
return rentCarDAO.findAll();
}
@Override
public List<Car>findCarByDescription(String description) {
return (List<Car>) rentCarDAO.findAllByDescription(description);
}
@Override
public void updateCar(Car car) {
rentCarDAO.update(car);
}
@Override
public void delete() {
rentCarDAO.delete();
}
}

Unit Test

Se você não reparou quando criou o projeto, temos um source apenas para os unit tests, conforme a imagem a seguir:

springrentcarunittestssource

Agora crie a classe de teste. A seguir, mostrarei apenas dois simples testes. Você deve criar os demais para validar os cenários – não coloquei todos para não ficar ainda mais longo o artigo.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={“classpath:config/springconfiguration.xml”})
public class RentCarServicesTest {
@Autowired
private RentCar rentCarServicesImpl;
private Car car;
@Before
public void setUp() throws Exception {
car = new Car();
}
@Test
public void testAddingNewCarWithSuccess(){
try{
car.setDescription(“Civic”);
car.setManufacter(“Honda”);
car.setMarketValue(new BigDecimal(740000));
rentCarServicesImpl.addNewCar(car );
}catch (Exception e) {
Assert.fail(“not expected result”);
}
}
@Test
public void testListAllCarIsNotEmpty(){
assertFalse(rentCarServicesImpl.findAll().isEmpty());
}

Resultado

Lembre-se que seu BD precisa estar rodando:

springhibernateunittestresult

Ufa! Esse artigo foi longo, mas não tinha outra forma de mostrar tudo se não fosse assim… E olha que busquei resumir ao máximo possível.

Enfim, vou ficando por aqui…

GitHub

Acesse o repositório no github com todos os projetos da série Spring:  https://github.com/camilolopes/workspacespring

Espero que tenham gostado do post.