Uma luz com DaoFactory dinamica + Generics e Reflections

4 respostas
BLV_DOOM_JAVA

Ola pessoal!
Estou fazendo um software para uso pessoal para me ajudar em meu apontamento de horas gastas em atividades no meu trabalho.
Eu estou quase terminando as classes principais, depois dessa fase pretendo fazer a parte gráfica da aplicação, pois a mesma é uma aplicação web.

Explicando o tipo de ajuda que preciso:
Eu tenho minha camada de acesso a banco de dados...
Eu tentei me basear, melhor, me baseei no conceito de programar para Interfaces, ou seja, toda dao em meu sistema possui uma Interface que a representa.
Logo, minha estrutura de pacotes ficou assim:

com.porto.nucleo.diariodebordo.dao.interfaces; // pacote das Interfaces
com.porto.nucleo.diariodebordo.dao.implementacoes; // pacote das implementações
com.porto.nucleo.diariodebordo.dao.factory; // pacote das factorys

Então...continuando, segue abaixo a implementação de algumas classes que eu fiz que vão ajudar a entender melhor o que eu quero fazer...

package com.porto.nucleo.diariodebordo.dao.interfaces;

import java.sql.Connection;
import java.util.List;

import com.porto.nucleo.diariodebordo.exceptions.DiarioDeBordoException;
import com.porto.nucleo.diariodebordo.model.Entidade;

/**
 * Interface de uma dao genérica para classes que acessam o banco de dados.
 * @author Bruno Luiz Viana
 * @since 28/06/2011
 * @version 1.0.0
 * @param <T> Paramentro genérico que sempre será uma Entidade
 */
public interface Dao<T extends Entidade> {
	
	/**
	 * Método utilizado para incluir um novo registro no banco de dados.
	 * @param entidade registro que será incluido no banco de dados.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido no método.
	 */
	public void incluir(final T entidade) throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para alterar um registro no banco de dados.
	 * @param entidade registro que será alterado no banco de dados.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido no método.
	 */
	public void alterar(final T entidade) throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para deletar um registro no banco de dados.
	 * @param entidade registro que será deletado no banco de dados.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido no método.
	 */
	public void deletar(final T entidade) throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para buscar um registro no banco de dados pelo campo ID.
	 * @param id id do registro a ser pesquisado.
	 * @return objeto populado com os dados recuperdos da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido no método.
	 */
	public T buscarPorId(final Integer id) throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para listar todos registros armazenados no banco de dados.
	 * @return List contendo todos os registros
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido no método.
	 */
	public List<T> listarTodos() throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para setar o objeto connection utilizado por um Dao.
	 * @param connection conexão com o banco de dados.
	 */
	public void setConnection(final Connection connection);
	
	/**
	 * Método utilizado para obter o objeto connection utilizado por um Dao.
	 * @return Connection que representa a conexão com o banco dados.
	 */
	public Connection getConnection();

} // fim da interface Dao
package com.porto.nucleo.diariodebordo.dao.interfaces;

import java.util.List;

import com.porto.nucleo.diariodebordo.exceptions.DiarioDeBordoException;
import com.porto.nucleo.diariodebordo.model.Atividade;

/**
 * Interface de uma dao que lida com dados do banco de dados referentes a Atividade.
 * @author Bruno Luiz Viana
 * @since 29/06/2011
 * @version 1.0.0
 */
public interface AtividadeDao extends Dao<Atividade> {
	
	/**
	 * Método utilizado para retornar uma atividade pelo id de atividade realizada por trainee.
	 * @param id id da atividade realizada
	 * @return Atividade objeto populado com o resultado da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer problema que ocorrer durante a execução do método.
	 */
	public Atividade carregarPorIdAtividadeTrainee(final Integer id) throws DiarioDeBordoException;
	
	/**
	 * Método utilizado para retornar uma lista de todas atividade que possuam a descricao informada ao método (ou parte dela).
	 * @param descricao descricao a ser pesquisada no banco de dados.
	 * @return List contendo todos os resultados da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer problema que ocorrer durante a execução do método.
	 */
	public List<Atividade> carregarTodosPorDescricao(final String descricao) throws DiarioDeBordoException;

} // fim da interface AtividadeDao
package com.porto.nucleo.diariodebordo.dao.implementacoes;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.porto.nucleo.diariodebordo.dao.interfaces.AtividadeDao;
import com.porto.nucleo.diariodebordo.exceptions.DiarioDeBordoException;
import com.porto.nucleo.diariodebordo.model.Atividade;

public class AtividadeDaoImpl implements AtividadeDao {

	private Connection connection;
	
	/**
	 * Método construtor sem argumentos.
	 */
	public AtividadeDaoImpl(){
	} // fim do método construtor
	
	/**
	 * Método construtor com argumentos.
	 * @param connection conexão com o banco de dados.
	 */
	public AtividadeDaoImpl(final Connection connection) {
		this.connection = connection;
	} // fim do método construtor
	
	public void incluir(Atividade entidade) throws DiarioDeBordoException {
		final String sql = "insert into atividade (descricao) values (?)";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setString(1, entidade.getDescricao());
			stmt.execute();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao incluir o registro no banco de dados.", e.getCause());
		}
	}

	public void alterar(Atividade entidade) throws DiarioDeBordoException {
		final String sql = "update atividade set descricao = ? where id = ?";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setString(1, entidade.getDescricao());
			stmt.setInt(2, entidade.getId());
			stmt.execute();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao alterar o registro no banco de dados.", e.getCause());
		}
	}

	public void deletar(Atividade entidade) throws DiarioDeBordoException {
		final String sql = "delete from atividade where id = ?";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setInt(1, entidade.getId());
			stmt.execute();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao deletar o registro no banco de dados.", e.getCause());
		}
	}

	public Atividade buscarPorId(Integer id) throws DiarioDeBordoException {
		Atividade atividade = null;
		final String sql = "select id, descricao from atividade where id = ?";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setInt(1, id);
			final ResultSet rs = stmt.executeQuery();
			if (rs.next()) {
				atividade = new Atividade();
				atividade.setId(rs.getInt("id"));
				atividade.setDescricao(rs.getString("descricao"));
			}
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao pesquisar o registro pelo id.", e.getCause());
		}
		return atividade;
	}

	public List<Atividade> listarTodos() throws DiarioDeBordoException {
		final List<Atividade> listaAtividades = new ArrayList<Atividade>();
		final String sql = "select id, descricao from atividade";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			final ResultSet rs = stmt.executeQuery();
			while (rs.next()) {
				final Atividade atividade = new Atividade();
				atividade.setId(rs.getInt("id"));
				atividade.setDescricao(rs.getString("descricao"));
				listaAtividades.add(atividade);
			}
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao pesquisar todos os registros do banco de dados.", e.getCause());
		}
		return listaAtividades;
	}

	public List<Atividade> carregarTodosPorDescricao(String descricao)
			throws DiarioDeBordoException {
		final List<Atividade> listaAtividades = new ArrayList<Atividade>();
		final String sql = "select id, descricao from atividade where descricao like ?";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setString(1, "%" + descricao + "%");
			final ResultSet rs = stmt.executeQuery();
			while (rs.next()) {
				final Atividade atividade = new Atividade();
				atividade.setId(rs.getInt("id"));
				atividade.setDescricao(rs.getString("descricao"));
				listaAtividades.add(atividade);
			}
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao pesquisar todos os registros do banco de dados por descricao.", e.getCause());
		}
		return listaAtividades;
	}

	public Atividade carregarPorIdAtividadeTrainee(Integer id)
			throws DiarioDeBordoException {
		Atividade atividade = null;
		final String sql = "select a.id, a.descricao from atividade a, atividadetrainee at where at.id = ? " +
				"and a.id = at.idatividade";
		try {
			final PreparedStatement stmt = this.connection.prepareStatement(sql);
			stmt.setInt(1, id);
			final ResultSet rs = stmt.executeQuery();
			if (rs.next()) {
				atividade = new Atividade();
				atividade.setId(rs.getInt("id"));
				atividade.setDescricao(rs.getString("descricao"));
			}
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			throw new DiarioDeBordoException("Erro ao pesquisar o registro pelo id.", e.getCause());
		}
		return atividade;
	}

	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

}
package com.porto.nucleo.diariodebordo.dao.factory;

import com.porto.nucleo.diariodebordo.exceptions.DiarioDeBordoException;

/**
 * Classe que tem o objetivo de devolver Classes Dao diferentes dinamicamente.
 * @author Bruno Luiz Viana
 * @since 03/07/2011
 * @version 1.0.0
 */
public class DaoFactory {
	
	private static DaoFactory instance = null;
	
	/**
	 * Método construtor privado sem argumentos.
	 */
	private DaoFactory() {
	} // fim do método construtor
	
	/**
	 * Método utilizado para retornar uma instancia de DaoFactory para a aplicação.
	 * @return Uma instancia de DaoFactory
	 */
	public static final DaoFactory getInstance() {
		if (DaoFactory.instance == null) {
			DaoFactory.instance = new DaoFactory();
		}
		return DaoFactory.instance;
	} // fim do método getInstance
	
	/**
	 * Método utilizado para retornar classes dao dinamicamente.
	 * @param nomeDao nome da interface a qual a implementação será instanciada.
	 * @return Object que guarda a referencia para o Dao instanciado.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido durante a execução do método.
	 */
	public Object getDao(final String nomeDao) throws DiarioDeBordoException {
		Object daoImpl = null;
		final String nomePackage = "com.porto.nucleo.diariodebordo.dao.implementacoes.";
		final String nomeClasse = nomeDao + "Impl";
		final String fqn = nomePackage + nomeClasse;
		try {
			final Class<? extends Object> classe = Class.forName(fqn);
			daoImpl = classe.newInstance();
		} catch (InstantiationException e) {
			throw new DiarioDeBordoException("Erro ao instanciar o dao requisitado pela aplicação.", e.getCause());
		} catch (IllegalAccessException e) {
			throw new DiarioDeBordoException("Erro ao instanciar o dao requisitado pela aplicação.", e.getCause());
		} catch (ClassNotFoundException e) {
			throw new DiarioDeBordoException("Erro ao instanciar o dao requisitado pela aplicação.", e.getCause());
		}
		return daoImpl;
	} // fim do método getDao
	
} // fim da classe DaoFactory
package com.porto.nucleo.diariodebordo.business;

import java.sql.Connection;
import java.util.List;

import com.porto.nucleo.diariodebordo.constantes.TipoManutencao;
import com.porto.nucleo.diariodebordo.dao.factory.DaoFactory;
import com.porto.nucleo.diariodebordo.dao.implementacoes.AtividadeDaoImpl;
import com.porto.nucleo.diariodebordo.dao.interfaces.AtividadeDao;
import com.porto.nucleo.diariodebordo.exceptions.DiarioDeBordoException;
import com.porto.nucleo.diariodebordo.model.Atividade;

/**
 * Classe que trata de toda regra de negócio referente a Atividade.
 * @author Bruno Luiz Viana
 * @since 03/07/2011
 * @version 1.0.0
 */
public abstract class AtividadeBusiness {

	/**
	 * Método utilizado para realizar as operações de manutenção de registros de
	 * Atividade (Inclusão, Alteração, Exclusão).
	 * @param atividade registro que será tratado.
	 * @param tipoManutencao tipo de operação que será realizada.
	 * @param connection conexão com o banco de dados que será usada.
	 * @throws DiarioDeBordoException - Para qualquer problema ocorrido durante a execução do método.
	 */
	public static void manterAtividade(final Atividade atividade,
			final TipoManutencao tipoManutencao, final Connection connection)
			throws DiarioDeBordoException {
		final DaoFactory daoFactory = DaoFactory.getInstance();
		final AtividadeDao atividadeDao = (AtividadeDaoImpl) daoFactory.getDao(AtividadeDao.class.getSimpleName());
		atividadeDao.setConnection(connection);
		switch (tipoManutencao) {
		case INCLUSAO:
			atividadeDao.incluir(atividade);
			break;
		case ALTERACAO:
			atividadeDao.alterar(atividade);
			break;
		case EXCLUSAO:
			atividadeDao.deletar(atividade);
			break;
		default:
			break;
		} // fim do bloco switch/case
	} // fim do método manterAtividade
	
	/**
	 * Método utilizado para buscar uma atividade pelo id.
	 * @param id id da atividade a ser pesquisada no banco de dados.
	 * @param connection conexão com o banco de dados.
	 * @return Atividade - objeto populado com o resultado da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer erro ocorrido durante a execução do método.
	 */
	public static Atividade buscarPorId(final Integer id, final Connection connection) throws DiarioDeBordoException {
		final DaoFactory daoFactory = DaoFactory.getInstance();
		final AtividadeDao atividadeDao = (AtividadeDaoImpl)daoFactory.getDao(AtividadeDao.class.getSimpleName());
		atividadeDao.setConnection(connection);
		final Atividade atividade = atividadeDao.buscarPorId(id);
		return atividade;
	} // fim do método buscarPorId
	
	/**
	 * Método utilizado para buscar todas as atividades no banco de dados.
	 * @param connection conexão com o banco de dados.
	 * @return List contendo todos os resultados da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer erro ocorrido durante a execução do método.
	 */
	public static List<Atividade> listarTodos(final Connection connection) throws DiarioDeBordoException {
		final DaoFactory daoFactory = DaoFactory.getInstance();
		final AtividadeDao atividadeDao = (AtividadeDaoImpl)daoFactory.getDao(AtividadeDao.class.getSimpleName());
		atividadeDao.setConnection(connection);
		final List<Atividade> listaAtividades = atividadeDao.listarTodos();
		return listaAtividades;
	} // fim do método listarTodos
	
	/**
	 * Método utilizado para buscar todas as atividades no banco de dados por descrição.
	 * @param descricao pela qual as atividades serão pesquisadas.
	 * @param connection conexão com o banco de dados.
	 * @return List contendo todos os resultados da pesquisa.
	 * @throws DiarioDeBordoException - Para qualquer erro ocorrido durante a execução do método.
	 */
	public static List<Atividade> carregarTodosPorDescricao(final String descricao, final Connection connection) throws DiarioDeBordoException {
		final DaoFactory daoFactory = DaoFactory.getInstance();
		final AtividadeDao atividadeDao = (AtividadeDaoImpl)daoFactory.getDao(AtividadeDao.class.getSimpleName());
		atividadeDao.setConnection(connection);
		final List<Atividade> listaAtividades = atividadeDao.carregarTodosPorDescricao(descricao);
		return listaAtividades;
	} // fim do método carregarTodosPorDescricao
	
	/**
	 * Método utilizado para buscar todas as atividades no banco de dados pelo id de um registro de AtividadeTrainee.
	 * @param id id de uma atividade realizada por um trainee.
	 * @param connection conexão com o banco de dados.
	 * @return Atividade - objeto populado com o resultado da busca.
	 * @throws DiarioDeBordoException - Para qualquer erro ocorrido durante a execução do método.
	 */
	public static Atividade carregarPorAtividadeTrainee(final Integer id, final Connection connection) throws DiarioDeBordoException {
		final DaoFactory daoFactory = DaoFactory.getInstance();
		final AtividadeDao atividadeDao = (AtividadeDaoImpl)daoFactory.getDao(AtividadeDao.class.getSimpleName());
		atividadeDao.setConnection(connection);
		final Atividade atividade = atividadeDao.carregarPorIdAtividadeTrainee(id);
		return atividade;
	} // fim do método carregarPorAtividadeTrainee

} // fim da classe AtividadeBusiness

Então gente...
Meu problema é mais uma duvida sobre boas práticas...
Como vcs podem ver, meu código na minha DaoFactory tem Strings chumbadas.
Eu gostaria que alguém me desse um sugestão de como generalizar mais essa classe e conseguir colocar um pouco mais de Reflections nela.

Agradeço desde agora.

4 Respostas

ganondorfan

Gosteis muito da forma como você documentou sua classe.

Olha acho que da forma que você fez está de bom tamanho, mas eu tenho uma crítica (Construtiva eu espero) sobre a forma que você constrói seus objetos. Você sempre constrói do zero o objeto em diferentes métodos, vide o exemplo em que retorna uma instancia da entidade e a outra que retorna uma coleção (Existe código duplicado na construção), eu costuma criar uma classe utilitária com todas as construções de entidades do banco, assim quando tenho algum problema existe apenas um lugar no programa a corrigir.

Outra pratica que eu utilizo (não posso afirmar que seja uma boa prática de programação) é declarar todas as Query’s como atributos da minha DAO, apenas a chamando nos métodos, isso facilita na hora de consultar algum método em específico, pois as mesmas ficam centralizadas.

No mais, espero ter contribuído com seu tópico.

BLV_DOOM_JAVA

ganondorfan:
Gosteis muito da forma como você documentou sua classe.

Olha acho que da forma que você fez está de bom tamanho, mas eu tenho uma crítica (Construtiva eu espero) sobre a forma que você constrói seus objetos. Você sempre constrói do zero o objeto em diferentes métodos, vide o exemplo em que retorna uma instancia da entidade e a outra que retorna uma coleção (Existe código duplicado na construção), eu costuma criar uma classe utilitária com todas as construções de entidades do banco, assim quando tenho algum problema existe apenas um lugar no programa a corrigir.

Outra pratica que eu utilizo (não posso afirmar que seja uma boa prática de programação) é declarar todas as Query’s como atributos da minha DAO, apenas a chamando nos métodos, isso facilita na hora de consultar algum método em específico, pois as mesmas ficam centralizadas.

No mais, espero ter contribuído com seu tópico.

Valeu cara…
Eu entendi o que vc falou, porém isso na minha concepção não tem tanto impacto na execução, mas vou tentar ajeitar mais minhas classes.

Mas eu gostaria mesmo era uma ajuda com a Classe DaoFactory, pois gostaria de saber se ela esta certa.

Vlw pessoal.

BLV_DOOM_JAVA

wellington.nogueira:
Não achei teu dao ruim não e deve atender sua necessidade.
O que eu não gostei nele é que ele impõe um contrato extra a teus DAOs:

  • O DAO, obrigatoriamente, terá um construtor vazio;
  • Sua interface só poderá ter uma única implementação e;
  • Todos os DAOs deverão ficar num mesmo pacote.

Acho que isso engessa um pouco a utilidade de teu daoFactory.

Existe alguma forma de amenizar esse problema?
Pois eu pensei em algumas formas, e todas deram errado, sendo que apenas essa deu certo…teria alguma forma de retirar essa dependencia de pacote?
Essa é uma grande questão que eu tenho em mente.

Eu estava vendo alguma coisa sobre Generics e Reflection, porém não consegui entender o conceito…da para aplicar nesse Dao?

Obrigado.

WellingtonRamos

Não achei teu dao ruim não e deve atender sua necessidade.
O que eu não gostei nele é que ele impõe um contrato extra a teus DAOs:

  • O DAO, obrigatoriamente, terá um construtor vazio;
  • Sua interface só poderá ter uma única implementação e;
  • Todos os DAOs deverão ficar num mesmo pacote.

Acho que isso engessa um pouco a utilidade de teu daoFactory.

Criado 4 de julho de 2011
Ultima resposta 6 de jul. de 2011
Respostas 4
Participantes 3