Ola pessoal,
No post de hoje vamos ver como criar genericDAO e DAOFactory em poucos passos. O objetivo maior aqui é colocar a mão na massa. Os conceitos DAO Design Pattern não serão abordados, pois o que não falta é explicação na internet sobre o assunto. Vou considerar que você sabe conceitualmente um DAO, mas que nunca implementou de forma genérica.
Lets go…
Primeiro passo
Cria a interface GenericDAO :
public interface GenericDAO<T,Type extends Serializable> {
void beginTransaction();
void commitTransaction();
void save(T entity);
void delete (T entity);
List<T> listAll();
}
O código acima usa apenas o recurso de generics do Java 5, deixando a interface que extends dizer qual será o tipo para T e o para Type.
O que significa cada um?
Simples: o T será a classe, ou seja, a entidade. O Type representará o tipo que usaremos para o Id da entidade. Você pode estar se perguntando pq a letra T e a palavra type. Apenas segui uma convenção, mas poderia ser qualquer outra letra ou nome.
Agora precisamos criar a interface dos nossos DAO, que nesse caso teremos as seguintes interfaces: ClientDAO e AccountDAO.
public interface AccountDAO extends GenericDAO<Account, Long>{
}
public interface ClientDAO extends GenericDAO<Client, Long> {
}
Observe que nas nossas interfaces é que definimos para qual entidade ela está associada e qual será o tipo do nosso ID.
É nessa interface que colocamos os métodos específicos para o nosso DAO.
Abstract HibernateDAO
Criaremos uma classe abstract que vai implementar o métodos abstract da interface GenericDAO. E nossas classes concentras vão extends a HibernateDAO como veremos a seguir. Mas antes disso precisamos criar uma classe utilitária que chamei de HibernateUtil, que terá o método para obtermos a sessão, iniciar a transação, commit, etc.
public class HibernateUtil {
private static SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
private static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
public static Session getSession() {
Session session = threadLocal.get();
if (session == null) {
session = sessionFactory.openSession();
threadLocal.set(session);
}
return session;
}
public static void beginTransaction() {
getSession().beginTransaction();
}
public static void commitTransaction() {
getSession().getTransaction().commit();
}
public static void rollBackTransaction() {
getSession().getTransaction().rollback();
}
public static void closeSession() {
getSession().close();
}
}
E na classe HibernateDao temos o código a seguir:
public abstract class HibernateDAO<T, Type extends Serializable> implements GenericDAO<T, Type>{
private Class<T> persistentClass;
public HibernateDAO(Class persistentClass) {
super();
this.persistentClass = persistentClass;
}
@Override
public void beginTransaction() {
HibernateUtil.beginTransaction();
}
@Override
public void commitTransaction() {
HibernateUtil.commitTransaction();
}
@Override
public void save(T entity) {
HibernateUtil.getSession().saveOrUpdate(entity);
}
@Override
public void delete(T entity) {
HibernateUtil.getSession().delete(entity);
}
@Override
public List<T> listAll() {
HibernateUtil.beginTransaction();
Criteria criteria = HibernateUtil.getSession().createCriteria(persistentClass);
return criteria.list();
}
}
Observe que implementamos todos os métodos da GenericDAO, assim a classe que extends já tem a implementation done, ou seja, aquilo que é comum para qualquer classe DAO já vai estar implementado e disponível na classe pai.
Concrete DAO
Agora vamos criar a classe concreta que terá a implementação específica para cada DAO.
class HibernateClientDAO extends HibernateDAO<Client, Long> implements ClientDAO {
public HibernateClientDAO(){
// we passing the entity for super class
super(Client.class);
}
}
Como não temos nada de específico para implementar da interface ClientDAO deixaremos o código assim. Observe que no construtor passei qual será o tipo T, que nesse caso será a class do Client.
DAOFactory
Agora vamos criar um DAOFactory que será responsável por criar as instâncias das classes Hibernate. A classe DAOFactory será abstract e tendo apenas um método implementado que será o getFactory, o qual terá como objetivo simplesmente de retornar uma instância da classe.
public abstract class DAOFactory {
private static final Class FACTORY_CLASS = HibernateDAOFactory.class;
public static DAOFactory getFactory(){
try {
return (DAOFactory) FACTORY_CLASS.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
public abstract ClientDAO getClientDAO();
public abstract AccountDAO getAccountDAO();
}
Em seguida adicionamos os métodos que retornam a instância para as classes que implementam as interfaces ClientDAO e AccountDAO.
Agora teremos uma classe que implementa os métodos do DAOFactory, que será a classe HibernateDAOFactory.
public class HibernateDAOFactory extends DAOFactory{
@Override
public ClientDAO getClientDAO() {
return new HibernateClientDAO();
}
@Override
public AccountDAO getAccountDAO() {
return new HibernateAccountDAO();
}
}
Observe que apenas instanciamos as classes para cada DAO. Agora vamos criar HibernateAccountDAO
class HibernateAccountDAO extends HibernateDAO<Account, Long> implements AccountDAO {
public HibernateAccountDAO() {
super(Account.class);
}
}
E para testar, criaremos uma classe com o método main:
public class MainBank {
public static void main(String[] args) {
// getting instance of the factory
DAOFactory daoFactory = DAOFactory.getFactory();
// getting intance of clientDAO and starting transaction
daoFactory.getClientDAO().beginTransaction();
ClientDAO clientDAO = daoFactory.getClientDAO();
Client client = new Client();
client.setName(“Camilo Lopes”);
// creating object of the entity
Account checkigAccount = new Account();
checkigAccount.setAccountType(AccountType.CHECKING_ACCOUNT);
// associate acocunt with the client
checkigAccount.setClient(client);
// money available in account
checkigAccount.setBalance(BigDecimal.ONE);
client.getAccount().add(checkigAccount);
// saveing in hibernate session
clientDAO.save(client);
AccountDAO accountDAO = daoFactory.getAccountDAO();
accountDAO.save(checkigAccount);
// commit
clientDAO.commitTransaction();
}
}
Resultado
É isso ai pessoal, espero que tenham gostado. Vou ficando por aqui.
See ya!!!
Não entendi o uso da HibernateUtil, em que momento ela é chamada ?
No momento que vc precisa obter a sessão, iniciar/commit a transação. Conforme pode ser vista na classe public abstract class HibernateDAO
Seu blog é muito bom, mas infelizmente a formatação de códigos ainda não é boa e atrapalha um pouco. Talvez seja uma limitação do tema.
Com relação ao post, o delete() é assim mesmo ?
Eu tive pequenos problemas utilizando esse getSession().
Eu tinha alguns registros e fazia alterações em alguns, mas quando buscava novamente os registros vinham os antigos e não os atualizados, mesmo commitando e fechando sessão. Tanto que algumas vezes vinham atualizados. hehehe. Eu verifiquei que isso ocorria por causa do ThreadLocal, mesmo fechando as sessões ele pegava uma antiga com dados antigos.
Resolvi não usa-lo.
Fiz da forma abaixo que resolveu:
private Session session;
public static Session getSession() {
if (session == null || !session.isOpen()) {
session = sessionFactory.openSession();
}
return session;
}
Ainda não notei problemas com essa forma.
Abraço !
olá Wendel,
Essa formatação dos códigos é uma novela. Eu usava um plugin, só que é ruim, pq quando eles nao sao mais atualizados e eu removo ou surge um bug e eu n sabia e atualizo, dai acabava zuando tudo. E desistir do plugin, hoje uso as marcacoes simples do wordpress que não é bem o que eu queria, mas pelo menos resolvi o problema de não quebrar mais como os plugins me deixavam na mão.
Sobre o problema, eu tive algo do tipo com o Hibernate 4, no Hibernate 3 não tive esse problema não.
abracos,
Fala cara, eu sou desenvolvedor .NET e estou estudando Java. Neste exemplo, não daria para fazer a classe
public abstract class HibernateDAO implements GenericDAO concreta e usá-la para fazer a persistência?. Tipo:
HibernateDAO clientDAO = new HibernateDAO();
clientDAO.save(client);?
Valeu.
olá Rafael,
Sim nada impede. Mas, pq eu faria isso? Ela já faz a persistencia a partir da entidade. Pelo que entendi o que você sugeriu não deixa a classe generica para tratar qualquer entidade.
abraco, obrigado por comentar.
primeiramente parabéns pelo post…
Sua divisão entre interfaces e classes primeiramente me causou um certo susto, pensei: “Meu Deus! Quanta Classes estou criando!”, depois eu vi o quanto isso deixou mais legível meu projeto, realmente nos ajudou muito!
Achei um errinho no meio do sua classe HibernateDAO, o método delete está executando: HibernateUtil.getSession().save(entity); ao invés de HibernateUtil.getSession().delete(entity);
Um grande abraço!