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

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.

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.

domingo, 22 de setembro de 2013

Configurando fontes de dados com o JBoss (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.

Vamos agora abordar a criação da unidade de persistência. Já vimos que a unidade de persistência será chamada de "PU", como está declarado no arquivo AppConfig (veja a linha 22 do arquivo, neste post).

Vamos usar Hibernate, JPA 2, JTA, além do Spring, é claro. Vai ser um pouco trabalhoso, mas que renderá muitos benefícios e poderá ser totalmente reaproveitado em outros projetos.

Criamos o arquivo persistence.xml no NetBeans e configuramos seu código (é melhor que configurar pelo wizard...) da seguinte maneira:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="PU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/fonteDeDados</jta-data-source>
    <class>entity.classe1</class>
    <class>entity.classe2</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <validation-mode>NONE</validation-mode>
    <properties>
      <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
      <property name="hibernate.show_sql" value="false"/>
      <property name="hibernate.format_sql" value="false"/>
      <property name="javax.persistence.validation.mode" value="none"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>

  • Na linha 3, temos o nome da unidade de persistência, como foi comentado no primeiro parágrafo. Na linha 5, a ligação da unidade de persistência com o famigerado identificador que já apareceu em vários arquivos de configuração. Aqui percebemos que em um novo projeto, o que muda é apenas o arquivo persistence.xml - todo o restante da configuração aponta para "fonteDeDados", inclusive o próprio persistence.xml. Até o JBoss referencia fonteDeDados, e essas dependências ajudam a realizar alterações de ambiente (mudar servidor, mudar plataforma do banco de dados, etc.) com pouquíssimas alterações no projeto.
  • Nas linhas 6, 7 e 8 temos a configuração das classes (que serão criadas em outro post).
  • Na linha 15 temos um parâmetro que indica ao Hibernate para criar as tabelas de acordo com as anotações das classes declaradas nas linhas 6 e 7. Percebemos que só é necessário criar um banco de dados, conseguir uma conexão, configurar o Spring e a unidade de persistência. Quando executarmos o projeto e ele necessitar de acesso ao banco de dados, será feita a verificação das classes e as tabelas serão criadas automaticamente. Esse assunto veremos com mais detalhes em outro post.
Aqui termina a configuração do projeto básico usando Spring + JPA. Todas as configurações necessárias para trabalhar com as camadas de dados já estão contempladas nestes arquivos de configuração que foram mostrados até aqui. Agora precisamos criar os métodos genéricos para realizar as operações CRUD (Create, Read, Update e Delete), e é nesse instante que o conhecimento de OOP (Object Oriented Programming) e Spring faz a diferença.

Conforme avançarmos na construção das camadas (elaboradas com base no design pattern MVC), surgirão os comentários a respeito das configurações utilizadas até aqui. Modificações deverão ser feitas sempre que quisermos alterar comportamento de logs, recriar tabelas, e etc..

No próximo post, como gerar as tabelas pelo próprio projeto do NetBeans.

quinta-feira, 1 de abril de 2010

Criar um mapeamento no Hibernate


Existe uma maneira muito fácil para criar um mapeamento no Hibernate. Como as anotações já estão no modelo (ver em Criar uma classe de entidade a partir do banco de dados), basta que o arquivo hibernate.cfg.xml tenha uma referência à classe desejada - no nosso caso, a classe Person. Isto pode ser feito manualmente, inserindo a linha

<mapping class="modelo.Person" />
antes da declaração
</session-factory>
Também pode ser feito na aba Projeto do arquivo hibernate.cfg.xml, como mostra a figura. Basta abrir o nó Mapeamentos, clicar em Adicionar e preencher apenas o nome da classe.

É importante destacar que a simplicidade dessa etapa se dá porque as anotações a respeito dos atributos e de qual tabela do banco está sendo mapeada já foram feitas quando a classe Person foi criada.

Voltar para DataTables básico

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

quarta-feira, 31 de março de 2010

Criar uma classe de entidade a partir do banco de dados

Esta etapa também é bastante mecânica. O NetBeans se encarrega de criar uma classe a partir de uma tabela do banco de dados e colocar as anotações básicas. Em seguida, o desenvolvedor pode modificar o que achar necessário.
Vamos criar uma classe Person, baseada na tabela Person do banco de dados Travel, do Derby: basta utilizar Novo->Outro->Persistência->Classe de entidade a partir do banco de dados.
Em seguida, é só escolher a conexão e a tabela desejada. Na tela seguinte, o nome do pacote deve ser colocado (no nosso caso, modelo) e na última tela pode-se preencher o tipo de recuperação, o tipo de dados que será gerado (pode ser java.util.list) e pronto.
Se houverem tabelas associadas com chaves estrangeiras, o NetBeans se encarrega de criar as outras classes associadas, e colocar as anotações necessárias. Abaixo, a listagem final da classe Person.java - quanto tempo economizado!!


package modelo;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "PERSON", catalog = "", schema = "TRAVEL")
@NamedQueries({
@NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"),
@NamedQuery(name = "Person.findByPersonid", query = "SELECT p FROM Person p WHERE p.personid = :personid"),
@NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name"),
@NamedQuery(name = "Person.findByJobtitle", query = "SELECT p FROM Person p WHERE p.jobtitle = :jobtitle"),
@NamedQuery(name = "Person.findByFrequentflyer", query = "SELECT p FROM Person p WHERE p.frequentflyer = :frequentflyer"),
@NamedQuery(name = "Person.findByLastupdated", query = "SELECT p FROM Person p WHERE p.lastupdated = :lastupdated")})
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "PERSONID")
private Integer personid;
@Column(name = "NAME")
private String name;
@Column(name = "JOBTITLE")
private String jobtitle;
@Column(name = "FREQUENTFLYER")
private Short frequentflyer;
@Column(name = "LASTUPDATED")
@Temporal(TemporalType.TIMESTAMP)
private Date lastupdated;

public Person() {
}

public Person(Integer personid) {
this.personid = personid;
}

public Integer getPersonid() {
return personid;
}

public void setPersonid(Integer personid) {
this.personid = personid;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getJobtitle() {
return jobtitle;
}

public void setJobtitle(String jobtitle) {
this.jobtitle = jobtitle;
}

public Short getFrequentflyer() {
return frequentflyer;
}

public void setFrequentflyer(Short frequentflyer) {
this.frequentflyer = frequentflyer;
}

public Date getLastupdated() {
return lastupdated;
}

public void setLastupdated(Date lastupdated) {
this.lastupdated = lastupdated;
}

@Override
public int hashCode() {
int hash = 0;
hash += (personid != null ? personid.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
if (!(object instanceof Person)) {
return false;
}
Person other = (Person) object;
if ((this.personid == null && other.personid != null) || (this.personid != null && !this.personid.equals(other.personid))) {
return false;
}
return true;
}

@Override
public String toString() {
return "modelo.Person[personid=" + personid + "]";
}

}

Voltar para DataTables básico

terça-feira, 30 de março de 2010

DataTables básico

A criação de tabelas (básicas) com IceFaces envolve 7 etapas, já incluindo a persistência de dados com Hibernate. Podemos criar uma tabela utilizando quase que somente recursos da IDE do NetBeans, com poucas e pequenas alterações. Vamos utilizar o banco de dados de exemplo Travel, do Derby, para mostrar os passos necessários.

  1. Criar uma conexão com uma base de dados;
  2. Criar os arquivos HibernateUtil.java e hibernate.cfg.xml;
  3. Criar uma classe de entidade a partir do banco de dados;
  4. Criar uma classe controladora (DAO);
  5. Criar um mapeamento no Hibernate;
  6. Criar um bean gerenciado que retorne uma lista com os dados,
  7. Criar a DataTable e colocar um paginador.

A primeira impressão é que são muitas tarefas, mas a IDE vai providenciar a maior parte do trabalho, que na verdade é bem mecânico e não deve desperdiçar o precioso tempo do desenvolvedor.

domingo, 28 de março de 2010

Web + Java. É viável?

Aplicações web construídas com auxílio de java são viáveis? Sim, desde que as aplicações ofereçam ao internauta recursos que há algum tempo não eram muito exigidos.

Exemplos: calendários, validação, abas deslizantes, objetos reposicionáveis, etc. E todos em tempo real (AJAX?), sem recarregar as páginas.

Para consolidar este tema, vamos investigar o uso do framework Icefaces para criar uma aplicação clássica de controle de biblioteca, utilizando inicialmente as tecnologias:

  • IDE Netbeans
  • Banco de dados Firebird
  • Persistência de dados com Hibernate
  • IceFaces
  • Javaserver Faces

As abordagens irão desde a criação do banco de dados, até a utilização da aplicação com usuários e documentos reais.