Mostrando postagens com marcador DAO. Mostrar todas as postagens
Mostrando postagens com marcador DAO. Mostrar todas as postagens

domingo, 29 de setembro de 2013

A camada de visão - o managed bean do JSF

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Finalizando essa série de posts sobre Spring, JPA e JSF, vou mostrar finalmente como juntar todas as camadas e executar uma pequena aplicação para testar tudo o que foi feito até aqui.

Relembrando que a integração do JSF ocorre sem problemas desde que sejam feitas as alterações no obsoleto arquivo faces-config.xml, como já foi dito aqui: A camada de visão - Business Delegate.

Antes de executar o exemplo, é interessante deixar o arquivo persistence.xml configurado para apagar e criar as entidades, pois assim o teste pode ser refeito quantas vezes forem necessárias.

Vamos usar uma classe Pessoa,  como foi mostrado aqui: Criando entidades de bancos de dados e uma classe Autor conforme o código a seguir:

package entidades;

import java.io.Serializable;
import javax.persistence.Entity;

@Entity
public class Autor extends Pessoa implements Serializable {

    public Autor() {
    }
}

É apenas uma herança direta, já que a classe Pessoa é abstrata e não pode ser instanciada. Em seguida criamos um pacote chamado controle e dentro dele uma classe Controlador.java conforme o código abaixo:

package controle;

import delegate.FacadeBD;
import entidades.Entidade;
import entidades.Autor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

@ManagedBean(name = "controlador")
@ViewScoped
public class Controlador implements Serializable {

    @ManagedProperty(value = "#{facadeBD}")
    private FacadeBD facadeBD;

    public Controlador() {
    }

    public String listaGravada() {
        String retorno = "";
        List<Autor> la = facadeBD.listar(Autor.class);
        retorno += "\n\nLista gravada:\n\n";
        if (la != null) {
            for (Autor autor : la) {
                retorno += "("+autor.getId()+") "+autor.getNome() + "\n";
            }
        }
        return retorno;
    }

    public String getResultado() {
        String retorno = "";
        Autor autor1 = new Autor();
        autor1.setNome("Stephen King");
        Autor autor2 = new Autor();
        autor2.setNome("Dan Brown");
        List<Entidade> listaAutores = new ArrayList<Entidade>();
        listaAutores.add(autor1);
        listaAutores.add(autor2);
        retorno += "\nInserindo dois autores\n";
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        autor1.setFlagRemover(Boolean.TRUE);
        facadeBD.salvar(autor1);
        retorno += "\nExcluindo ("+autor1.getId()+") "+autor1.getNome()+"\n";
        retorno += listaGravada();
        Autor autor3 = new Autor();
        autor3.setNome("ray bradbury");
        listaAutores.add(autor3);
        retorno += "\nInserindo novo autor "+autor3.getNome()+"\n";
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        retorno += "\nAlterando "+autor3.getNome()+"\n";
        autor3.setNome("Ray Bradbury");
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        return retorno;
    }

    public FacadeBD getFacadeBD() {
        return facadeBD;
    }

    public void setFacadeBD(FacadeBD facadeBD) {
        this.facadeBD = facadeBD;
    }
}

Esta classe possui um atributo facadeBD, injetado pelo JSF, ou seja, é uma instância da classe FacadeBD. Podemos verificar na linha 13 que essa classe é colocada no contexto do JSF com o nome "controlador" (em minúsculas), e é como será referenciada logo a seguir, no página que será construída. Temos ainda um método listaGravada para recuperar o conteúdo do banco de dados correspondente à tabela pessoas. Podemos observar, na linha 25, o uso do método listar.

Finalmente, um método acessor getResultado na linha 35. Apesar de não termos um atributo resultado, verificamos aqui que o JSF só precisa do método acessor, e não do atributo, como já era esperado. Esse método faz várias chamadas aos métodos DAO por meio de facadeBD. É importante ressaltar que não há preocupação com conexões, transações, etc.. Vemos também que para excluir um registro do banco de dados, mudamos o atributo flagRemover para true e salvamos. No final do processamento, temos uma String com o histórico das operações, que vai ser acessada na página a seguir:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Livraria</title>
    </h:head>
    <h:body>
        <h:inputTextarea cols="100" rows="50" value="#{controlador.resultado}" />
    </h:body>
</html>

Na linha 9, temos a referência ao "atributo" resultado, acessado por meio do método getResultado do managed bean controlador. Ao executar essa aplicação, há um redirecionamento para a página de autenticação, e em seguida, ocorre o processamento nas camadas e o histórico de operações é exeibido no inputTextArea:

Inserindo dois autores


Lista gravada:

(1) Stephen King
(2) Dan Brown

Excluindo (1) Stephen King


Lista gravada:

(2) Dan Brown

Inserindo novo autor ray bradbury


Lista gravada:

(2) Dan Brown
(3) ray bradbury

Alterando ray bradbury


Lista gravada:

(2) Dan Brown
(3) Ray Bradbury

Outra observação importante é que também não há uma preocupação com as classes que vão ser gravadas. Pode ser uma lista com apenas um tipo de classe (autores) ou com mais de um tipo (autores mesclados com publicações, por exemplo). A única restrição é que as classes devem ser subclasses de Entidade.

Ainda, se observarmos as chamadas que foram feitas ao método salvarLista, veremos que em nenhum momento removemos da lista listaAutores o autor que foi excluído do banco. A classe GenericDAO já prevê tudo isso, justamente para que a camada de visão fique ainda mais desacoplada da camada de serviços, deixando para o desenvolvedor apenas a preocupação com o fluxo da aplicação e a disposição dos dados nas páginas.

E assim termina a série de posts sobre uso de JPA, Spring, JSF, MVC. Temos um projeto básico completo que será usado na próxima série de posts, ode serão abordados os relacionamentos entre entidades e como utilizar o Primefaces para construção de uma aplicação completa.

quarta-feira, 25 de setembro de 2013

Transações e a camada de controle

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Como foi mostrado nos dois posts anteriores, Temos um pacote entidades onde serão descritas as tabelas do banco de dados e um pacote dao, onde estão as classes que fazem o acesso aos dados do banco, por meio de JPA+Hibernate. Esses dois pacotes formam uma camada que frequentemente é chamada de camada de Modelo (o M do MVC).

Manter essa camada separada das demais facilita a mudança de uma tecnologia para outra. Por exemplo, suponhamos que o desenvolvedor precisar mudar de Hibernate para EclipseLink. Somente essa camada precisará ser alterada, além de algumas bibliotecas e configurações do Spring, sem afetar o restante do sistema.

Da mesma maneira, uma camada de Controle (o C do MVC) onde as transações são configuradas, deve ser implementada separadamente das outras camadas, para que se possa jogar para ela toda essa responsabilidade. A camada de controle muitas vezes é citada como camada de serviço, por isso as denominações "service" que acrescentaremos a seguir.

Vamos ao código de uma classe abstrata que vai direcionar como os métodos DAO da camada de modelo devem ser controlados:

package service;

import dao.InterfaceDAO;
import java.io.Serializable;
import java.util.List;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public abstract class AbstractService<T> {

    protected InterfaceDAO<T> dao;

    public void setDao(InterfaceDAO<T> dao) {
        this.dao = dao;
    }

    public void salvar(T entity) {
        dao.salvar(entity);
    }

    public void salvarLista(List<T> lista) {
        dao.salvarLista(lista);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public T carregar(Class classe, Serializable chave) {
        return dao.carregar(classe, chave);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> listar(Class classe) {
        return dao.listar(classe);
    }


    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> consultaPersonalizada(String consulta) {
        return dao.consultaPersonalizada(consulta);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> consultaNativa(String consulta) {
        return dao.consultaNativa(consulta);
    }
}

Foi criado um pacote chamado service e uma classe chamada AbstractService. Nela está centralizado todo o controle das transações que ocorrerão. É óbvio que isso só funciona se todas as operações relativas ao CRUD das entidades passar por essa camada.

Atenção para as linhas 9 e 26. Na linha 9 determinamos que todos os métodos descritos na classe exigirão uma transação, a menos que outra anotação interna determine outra configuração. Já na linha 26, estamos dizendo que o método carregar, em particular, suporta a utilização de uma transação já em andamento, mas não precisa de uma transação para ser utilizado. Não vou entrar em detalhes sobre esse assunto, mas a documentação do Spring explica muito bem essas anotações.

As outras linhas com anotações semelhantes servem para o mesmo propósito. Dessa maneira, nenhuma outra parte do sistema deve abordar controle de transações. Essa classe é a única responsável por essa tarefa, e deve interceptar todas as operações de CRUD.

Em seguida, no mesmo pacote service, criamos uma classe GenericService (agora concreta) para disponibilizar esse serviço, já que a classe abstrata não pode ser instanciada:

package service;

import dao.InterfaceDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service("genericService")
public class GenericService extends AbstractService {

    @Override
    @Value("#{genericDAO}")
    public void setDao(InterfaceDAO dao) {
        this.dao = dao;
    }

}

Esta classe é bastante simples, e tem poucas coisas relevantes para se comentar. Ela utiliza uma injeção de dependência na linha 11, trazendo ao atributo chamado de dao (que foi herdado de AbstractService - linha 12) o objeto genericDAO (identificador reconhecido pelo Spring - confira a linha 11 da classe no post anterior). Podemos notar também que há uma anotação na linha 7, nomeando essa classe no contexto (do mesmo modo que foi feito na classe GenericDAO) para que o Spring possa injetar uma instância dessa classe em outra camada, como veremos no próximo post.

Essa construção possibilita que a camada seguinte dependa apenas da classe GenericService. Qualquer mudança no contexto transacional será feita na classe abstrata, sem atingir o restante das classes da aplicação!

Uma observação a respeito da injeção de dependências: se o atributo dao e o identificador genericDAO tiverem uma correspondência única, ou seja, se houver apenas um objeto no contexto do Spring do tipo AbstractDAO, podemos utilizar uma abordagem com anotação @Autowired, e basta definir um atributo com os mesmos nome e tipo da classe desejada, anotá-lo com @Autowired e declarar seus get e set. Essas variações dependem do objetivo da classe - uma classe que vai manipular vários componentes do contexto do Spring que tenham o mesmo tipo mas nomes de classe diferentes, pode ser implementada para trabalhar strings, obter em tempo de execução o nome da classe e instanciá-la. Nesse caso, utiliza-se @value referenciando uma EL com o nome da classe, como foi feito aqui. Mas se os objetos forem únicos, pode-se utilizar apenas a abordagem com @Autowired. Já foi utilizada uma abordagem dessas quando foi implementada a classe GenericDAO, com o atributo jpaTemplate (Como criar uma camada DAO).

No próximo post, a camada de Visão.

terça-feira, 24 de setembro de 2013

Como criar uma camada DAO

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Criar uma camada DAO (Data Access Objects) que possa ser reutilizada em outros projetos não é uma tarefa muito difícil, embora envolva vários conceitos de OOP (Object Oriented Programming) e Design Patterns.

Este exemplo mostrará uma construção que vai disponibilizar ao desenvolvedor um ambiente de CRUD (Create, Retrieve, Update, Delete) com algumas características interessantes:

  1. decisão automática por inserção ou atualização, baseada no valor da chave primária;
  2. recuperação por consulta total, personalizada ou nativa do banco de dados;
  3. exclusão direta ou por meio do sinalizador da classe Entidade (flagRemover);
  4. processamento de uma única entidade ou de uma lista de entidades, pertencentes a qualquer subclasse da classe Entidade.
A última característica é a mais interessante, pois possibilita enviar várias entidades diferentes de uma só vez, garantindo o processamento dentro da mesma transação, o que garante um rollback total em caso de erro.

Dentro de um pacote chamado de dao, vamos criar uma interface chamada InterfaceDAO. É muito importante a programação voltada a interfaces (ou classes abstratas, quando for interessante), pois traz ao projeto uma versatilidade muito grande, permitindo que sejam agregados outros mecanismos, sem interferir naquilo que já foi desenvolvido.

Segue o código da classe InterfaceDAO:

package dao;

import java.io.Serializable;
import java.util.List;

public interface InterfaceDAO<T> {

    public void salvar(T entidade);

    public void salvarLista(List<T> lista);

    public T carregar(Class<T> classe, Serializable chavePrimaria);

    public List<T> listar(Class<T> classe);

    public List<T> consultaPersonalizada(String consulta);

    public List<T> consultaNativa(String consulta);

}

Essa interface orienta o desenvolvedor a respeito dos métodos que serão necessários ao projeto e que podem ser utilizados em outras camadas, já que são métodos públicos. Não há método de atualização e exclusão na interface, pois a atualização depende apenas do valor da chave primária, e a exclusão, como foi visto anteriormente, será feita por meio da sinalização do atributo flagRemover. É importante observar que a interface não fixa o tipo das classes que serão processadas. Em vez disso, usa o tipo genérico, representado por T.

A seguir, a implementação da classe  que o projeto utilizará para as operações CRUD:

package dao;

import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.jpa.support.SharedEntityManagerBean;
import org.springframework.stereotype.Repository;

@Repository("genericDAO")
@Scope(value = "singleton")
public class GenericDAO<T extends Entidade> implements InterfaceDAO<T> {

    @Autowired
    private SharedEntityManagerBean jpaTemplate;

    @Override
    public void salvar(T entidade) {
        if (entidade != null) {
            salvarSemFlush(entidade);
            jpaTemplate.getObject().flush();
        }
    }

    private void salvarSemFlush(T entidade) {
        if (entidade != null) {
            try {
                if (entidade.getFlagRemover()) {
                    if (entidade.getId() != null) {
                        if (jpaTemplate.getObject().find(entidade.getClass(), entidade.getId()) != null) {
                            jpaTemplate.getObject().remove(jpaTemplate.getObject().getReference(entidade.getClass(), entidade.getId()));
                        }
                    }
                } else {
                    if (entidade.getId() == null) {
                        jpaTemplate.getObject().persist(entidade);
                    } else {
                        if (jpaTemplate.getObject().find(entidade.getClass(), entidade.getId()) != null) {
                            jpaTemplate.getObject().merge(entidade);
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());;
            }
        }
    }

    @Override
    public void salvarLista(List<T> lista) {
        Integer i = 0;
        if (lista != null) {
            for (T entidade : lista) {
                salvarSemFlush(entidade);
                i++;
                // sincroniza a cada 500 registros
                if ((i % 500) == 0) {
                    try {
                        jpaTemplate.getObject().flush();
                    } catch (Exception e) {
                        System.out.println(e.getLocalizedMessage());;
                    }
                }
            }
            // sincroniza os ultimos registros
            if (i % 500 != 0) {
                try {
                    jpaTemplate.getObject().flush();
                } catch (Exception e) {
                    System.out.println(e.getLocalizedMessage());;
                }
            }
        }
    }

    @Override
    public T carregar(Class<T> classe, Serializable chave) {
        if (classe == null || chave == null) {
            return null;
        }
        return jpaTemplate.getObject().find(classe, chave);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<T> listar(Class<T> classe) {
        if (classe == null) {
            return null;
        }
        return jpaTemplate.getObject().createQuery("select a from " + classe.getSimpleName() + " a ").getResultList();
    }

    @Override
    public List<T> consultaPersonalizada(String consulta) {
        if (consulta == null) {
            return null;
        }
        return jpaTemplate.getObject().createQuery(consulta).getResultList();
    }

    @Override
    public List<T> consultaNativa(String consulta) {
        if (consulta == null) {
            return null;
        }
        return jpaTemplate.getObject().createNativeQuery(consulta).getResultList();
    }

    public SharedEntityManagerBean getJpaTemplate() {
        return jpaTemplate;
    }
}

Vamos à análise das linhas mais relevantes:

  • linhas 11 e 12: configurações do Spring. Essa classe será injetada pelo Spring posteriormente, na camada de controle.
  • linha 13:Essa classe restringe o uso às subclasses de Entidade e utiliza os métodos definidos em InterfaceDAO.
  • linhas 15 e 16: modelo JPA injetado pelo Spring, e que foi definido em AppConfig (Configurando o Spring (II)).
  • linhas 19 e 26: aqui a camada DAO possibilita ao desenvolvedor chamar o método salvar (para apenas uma entidade, com flush no final) ou chamar o método salvarLista (linha 51), que vai gerenciar o flush conforme o processamento vai atingindo blocos de 500 registros.
  • demais linhas: implementação dos demais métodos.

No próximo post, a implementação de um serviço que faz o gerenciamento de transações para chamar os métodos DAO.

segunda-feira, 23 de setembro de 2013

Criando entidades de bancos de dados

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Uma boa alternativa para quem está desenvolvendo um projeto e precisa agilizar os testes durante a modelagem dos dados e a criação da camada DAO é deixar a unidade de persistência e o Hibernate se encarregarem da criação das tabelas das entidades no banco de dados.

Para que isso ocorra, é necessária uma propriedade no arquivo persistence.xml, mais precisamente na linha 13, mostrada abaixo:


  
    org.hibernate.ejb.HibernatePersistence
    java:/fonteDeDados
    entidades.Pessoa
    false
    
      
      
      
      
      
      
    
  


Essa propriedade indica ao Hibernate para apagar todas as tabelas e recriá-las, a cada vez que a aplicação for executada. Depois que todos os testes terminarem, a propriedade pode ser deixada com o valor update, que atualiza ou cria automaticamente qualquer atributo novo ou tabela que for acrescentada ao projeto.


  
    org.hibernate.ejb.HibernatePersistence
    java:/fonteDeDados
    entidades.Pessoa
    false
    
      
      
      
      
      
      
    
  


Caso não haja mais necessidade de alterar a estrutura do banco de dados, a propriedade pode ser simplesmente retirada do arquivo.

Uma pequena observação: se a propriedade estiver configurada para create-drop e ocorrer erro no servidor de aplicações, é preciso remover manualmente as tabelas do banco de dados.

Para demonstrar estas configurações, vou criar uma série de posts com a construção das camadas MVC que serão usadas em um projeto simples para uma livraria.

O primeiro passo é preparar uma classe abstrata que servirá de modelo para todas as entidades que forem criadas no projeto. O objetivo é que todas as entidades atendam os requisitos abaixo:

  1. um método chamado getId() que retorne o valor de sua chave primária;
  2. um atributo temporário chamado flagRemover, que sinalizará ao DAO que o objeto que chegou até ele deve ser removido do banco de dados;

A importância desta classe será demonstrada quando for implementado o CRUD do projeto. Vamos ao código:

package entidades;

import java.io.Serializable;
import javax.persistence.Transient;

public abstract class Entidade implements Serializable {

    @Transient
    public boolean flagRemover;

    public abstract Serializable getId();

    public Boolean getFlagRemover() {
        return flagRemover;
    }

    public void setFlagRemover(Boolean flagRemover) {
        this.flagRemover = flagRemover;
    }
}

Numa rápida análise, o pacote entidades conterá essa classe abstrata que tem  um atributo flagRemover, com seus métodos de acesso (get e set) e um método público abstrato que simula o método get de um atributo chamado id. Esse método possibilita generalizar a obtenção da chave primária de qualquer classe de entidade que herde essa classe, já que ele será implementado na subclasse, e poderá retornar justamente o atributo da chave primária da subclasse. Observamos também que o atributo flagRemover não será escrito no banco de dados, pois está anotado com @Transient. Ele será utilizado apenas para marcar uma entidade que deve ser removida, antes de enviá-la ao DAO.

É bastante clara a vantagem de usar uma classe abstrata em vez de uma interface, pois podemos deixar alguns métodos prontos já para a subclasse, o que não poderia ser feito com o uso de uma interface.

Um rápido exemplo:

package entidades;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public abstract class Pessoa extends Entidade implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer reg;
    @Column(length=50)
    private String nome;
    @Column(length=50)
    private String sobrenome;

    public Integer getReg() {
        return reg;
    }

    public void setReg(Integer reg) {
        this.reg = reg;
    }

    public String getNome() {
        return nome;
    }

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

    public String getSobrenome() {
        return sobrenome;
    }

    public void setSobrenome(String sobrenome) {
        this.sobrenome = sobrenome;
    }

    @Override
    public Serializable getId() {
        return reg;
    }

}

A classe Pessoa (que também é uma classe abstrata!) herda a estrutura da classe Entidade (linha 11), ou seja, automaticamente possui um atributo flagRemover com get e set. Essa classe possui sua própria chave primária reg, anotada devidamente com @Id, e que não tem, inicialmente, uma ligação com o método getId.  No final do código, podemos ver a implementação (que é obrigatória) do método getId, que está retornando o atributo reg (chave primária da classe Pessoa). Portanto, outras classes poderão acessar o valor da chave primária da classe Pessoa, utilizando o método getId(), sem saber que o atributo original é chamado de reg, ou seja, deixando o atributo reg encapsulado.

No próximo post, a construção das classes DAO.

quarta-feira, 18 de setembro de 2013

Configurando o Spring (II)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Continuando o post anterior, vou mostrar agora o arquivo web-application-config.xml. Tive alguns problemas com a localização do arquivo, o que foi resolvido mantendo o seguinte padrão: os arquivos de configuração do Spring referenciados no arquivo web.xml devem ser sempre colocados em uma subpasta /WEB-INF/classes. Observe na imagem abaixo:


O conteúdo do arquivo é bem simples:

<?xml version="1.0" encoding="UTF-8"?><br/><beans xmlns="http://www.springframework.org/schema/beans"<br/>       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br/>       xmlns:context="http://www.springframework.org/schema/context"<br/>       xsi:schemaLocation="<br/>           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd<br/>           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"<br/>       default-autowire="byName"><br/>    <context:component-scan base-package="configuracao" /><br/>    <import resource="security-config.xml" /><br/></beans>

Uma atenção especial para as linhas 9 e 10:

Na linha 9, é declarado um pacote onde o Spring vai procurar por arquivos de configuração, que mostrarei logo a seguir.

Na linha 10, é declarado um arquivo de configuração para autenticação, que será mostrado em outro post. Se não houver necessidade de autenticação, ou se for preciso testar o projeto inicialmente sem preocupação com autorização de acesso, esta linha pode ser temporariamente comentada.

Seguindo a declaração da linha 9, o projeto deve ter um pacote chamado "configuracao":


Neste pacote foi criada uma classe AppConfig.java, que substitui a implementação xml da configuração do Spring. Qual a vantagem disso? É que um arquivo de classe pode ser facilmente reaproveitado em um projeto novo, simplesmente criando um arquivo jar.

Vamos analisar rapidamente a classe AppConfig:

package configuracao;

import java.io.Serializable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.support.SharedEntityManagerBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = { "controle", "dao", "delegate", "entidades", "service"})
public class AppConfig implements Serializable {

    @Bean @Scope(value="singleton")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setPersistenceUnitName("PU");
        return bean;
    }

    @Bean @Scope(value="singleton")
    public SharedEntityManagerBean jpaTemplate() {
        SharedEntityManagerBean bean = new SharedEntityManagerBean();
        bean.setEntityManagerFactory(entityManagerFactory().getObject());
        return bean;
    }

    @Bean @Scope(value="singleton")
    public JtaTransactionManager transactionManager() {
        JtaTransactionManager bean = new JtaTransactionManager();
        return bean;
    }

    @Bean @Scope(value="singleton")
    public JndiObjectFactoryBean fonteDeDados() {
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
        bean.setJndiName("java:comp/env/jdbc/fonteDeDados");
        return bean;
    }
}

Novamente, a classe deve ser estudada e o leitor deve buscar na documentação do Spring as explicações para todas as anotações mostradas aqui. Mas vamos destacar as linhas mais importantes da classe:

  • Na linha 14, uma anotação que vai sinalizar ao Spring para que use o conteúdo deste arquivo como configuração. Logo em seguida, na linha 16, uma lista com os pacotes onde o Spring deve procurar por classes com anotações. Isto será muito importante quando chegar o momento de construir a camada de acesso aos dados (DAO). É óbvio que os pacotes devem ser criados com os mesmos nomes declarados na linha 16.
  • Na linha 22, podemos identificar o nome da unidade de persistência que vai ser utilizada ("PU"). Vamos criar esta unidade de persistência durante a construção da camada de acesso aos dados.
  • E nas linhas 40 e 42, podemos ver a mesma referência que foi utilizada na configuração do Spring contida no arquivo web.xml.

Importante: esta configuração é para uso do Spring com Jboss. Há diferenças nas configurações se o servidor de aplicações for TomCat, ou Glassfish. Em um próximo artigo, vou mostrar como configurar o Jboss para este projeto.

No próximo post, a configuração do Spring Security.

segunda-feira, 19 de abril de 2010

Criar um bean gerenciado que retorne uma lista com os dados

Nosso próximo passo é um Bean gerenciado que controle as requisições. É altamente recomendado que o projeto siga o padrão MVC - todas as requisições das páginas devem ser enviadas para este bean. Começamos com

Novo->Outro->JavaServer Faces->Bean gerenciado JSF

As configurações podem seguir o modelo a seguir, lembrando de colocar o arquivo no pacote controle, para manter o padrão:


As observações mais relevantes são a respeito do nome da classe e do nome que será usado nas páginas (ControladorBean e controlador, respectivamente). Essa diferenciação é apenas para facilitar a identificação.
Este Bean deve ter um método que retorne uma lista para montarmos o DataTable. Há várias maneiras de se fazer isto - vamos seguir este roteiro:

  1. instanciar a classe DAO;
  2. utilizar o método crud da classe DAO para recuperar os dados da tabela Person;
  3. criar uma instância de ListDataModel a partir dos dados recuperados.
O código final do Bean gerenciado ficará assim:
  
01 package controle; 02 03 import java.util.List; 04 import javax.faces.model.ListDataModel; 05 06 public class ControladorBean { 07 08 private ListDataModel lista; 09 private DAO dao; 10 11 public ControladorBean() { 12 dao = new DAO(); 13 } 14 15 public ListDataModel getLista() { 16 List relacao = dao.crud("from Person order by name"); 17 lista = new ListDataModel(relacao); 18 return lista; 19 } 20 21 }
O método getLista será referenciado na página onde será implementado o componente DataTable.

Voltar para DataTables básico

quinta-feira, 1 de abril de 2010

Criar uma classe controladora (DAO)

(Confira minha nova série de posts: Spring + JPA + JTA)
 
A classe controladora DAO (Data Access Object é um padrão de projeto utilizado em engenharia de softwares orientados a objeto) vai se encarregar da ligação entre o Hibernate e as classes controladoras de cada CRUD da aplicação web. Ela pode ser construída particularmente para uma classe, mas utilizando polimorfismo podemos criar métodos que abstraem a classe para que seja totalmente reaproveitável.

No pacote controle, criaremos uma classe chamada DAO.java, com o código a seguir:

01 package controle;
02
03 import java.util.List;
04 import java.util.logging.Level;
05 import java.util.logging.Logger;
06 import org.hibernate.Session;
07
08 public class DAO {
09
10     private Session session;
11
12     public DAO() {
13     }
14
15     public boolean crud(Object o, String operacao) {
16         session = HibernateUtil.getSessionFactory().openSession();
17         session.beginTransaction();
18         switch (Integer.parseInt(operacao)) {
19             case 1: {
20                 session.save(o);
21                 break;
22             }
23             case 2: {
24                 session.delete(o);
25                 break;
26             }
27             case 3: {
28                 session.update(o);
29                 break;
30             }
31         }
32         session.getTransaction().commit();
33         session.close();
34         return true;
35     }
36
37     public Object crud(Object o, int id) {
38         Object objeto = null;
39         session = HibernateUtil.getSessionFactory().openSession();
40         Class classe = o.getClass();
41         try {
42             objeto = classe.newInstance();
43         } catch (InstantiationException ex) {
44             Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex);
45         } catch (IllegalAccessException ex) {
46             Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex);
47         }
48         objeto = session.get(o.getClass(), new Integer(id));
49         session.close();
50         return objeto;
51     }
52
53     public List crud(String sql) {
54         session = HibernateUtil.getSessionFactory().openSession();
55         session.beginTransaction();
56         List lista = session.createQuery(sql).list();
57         session.getTransaction().commit();
58         session.close();
59         return lista;
60     }
61 }
O código é bem simples, e não há muitas considerações a fazer. O método crud pode ser utilizado de três maneiras: uma para inserção, exclusão e atualização (linhas 15-35), outra para recuperar um objeto (linhas 37-51) e uma terceira que retorna uma lista de objetos (linhas 53-60).
Esta classe será instanciada no bean gerenciável que se encarregará da integração entre a interface visual e os dados.

Voltar para DataTables básico