quinta-feira, 6 de março de 2008

[Java] Desenvolvimento declarativo orientado a anotações sobre JSF/Spring/JPA

A arquitetura definida sobre a pilha JSF/Spring/JPA apresenta um altenativa interessante para gestão da configuração de aplicações web, permitindo produtividade pelo uso extensivo de programação declarativa, desacoplamento entre camadas e independencia da persistencia/visualização. Neste contexto, é proposto um projeto "mock", demonstrando a integração JSF/Spring/JPA fortemente orientada a "annotations". A proposta consiste em somente definir por xml os elementos basicos da infra-estrutura descrita tais como datasource, entityManagerFactory e transactionManager (utiliza-se MyFaces 1.2.2 com Facelets, Spring 2.5 e JPA sobre Hibernate).

A aplicação consiste de um "Xavecometro", destinado a cadasto de "xavecos" (contatos do sexo oposto). Neste contexto, existem duas categorias de "xavecos": "tchutchucas" e "bagangas". (As colegas nao se ofendam! É uma grande piada!)

Classe Xaveco

package net.jorgealbuquerque.tutorial.xavecometro.entidades;

//imports not displayed

@Entity
public class Xaveco implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Basic
private String nome;

@Basic
private Integer telefone;

@Enumerated
private Categoria categoria;

public Xaveco() {}

public Xaveco(String nome, Integer telefone) {
this.nome = nome;
this.telefone = telefone;
}

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}

public Integer getTelefone() {
return telefone;
}
public void setTelefone(Integer telefone) {
this.telefone = telefone;
}

public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}

public Categoria getCategoria() {
return categoria;
}
public void setCategoria(Categoria categoria) {
this.categoria = categoria;
}
}


Para a DAO, é definida uma interface generica para os operadores CRUD (IDAO)

Interface IDAO

package net.jorgealbuquerque.tutorial.xavecometro.dao;

//imports not displayed

public interface IDAO {

T getById(ID id);
void persist(T entity);
void update(T entity);
void delete(T entity);
List getAll();
}


Seguindo o contrato IDAO, apresenta-se a implementação com JPA. Note que como a entityManager é instanciada. Ao inves de exterder a JPATemplate com a anotação @PersistenceContext, a entityManager é resolvida pelo Spring.

Classe AbstractJPADAO

package net.jorgealbuquerque.tutorial.xavecometro.dao;

//imports are not displayed

public abstract class AbstractJPADAO implements IDAO {

private Class persistentClass;

protected EntityManager entityManager;

@SuppressWarnings("unchecked")
public AbstractJPADAO() {
this.persistentClass = (Class) ((ParameterizedType) getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
}

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}

public Class getPersistentClass() {
return persistentClass;
}

public T getById(ID id) {
return entityManager.find(persistentClass, id);
}

public void persist(T entity) {
entityManager.persist(entity);
}

public void update(T entity) {
entityManager.merge(entity);
}

public void delete(T entity) {
entityManager.remove(entity);
}

@SuppressWarnings("unchecked")
public List loadAll() {
return entityManager.createQuery("Select t from " +
persistentClass.getSimpleName() + " t").getResultList();
}
}


A interface IXavecoDAO apresenta as especializacoes (operações especificas no database) sobre a IDAO

Interface IXavecoDAO

package net.jorgealbuquerque.tutorial.xavecometro.dao;

//imports not displayed

public interface IXavecoDAO extends IDAO{

public List getByNome(String nome);
}



Finalmente, a classe XavecoDAO recebe a anotação @Repository, desta forma o Spring pode gerenciar seu comportamento, também permitidindo ExceptionTranslation (PersistenceAnnotationBeanPostProcessor deve ser configurado no applicationContext.xml).

classe XavecoDAO

package net.jorgealbuquerque.tutorial.xavecometro.dao;

//imports not displayed

@Repository
public class XavecoDAO extends AbstractJPADAO implements IXavecoDAO{

@SuppressWarnings("unchecked")
public List getByNome(String nome) {
Query query = entityManager.createQuery("Select m from Xaveco m where m.nome = ?1");
query.setParameter(1, nome);
return query.getResultList();
}

}


A camada de negócios apresente a ServiceBean (XavecoService) e sua interface (IXavecoService). Note a anotação @Transactional, desta forma as transações tambem são configuradas por anotações (É necessária a marcação em applicationContext.xml)

Interface IXavecoService

package net.jorgealbuquerque.tutorial.xavecometro.service;

//imports not displayed

public interface IXavecoService {

@Transactional
public void createNew(Xaveco xaveco);
public List getAll();
public List getByNome(String nome);
}


Na classe Classe XavecoService, a anotação @Service transforma a classe em uma spring bean, e a classe XavecoDAO é injetada por referência utilizando @AutoWired

Classe XavecoService

package net.jorgealbuquerque.tutorial.xavecometro.service;

//imports not displayed

@Service
public class XavecoService implements IXavecoService{

private XavecoDAO xavecoDAO;

@Autowired
public XavecoService(XavecoDAO xavecoDAO) {
this.xavecoDAO = xavecoDAO;
}

public void createNew(Xaveco xaveco) {
xavecoDAO.persist(xaveco);
}

public List getAll() {
return xavecoDAO.getAll();
}

public List getByNome(String nome) {
return xavecoDAO.getByNome(nome);
}
}


Finalmente, Beans JSF e Facelets são utilizados para criacao de novos xavecos no sistema.

createXaveco.xhtml




A classe CreateXaveco tem a responsabildiade de manipular a página acima, sendo gerenciado pelo Spring. A anotação @Component refencia a spring bean com o nome "createXaveco". Nas views JSF, este nome é resolvido com expressões do tipo:
#{createXaveco.Xaveco.nome}. A anotação @Scope é utilizada para definir o request, separando escopos customizados.

Classe CreateXaveco

package net.jorgealbuquerque.tutorial.xavecometro.view;

//imports not displayed

@Component("createXaveco")
@Scope("request")
public class CreateXaveco implements Serializable{

private XavecoService xavecoService;
private Xaveco xaveco = new Xaveco();

@Autowired
public CreateXaveco(XavecoService xavecoService) {
this.xavecoService = xavecoService;
}

public Xaveco getXaveco() {
return xaveco;
}
public void setXaveco(Xaveco xaveco) {
this.xaveco = xaveco;
}

public String save() {
xavecoService.createNew(xaveco);
FacesMessage facesMessage = new FacesMessage(
FacesMessage.SEVERITY_INFO, "O Xaveco foi salvo com sucesso!",
"OK");
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
xaveco = new Xaveco();
return null;
}
}


Arquivos XML:

Arquivo applicationContext.xml




Notas sobre o spring config;

  • habilita a busca anotações nos subpacotes de xavecometro.

  • habilita a gestao de transacoes orientada a annotations



Arquivo persistence.xml

net.jorgealbuquerque.tutorial.xavecometro.entidades



Arquivo faces-config.xml

com.sun.facelets.FaceletViewHandler
org.springframework.web.jsf.DelegatingVariableResolver


Arquivo web.xml

javax.faces.STATE_SAVING_METHOD
client

javax.faces.DEFAULT_SUFFIX
.xhtml

contextConfigLocation
classpath:applicationContext.xml

org.springframework.web.context.ContextLoaderListener
org.springframework.web.context.request.RequestContextListener

JPA Filter
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter

JPA Filter
*.jsf

Faces Servlet
javax.faces.webapp.FacesServlet
1

Faces Servlet
*.jsf


Adicionalmente, foi incluida uma classe de teste de integraçào para a XavecoDAO (TDD).

Classe XavecoDAOTest

package net.jorgealbuquerque.tutorial.xavecometro.dao;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import net.jorgealbuquerque.tutorial.xavecometro.entidades;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@TransactionConfiguration(transactionManager="txManager")
@ContextConfiguration(locations={"/applicationContext.xml"})
public class XavecoDAOTest{

private XavecoDAO xavecoDAO;

@Autowired
public void setRepository(XavecoDAO xavecoDAO) {
this.xavecoDAO = xavecoDAO;
}

@Test
public void shouldPersistNewXaveco() {
Xaveco xaveco = new Xaveco();
xaveco.setNome("Robertinha Fireball");
xaveco.setTelefone(new Integer(2342343));
xavecoDAO.persist(xaveco);
assertNotNull(xaveco.getId());
}

@Test
public void shouldFindBynome() {
Xaveco xaveco = new Xaveco();
xaveco.setNome("Renatinha Raio Laser");
xaveco.setTelefone(new Integer(7452354));
xavecoDAO.persist(xaveco);
assertNotNull(xaveco.getId());
List results = xavecoDAO.findBynome("Renatinha Raio Laser");
assertEquals(1, results.size());
assertEquals("Renatinha Raio Laser", results.get(0).getNome());
}

@Test
public void shouldReadAllXavecos() {
Xaveco xaveco1 = new Xaveco();
xaveco1.setNome("Pathy Tissunami");
xaveco1.setTelefone(new Integer(86452343));
Xaveco xaveco2 = new Xaveco();
xaveco2.setNome("Rafinha Quebra-Barraco");
xaveco2.setTelefone(new Integer(6734565));
xavecoDAO.persist(xaveco1);
assertNotNull(xaveco1.getId());

xavecoDAO.persist(xaveco2);
assertNotNull(xaveco2.getId());
List results = xavecoDAO.loadAll();
assertEquals(2, results.size());
}
}


Desta forma, pelo estudo proposto, concluí-se que a orientação a anotações para a pilha JSF/Spring/JPA permitinde, delegando a responsabilidade de ligação das camadas ao Spring.

Um comentário:

Wandrey disse...

Ótimo artigo, tema útil e interessante
Os arquivos de configuração não ficaram visiveis, pode ser um problema com firefox 3.
Poderia compartilhar o projeto sem as libs? Apenas um lib.txt, fácil de gerar com o eclipse.
Grato