terça-feira, 4 de novembro de 2008

DAO Genérico com JPA e Hibernate


Olá pessoal,

Criei uma estrutura inicial de um DAO Genérico que gostaria de compartilhar com vcs. Ainda não existe todos os métodos que vou precisar, mas vou atualizando aqui a cada mudança no código. Para esta implementação utilizei JPA + Hibernate e para meu caso vem me atendendo bem por enquanto. Segue abaixo a implementação:


-----------------------------------------------


/**
* Esta classe é responsável por conter as operações genéricas para realização
* do acesso aos dados do sistema.
*
* @author Samuel
*
* @param
*/
@SuppressWarnings("unchecked")
public abstract class GenericDAOImpl {

private EntityManager entityManager;

private Class classePersistente;

/**
* Contrutor que guarda o tipo atual da classe T.
*/
public GenericDAOImpl() {
this.classePersistente = (Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}

/**
* Atualiza o objeto que se encontra em memória.
*
* @param object
* objeto a ser atualizado
*/
public final void refresh(T object) {
getEntityManager().refresh(object);
}

/**
* Executa o flush no entity manager.
*
*/
public final void flush() {
getEntityManager().flush();
}

/**
* Executa o flush no entity manager.
*
*/
public final void clear() {
flush();
getEntityManager().clear();
}


/**
* Executa o merge do objeto que se encontra em memória.
*
* @param objeto
* a ser realizado o merge
* @return objeto que foi executado o merge
*/
public final T merge(T objeto) {
objeto = getEntityManager().merge(objeto);
return objeto;
}

/**
* Salva o objeto atual na base de dados.
*
* @param objeto
* a ser salvo
*/
public final void salvar(T objeto) {
getEntityManager().persist(objeto);
}

/**
* Remove o objeto da base de dados.
*
* @param objeto
* a ser removido
*/
public final void remover(T objeto) {
getEntityManager().remove(objeto);
}

/**
* Remove o objeto uma vez passado sua chave como parâmetro.
*
* @param chave
* identificadora do objeto
*/
public final void removerPorChave(Integer chave) {
getEntityManager().createQuery(
"delete from " + getClassePersistente().getName()
+ " where id = " + chave).executeUpdate();
}

/**
* Busca o objeto uma vez passado sua chave como parâmetro.
*
* @param chave
* identificador
* @return Objeto do tipo T
*/
public final T buscarPorChave(Integer chave) {
T instance = null;
try {
instance = getEntityManager().find(getClassePersistente(), chave);
} catch (RuntimeException re) {
re.printStackTrace();
}
return instance;
}

/**
* Busca o objeto de acordo com o objeto preenchido com os valores passado
* como exemplo.
*
* @param objeto
* utilizado para realizar a busca
* @param ordenacoes
* lista de critérios de ordenação
* @return Lista de objetos retornada
*/
public final List buscarPorExemplo(T objeto, Order... ordenacoes) {
Session session = (Session) getEntityManager().getDelegate();
Example example = criaExemplo(objeto);
Criteria criteria = session.createCriteria(objeto.getClass()).add(
example);
for (int i = 0; i < ordenacoes.length; i++) {
criteria.addOrder(ordenacoes[i]);
}
return (List) criteria.list();
}

/**
* Busca o objeto de acordo com o objeto preenchido com os valores passado
* como exemplo.
*
* @param objeto
* @param indiceInicial
* @param indiceFinal
* @param ordenacoes
* lista de critérios de ordenação.
* @return Lista de orden
*/
public final List buscarPorExemplo(T objeto, Integer indiceInicial,
Integer indiceFinal, Order... ordenacoes) {
Example example = criaExemplo(objeto);
Criteria criteria = criaCriteria().add(example);
criteria.setFirstResult(indiceInicial);
criteria.setMaxResults(indiceFinal);

for (int i = 0; i < ordenacoes.length; i++) {
criteria.addOrder(ordenacoes[i]);
}

return (List) criteria.list();
}

/**
* Retorna a quantidade total de objetos para aquela entidade específica.
*
* @return quantidade total de objetos
*/
public final int buscaQuantidadeTotal() {
Criteria criteria = criaCriteria();
criteria.setProjection(Projections.rowCount());
return (Integer) criteria.uniqueResult();
}

/**
* Busca todos os objetos para aquela entidade específica.
*
* @param ordenacoes
* lista de ordenações para pesquisa
* @return lista de todos os objetos da entidade
*/
public List buscarTodos(Order... ordenacoes) {
List results = null;
try {
Query query = getEntityManager().createQuery(
"from " + getClassePersistente().getName()
+ adicionaOrderByHql(ordenacoes));
results = query.getResultList();
} catch (RuntimeException re) {
re.printStackTrace();
}
return results;
}

/**
*
* Busca todos os objetos de uma entidade específica de um índice inicial
* até um índice final.
*
* @param indiceInicial
* indice inicial da busca
* @param indiceFinal
* indice final da pesquisa.
* @param ordenacoes
* lista de ordenação a ser criado
* @return uma lista de objetos do tipo T
*/
public List buscarTodos(Integer indiceInicial,
Integer indiceFinal, Order... ordenacoes) {
List results = null;
try {
Query query = getEntityManager().createQuery(
"from " + getClassePersistente().getName()
+ adicionaOrderByHql(ordenacoes));
query.setFirstResult(indiceInicial);
query.setMaxResults(indiceFinal);

results = (List) query.getResultList();
} catch (RuntimeException re) {
re.printStackTrace();
}
return results;
}

/**
* Utilizado para se injetar o Entity manager no DAO.
*
* @param entityManager
* entity manager
*/
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}

/**
* Utilizado para se utilizar o entity manager nos DAOS que herdam do DAO
* genérico.
*
* @return Entity manager.
*/
protected EntityManager getEntityManager() {
return entityManager;
}

/**
* Adiciona o orderBy no final da query a ser utilizada.
*
* @param ordenacoes
* a serem utilizadas para a busca
* @return string com o orderBy
*/
protected final static String adicionaOrderByHql(Order... ordenacoes) {
String result = "";
if (ordenacoes.length > 0) {
StringBuilder builder = new StringBuilder(" order by ");
for (int i = 0; i < ordenacoes.length - 1; i++) {
builder.append(ordenacoes[i].toString());
builder.append(", ");
}
builder.append(ordenacoes[ordenacoes.length - 1]);
result = builder.toString();
}

return result;
}

/**
* Busca a classe persistente do objeto utilizado na classe.
*
* @return classe persistente
*/
protected final Class getClassePersistente() {
return classePersistente;
}

/**
* Retorna o objeto da clases Criteria.
*
* @return um objeto do tipo Criteria do Hibernate
*/
protected final Criteria criaCriteria() {
Session session = (Session) getEntityManager().getDelegate();
return session.createCriteria(getClassePersistente());
}

/**
* Método utilizado para criar o objeto Example. Este objeto é utilizado
* para realizar a busca por exemplo.
*
* @param objeto
* sobre o qual o Example será criado
* @return em objeto do tipo Example
*/
protected final Example criaExemplo(T objeto) {

Example example = Example.create(objeto);
example.enableLike(MatchMode.ANYWHERE);
example.excludeZeroes();
example.ignoreCase();

return example;
}

}


-----------------------------------------------

Para ilustrar como esta classe é utilizada mostrarei abaixo como extender este DAO genérico:

-----------------------------------------------



public class PessoaDAO extends GenericDAOImpl {

public List buscarTodosTelefoneEndereco(Integer indiceInicial, Integer indiceFinal,
Order... ordenacoes) {
List pessoas = super.buscarTodos(indiceInicial, indiceFinal,
ordenacoes);

for (Pessoa pessoa : pessoas) {
// Carrega os relazionamentos que são LAZY. Este método chama o
// método initialize do Hibernate.
pessoa.getTelefones().size();
pessoa.getEnderecos().size();
}

return pessoas;
}

@SuppressWarnings("unchecked")
public List buscarTodosHQL() {
Query query = getEntityManager().createNamedQuery("selectAllHQL");
List pessoas = query.getResultList();

return pessoas;
}

@SuppressWarnings("unchecked")
public List buscarTodosSQL() {

Query query = getEntityManager().createNamedQuery("selectAllNativo");
List pessoas = query.getResultList();

return pessoas;
}

@SuppressWarnings("unchecked")
public List buscarTodosComTelefone() {

List resultado = new ArrayList();
Query query = getEntityManager().createNamedQuery("selectAllNativoTelefone");

List resultados = query.getResultList();
for (Object object : resultados) {
Object[] arranjo = (Object[]) object;
Pessoa pessoa = (Pessoa) arranjo[0];
Telefone telefone = (Telefone) arranjo[1];
Collection telefones = new ArrayList();
telefones.add(telefone);
pessoa.setTelefones(telefones);

resultado.add(pessoa);
}

return resultado;
}



-----------------------------------------------

Em breve postarei como realizo a injeção do Entity Manager utilizando a classe DAOFactory. Qualquer dúvida ou coisa que precisarem me mandem mensagem.

5 comentários:

Fernando Scherrer disse...

Haw!

  Só uma sugestão: coloca os códigos entre tags <pre></pre>, assim não fica com a identação zuada (se você copiar de um lugar [eclipse, netbeans, etc] que esteja identado) fica mais fácil de ler.
  (assim)
  E se quiser deixar mais pro ainda, dá uma googleada por code highlight blogger que tem umas coisas interessantes.

Valewz!? E valeu por compartilhar.

Samuel Martins Delfim disse...

Obrigado pela dica Fernando...

Paulo Ribeiro disse...

Olá estou começando com JPA e li seu codigo sobre DAO generico e fiquei em duvida onde se le o nome que colocou no arquivo persistence.xml ???? como identifico ????

Samuel Martins Delfim disse...

Olá Paulo,

Não sei se eu entendi direito, mas o arquivo persistence.xml deve ser colocado dentro da pasta META-INF dentro do diretório onde se encontram os códigos fontes.

Automaticamente, se o seu arquivo estiver dentro desta pasta ele será reconhecido e utilizado como arquivo de configuração.

Dentro do arquivo persistence vc configura o acesso ao banco.

Qualquer problema me avise...

Danilo Barsotti disse...

Legal o artigo, mas acho que você esqueceu de colocar o public abstract class GenericDAOImpl< T > para identificar que a classe abstrata possue/é um tipo generico...