Desenvolvendo um Aplicativo Desktop Completo – Parte 03

Bom dia, pessoal! Hoje vamos entrar no mundo do mapeamento OR, usando JPA e Hibernate.
Vou mostrar como ficará nossa entidade Livro, e também explicarei um pouco do padrão JPA, integrante da especificação EJB3, que veio para facilitar a vida de quem programa para a Web. Não se preocupem com a sopa de letrinhas, elas serão revistas mais adiante.

Quando pensamos em Banco de Dados, pensamos em Tabelas, Linhas e Colunas, assim como em Chave Primária e Estrangeira (heranças de um passado, talvez). Mas no mundo OO, isso não existe, esqueça isso totalmente. Devemos sempre, e apenas, pensar em Objetos, então vem a questão: Como transformar um Objeto em um registro no BD? Isso é feito através de soluções de persistência. Estas soluções seguem um padrão chamado JPA (Java Persistence API), que faz parte de um conjunto de especificações chamado EJB3 (Enterprise Java Beans 3). Não vou entrar em detalhes sobre o EJB3, somente o necessário para manter o entendimento, certo?

No padrão EJB3 existem 3 tipos de Beans, que são Objetos que desempenham um papel essencial no desenvolvimento. São eles: Beans de Entidades, Beans de Sessão e Beans Orientados à Mensagem. O EJB3 veio para facilitar o desenvolvimento de aplicativos para a Web. Como não estamos programando para a Web, só usaremos o primeiro tipo, os Beans de Entidades.

As Entidades estão na representação das Tabelas dos Bancos de Dados em Objetos. Basicamente, o JPA informa como faremos a persistência e a recuperação de uma Classe. O JPA veio para substituir o JDBC, que era a especificação de como os Aplicativos Java se comunicavam com o BD. Vamos entender melhor com um exemplo. Abaixo está o código da Classe Livro, uma Classe Java Comum (ou, em inglês, POJO: Plain Old Java Object):

public class Livro {
private int id;
private String titulo;
private String isbn;
private String edicao;
private String editora;
private double preco;

public Livro() { }

public Livro(String nome, String isbn) {
this.nome = nome;
this.isbn = isbn;
}
//Getters e Setters
}

Pronto! Note que omitimos as importações, bem como os Getters e Setters, no código acima, para não ocuparem muito espaço. Agora vamos torná-lo persistente?
Para isso, vamos usar um recurso que veio com o Java 1.5: As Anotações. Você irá reconhecê-las facilmente, são algo parecido com:  @Anotação

Vou mostrar o código da Classe Livro após as Anotações:

@Entity
@Table(name="livro")
public class Livro {

@Id @GeneratedValue
@Column(name="id_livro")
private int id;
@Column(name="titulo", unique=true, nullable=false)
private String titulo;
private String isbn;
private String edicao;
private String editora;
private double preco;

public Livro() { }

public Livro(String nome, String isbn) {
this.nome = nome;
this.isbn = isbn;
}
//Getters e Setters
}

É fácil ver as diferenças, pois as anotações vieram para facilitar o desenvolvimento de aplicativos Java: basta uma anotação para substituir um arquivo de configuração, ou uma entrada (linhas) em um arquivo de configuração. Para explicar melhor vou colocar as  anotações usadas no código acima:

@Entity:  torna nossa Classe uma Entidade, dessa forma ela pode ser persistida.

@Table: Indica qual a tabela primária a Classe será mapeada. Podemos configurar qual tabela, o esquema do BD, e outras coisas. Caso seja necessário, podemos mapear uma Classe para mais de uma tabela, usando as Anotações @SecundaryTable e @SecundaryTables.

@Id: informa que um Atributo é o identificador, que será mapeado como “chave primária”.

@GeneratedValue: informa que o atributo é gerado automaticamente pelo BD e (se necessário) como ele é gerado.

@Column – Informa para qual coluna será mapeada o Atributo, se não for informado, o provedor de persistência irá gerar uma coluna automaticamente baseado no nome do atributo.

Pronto! Já podemos persistir um livro. Mas como fazemos isso? Se lembramos do SQL (que é seria bom sabermos um pouco), para inserir um registro usamos a cláusula: ” INSERT INTO tabela(campos) VALUES(valores); “

Tá? E no JPA, como fazemos? Bom, primeiro vamos criar uma outra Classe, que será responsável por toda a comunicação com o BD, esta se chamará LivroDAO. Mas, antes vamos escrever uma Classe auxiliar, para abrir uma conexão com o BD. Portanto, crie um pacote chamado DAO e dentro crie uma classe chamada HibernateUtil. Nós usaremos o Hibernate como solução de persistência, isto é, ele é quem irá gerenciar o modo como nos comunicamos com o BD usando a especificação JPA.

Observe o código abaixo:

public class HibernateUtil {
 private static EntityManagerFactory emf;

 public static EntityManager getEntityManager() {
 if (emf == null) {
 emf = Persistence.createEntityManagerFactory("biblio");
 }
 return emf.createEntityManager();
 }
}

O que essa classe faz? Ela retorna um EntityManager, que é a Classe responsável por todas as operações no Banco de Dados. Nós temos um acrônimo para as operações que fazemos no BD, o conhecido CRUD – Create, Read, Update e Delete (Criar, Ler, Atualizar e Apagar). Todas essas operações são executadas pelo EntityManager. Notem que no método createEntityManagerFactory(“biblio”), o argumento é o nome da unidade de persistência, vista no post anterior. Quando usamos o @Entity, importamos o pacote javax.persistence.Entity – Isso torna nossa Classe numa entidade que pode ser persistida. E usaremos o EntityManager para isso. Agora iremos escrever outra Classe, também em DAO, que terá os métodos CRUD para nossa aplicação. Ela se chamará GenericDAO:

public class GenericDAO<T extends Serializable> {
 @PersistenceContext(unitName="biblio")
 private final EntityManager entityManager;
 private final Class<T> persistentClass;

 public GenericDAO() {
 this.entityManager = HibernateUtil.getEntityManager();
 this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
 }

 public EntityManager getEntityManager() {
 return entityManager;
 }

 protected void save(T entity) {
 EntityTransaction tx = getEntityManager().getTransaction();
 try {
 tx.begin();
 getEntityManager().persist(entity);
 tx.commit();
 }catch(Throwable t) {
 t.printStackTrace();
 tx.rollback();
 }finally {
 close();
 }
 }

 protected void update(T entity) {
 EntityTransaction tx = getEntityManager().getTransaction();
 try {
 tx.begin();
 getEntityManager().merge(entity);
 tx.commit();
 }catch(Throwable t) {
 t.printStackTrace();
 tx.rollback();
 }finally {
 close();
 }
 }

 protected void delete(T entity) {
 EntityTransaction tx = getEntityManager().getTransaction();
 try {
 tx.begin();
 getEntityManager().remove(entity);
 tx.commit();
 }catch(Throwable t) {
 t.printStackTrace();
 tx.rollback();
 }finally {
 close();
 }
 }

 public List<T> findAll() throws Exception {
 Session session = (Session) getEntityManager().getDelegate();
 return session.createCriteria(persistentClass).list();
 }

 public List<T> findByName(String nome) throws Exception {
 Session session = (Session) getEntityManager().getDelegate();
 return session.createCriteria(persistentClass).add(Restrictions.ilike("nome", nome, MatchMode.ANYWHERE)).list();
 }

 public T findById(int id) {
 Session session = (Session) getEntityManager().getDelegate();
 return (T) session.createCriteria(persistentClass).add(Restrictions.eq("id", id)).uniqueResult();
 }

 protected void close() {
 if (getEntityManager().isOpen()) {
 getEntityManager().close();
 }
 shutdown();
 }

 private void shutdown() {
 EntityManager em = HibernateUtil.getEntityManager();
 EntityTransaction tx = em.getTransaction();
 tx.begin();
 em.createNativeQuery("SHUTDOWN").executeUpdate();
 em.close();
 }

Nossa, quanto código! O que ele faz? Bom, primeiro pegamos o EntityManager. Depois usamos um pouco da API Reflections do Java, assim descobrimos com que entidade estamos trabalhando. Depois definimos os métodos genéricos (por isso o argumento é T), que podem ser usados por qualquer classe e que são geralmente usados quando persistimos no BD. O único método que devemos ter atenção é o shutdown(), precisamos dele, pois nosso banco de dados é o HSQLDB. Se estivéssemos usando o MySQL, por exemplo, não precisaríamos dele.

Agora vamos criar a Classe que persistirá o Livro – o LivroDAO:

public class LivroDAO extends GenericDAO<Livro> {
 public void salvar(Livro book) {
 save(book);
 }

 public void alterar(Livro book) {
 update(book);
 }

 public void excluir(int id) {
 Livro book = findById(id);
 delete(book);
 }

 public Double getTotal() {
 Session session = (Session) getEntityManager().getDelegate();
 return (Double) session.createQuery("select sum(l.preco) from Livro l").uniqueResult();
 }

Pronto! Se quisermos colocar um livro no BD, basta um LivroDAO.salvar(livro); Mas porquê criar uma classe genérica e depois especializá-la? Eu não poderia criar um LivroDAO onde tratasse diretamente a persistência de um livro? A resposta é SIM, mas, tenho que lembrar, que, se adicionarmos uma nova Classe e esta for persistida, deveremos criar um DAO para ela e tratar de sua persistência também. Isso gera duplicação de código, algo ruim para manter. Se adicionarmos agora uma Classe Autor, por exemplo, basta criar uma Classe AutorDAO, que extenda GenericDAO e passar um Autor como parâmetro nos métodos. Se for necessário métodos exclusivos para a Classe Autor, escreveríamos o mesmo no fim da Classe AutorDAO. Imagine que, no lugar da Classe GenericDAO, escrevêssemos manualmente os métodos de um CRUD, para CADA Classe a ser persistida, conseguem imaginar quanta duplicação do código. Com o GenericDAO qualquer alteração será propagada por todos os DAOs, esta é a vantagem.

Vale a pena aqui dar uma pequena explicação sobre as Anotações e mostrar um recurso útil do eclipse (as outras IDE’s também têm, mas uso eclipse) que é o Fix Imports. As Anotações funcionam como se fossem Interfaces Java, mas a JVM faz uma tradução diferente ao encontrar uma Anotação. Por isso, ao digitarmos, por exemplo, @Table teremos também que importar o pacote que contém esta Anotação. Como eu estou usando o JPA, a maior parte das Anotações referentes à Banco de Dados estará em javax.persistence. Se você simplesmente copiar e colar o código acima, verá que a IDE importará automaticamente. Caso isso não aconteça, ou caso você esteja digitando o código, há uma forma rápida de fazer os import’s: pressione [Ctrl]+[Shift]+[O] (não é Zero, é ‘o’). Isso funciona para realizar qualquer import, não só das Anotações.

Bom, pessoal … este post extendeu-se bastante, então vou ficar por aqui. Qualquer dúvida, problema ou sugestão deixem nos comentários que responderemos o mais breve possível. No próximo post, veremos como desenhar a Interface de Usuário (em inglês, UI), criaremos as Classes Controllers, que serão responsáveis por vincular as ações da UI ao DAO.

Ao término deste simples projeto, implementaremos algumas funcionalidades extras, para mostrar outros aspectos da JPA, do padrão de projeto MVC e da herança.

Abraços e até a próxima! Qualquer dúvida, deixem um comentário e responderemos o mais rápido que pudermos.

Esse post foi publicado em Java, Programação, Tecnologia e marcado , , , , , , . Guardar link permanente.

2 respostas para Desenvolvendo um Aplicativo Desktop Completo – Parte 03

  1. codificador01 disse:

    Cara o resto cara? Abandonou o projeto no meio?

    • Hexemeister disse:

      Primeiramente desculpe a demora na resposta. Este foi um projeto que o Hiero começou em 2012 e, por algum motivo, acabou perdendo o que tinha feito. Realmente parou no meio. Existe a vontade de reescrever todos os projetos, inclusive começamos um novo blog este ano, mas atualmente o que falta é tempo. Quaisquer dúvidas que tiver, pode mandar que a gente tenta resolver.

Deixar mensagem para Hexemeister Cancelar resposta

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.