-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Generic DAO proposal
PostPosted: Mon Apr 18, 2005 3:36 am 
Beginner
Beginner

Joined: Tue Mar 15, 2005 2:36 am
Posts: 32
Location: Bonn, Germany
Hello all!

Hallo Christian,
thank you for the "easy-to-read" examples and your activity here in this forum!

I looked in the source of CaveatEmptor and found many duplicated code in all DAOs. If you're using Java 5 already, you might use the following code as a base for all your simple User/Item/...DAO classes. I give no warranties if this piece is useful or breaks common/best practice in the persitent world of Hibernate. I just had fun learning two things in one go: generics and Hibernate. Both do rock!

Anyway, here is my use-case (expecting the User class around or imported):

Code:
public class UserDAO extends AbstractDataAccessObject<User> {

  public UserDAO() {
    super(User.class);
  }

  public User getByName(String name) {
    User example = new User(name, null);
    Collection<User> users = getByExample(example);
    if (users.size() == 0) {
      return null;
    }
    if (users.size() > 1) {
      throw new IllegalStateException("More than one user with name \""
          + name + "\" found in database.");
    }
    return users.iterator().next();
  }

}


What do you think?

Cheers,
Christian

Code:
import java.util.Collection;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Example;

/**
* An abstract and generic DAO using Hibernate.
* <p>
* Extend this class to make use of it.
* </p>
* @author Christian Stein
*/
public abstract class AbstractDataAccessObject<E> {

  protected final Class<E> entityClass;

  protected AbstractDataAccessObject(Class<E> entityClass) {
    this.entityClass = entityClass;
  }

  public Collection<E> getAll() throws HibernateException {
    Session session = HibernateUtil.getSession();
    return session.createCriteria(entityClass).list();
  }

  public int getAllSize() throws HibernateException {
    return getCount();
  }

  public Collection<E> getByExample(E entity) throws HibernateException {
    Session session = HibernateUtil.getSession();
    Criteria criteria = session.createCriteria(entityClass);
    criteria.add(Example.create(entity));
    return (Collection<E>) criteria.list();
  }

  public E getById(Long id) throws HibernateException {
    Session session = HibernateUtil.getSession();
    return (E) session.get(entityClass, id, LockMode.NONE);
  }

  public E getById(Long id, LockMode lockMode) throws HibernateException {
    Session session = HibernateUtil.getSession();
    return (E) session.get(entityClass, id, lockMode);
  }

  public int getCount() throws HibernateException {
    Session session = HibernateUtil.getSession();
    String tableName = entityClass.getSimpleName();
    Query query = session.createQuery("select count(*) from " + tableName); // not very generic... needs fix!
    int count = ((Integer) query.uniqueResult()).intValue();
    return count;
  }

  public E loadById(Long id) throws HibernateException {
    Session session = HibernateUtil.getSession();
    return (E) session.load(entityClass, id, LockMode.NONE);
  }

  public E loadById(Long id, LockMode lockMode) throws HibernateException {
    Session session = HibernateUtil.getSession();
    return (E) session.load(entityClass, id, lockMode);
  }

  public void makePersistent(boolean flush, Collection<E> es)
      throws HibernateException {
    Session session = HibernateUtil.getSession();
    for (E e : es) {
      session.persist(e);
    }
    if (flush) {
      session.flush();
    }
  }

  public void makePersistent(boolean flush, E... es)
      throws HibernateException {
    Session session = HibernateUtil.getSession();
    for (E e : es) {
      session.persist(e);
    }
    if (flush) {
      session.flush();
    }
  }

  public void makePersistent(Collection<E> es) throws HibernateException {
    makePersistent(false, es);
  }

  public void makePersistent(E... es) throws HibernateException {
    makePersistent(false, es);
  }

  public void makeTransient(boolean flush, Collection<E> es)
      throws HibernateException {
    Session session = HibernateUtil.getSession();
    for (E e : es) {
      session.delete(e);
    }
    if (flush) {
      session.flush();
    }
  }

  public void makeTransient(boolean flush, E... es)
      throws HibernateException {
    Session session = HibernateUtil.getSession();
    for (E e : es) {
      session.delete(e);
    }
    if (flush) {
      session.flush();
    }
  }

  public void makeTransient(Collection<E> es) throws HibernateException {
    makeTransient(false, es);
  }

  public void makeTransient(E... es) throws HibernateException {
    makeTransient(false, es);
  }

}


Top
 Profile  
 
 Post subject: Dynamic DAO instead
PostPosted: Thu Apr 21, 2005 2:47 pm 
Newbie

Joined: Mon Apr 11, 2005 3:45 pm
Posts: 2
Hi Christian,

I have developed an approach to building DAOs that does not require developers to code an actual class for each DAO, instead the DAOs are implemented as interfaces that expose the operations that they wish to perform. The interfaces do not have any knowledge of hibernate and therefore could be used with other ORM libraries.

How this works is that I have implemented a Dynamic Proxy InvocationHandler for use with DAO's and hibernate. It basically looks for predefined method names and processes these using the SpringFramework HibernateTemplate (e.g. methods starting with load invoke load, insert invoke save etc).

For queries if the method starts with get or find it finds the named query using the name class.method (e.g. com.me.MyClass.findXXX) and invokes the named query. For get methods it returns the first object.

I am going to propose this to the springframework team once I have time to write up the proposal properly.

Paul


Top
 Profile  
 
 Post subject: Another Generic DAO
PostPosted: Fri Aug 05, 2005 1:41 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
I've posted another proposal for DAO using generics

http://forum.hibernate.org/viewtopic.php?t=945951

I though you might be insterested to discuss it too.

Thanks


Top
 Profile  
 
 Post subject: Another DAO using Generics [repost with formatted code]
PostPosted: Fri Aug 05, 2005 1:50 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
I'm using the following DAO implementation for hibernate, which
I'd like to share on this forum to get other people opinions.

It is using java 5.0 generics and is based on the code found at
http://www.ericburke.com/blog/2004/11/h ... ava-5.html

with the difference that id class is also parameterized.

Basically when creating your own DAO you usually only need to write
finder methods

Here is an example of using this generic DAO class

Code:

public class CategoryDAO extends GenericDAO<Category, Long> {
  public CategoryDAO() {
    super(Category.class, Long.class);
  }

public List<Category> getRootCategories() {
    return get(Expression.isNull("parentCategory"));
  }
}



Please, let know what do you think of this approach.

Thanks a lot


Code:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;

public abstract class GenericDAO<T extends Serializable, K extends Serializable> {

   private Class persistentClass;
   private Class idClass;

   private static Log log = LogFactory.getLog(GenericDAO.class);

   protected Session currentSession() {
      return HibernateUtil.currentSession();
   }

   /**
    * Constructor
    *
    * @param persistentClass
    * @param idClass
    */
   public GenericDAO(Class persistentClass, Class idClass) {
      this.persistentClass = persistentClass;
      this.idClass = idClass;
      currentSession().beginTransaction();
   }

   /**
    * @return Returns the idClass.
    */
   public Class getIdClass() {
      return idClass;
   }

   /**
    * @return Returns the persistentClass.
    */
   public Class getPersistentClass() {
      return persistentClass;
   }

   /**
    * Persist entity in database
    *
    * @param entity
    */
   public void persist(T entity) {
      Session session = null;

      try {
         session = currentSession();
         session.persist(entity);
      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }
   }

   /**
    * Save entity in database
    *
    * @param entity
    */
   public void save(T entity) {
      Session session = null;

      try {
         session = currentSession();
         session.save(entity);
      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }
   }

   /**
    * Save or update entity in database
    *
    * @param entity
    */
   public void saveOrUpdate(T entity) {
      Session session = null;

      try {
         session = currentSession();
         session.saveOrUpdate(entity);
      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }
   }

   /**
    * Delete entity in database
    *
    * @param entity
    */
   public void delete(T entity) {
      Session session = null;

      try {
         session = currentSession();
         session.delete(entity);
      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }
   }

   /**
    * Delete all entities
    */
   public void deleteAll() {
      Session session = null;

      try {
         session = currentSession();
         session.delete("from " + getPersistentClass().getName() + " x");

      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }
   }

   /**
    * Find entity by ID
    *
    * @param id
    * @param lock
    * @return entity
    */
   @SuppressWarnings("unchecked")
   public T getById(K id, boolean lock) {
      T entity = null;

      try {
         if (lock) {
            entity = (T) currentSession().get(getPersistentClass(), id,
                  LockMode.UPGRADE);
         } else {
            entity = (T) currentSession().get(getPersistentClass(), id);
         }
      } catch (HibernateException ex) {
         log.error(ex.getMessages());
         throw new DaoException(ex);
      }

      return entity;
   }

   /**
    * Get all
    *
    * @return list of entities
    */
   @SuppressWarnings("unchecked")
   public List<T> getAll() {
      List<T> list = null;
      Session session = null;
      Query query = null;

      try {
         session = currentSession();
         query = session.createQuery("from "
               + getPersistentClass().getName() + " x");
         list = query.list();
      } catch (HibernateException ex) {
         throw new DaoException(ex);
      }

      return list;
   }

   /**
    * This is the cool finder method
    *
    * @param criterion
    * @return list of entities
    */
   @SuppressWarnings("unchecked")
   public List<T> get(Criterion... criterion) {
      List<T> list = null;

      Criteria criteria = currentSession().createCriteria(
            getPersistentClass());

      try {
         for (Criterion c : criterion) {
            criteria.add(c);
         }

         list = new ArrayList<T>(criteria.list());
      } catch (HibernateException ex) {
         throw new DaoException(ex);
      }

      return list;
   }
}

[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 2:02 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Why are you using the DAO pattern if you just pass on Hibernate (or in the future EJB3) operations?

The point of the DAO pattern is to encapsulate data access operations at a higher level, use case oriented. The challenge is to design this API, the methods exposed, the granularity and type of the parameters, etc. The challenge is not to expose basic Hibernate CRUD operations.

The same concern is true about the Spring magic stuff someone else proposed earlier in this thread. True, DAOs often also need basic CRUD operations, but that's rather trivial and hardly worth discussing (well, except that I'd make a distinction between state-oriented CRUD DAOs and statement-oriented CRUD DAOs).

Furthermore, another reason to use the DAO pattern is persistence layer portability. Now, this is often completely unnecessary, so it's not a major concern. However, if you expose the Hibernate Criteria API on the DAO interface, it's certainly not a portable design. Not saying that this is completely wrong, just questioning why you use DAO at all then.

If you want state-oriented data access operations at the level of Hibernate, use Hibernate APIs, no templates, no wrappers, no DAOs. You get database portability, Hibernate data access, and integrated statement-oriented JDBC handling (built into Hibernate) if absolutely needed.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 2:35 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

Hiding hibernate specific functionality is one of the points of DAO.
If you look at CategoryDAO example (this is the DAO which application
is going to use), you would see that it hides hibernate behind DAO.

GenericDAO is used to create DAOs used by application, and to avoid
code replication between those DAOs since normally the differ
by Id type, and finder methods.

The point of this generic DAO is not to be used directly by
business layer, but rather as a tool for creating business oriented DAOs
and avoid code duplication between them.

I do not think you noticed this by reading my previous post.

Thanks,
--MG


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 2:58 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Well yes, you are right. Of course you could encapsulate this in an abstract class. I was in part responding to the Spring magic, I've had an overdose of HibernateTemplate craziness. Your DAO base class example is actually quite nice, mind if I use it for the Hibernate in Action second edition?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 3:26 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

Quote:
mind if I use it for the Hibernate in Action second edition?


Yes, please, use it. In fact I just was looking at CaveatEmptor DAOs code
http://cvs.sourceforge.net/viewcvs.py/h ... ction/dao/

It could be refactored to quite shorter DAOs refering to GenericDAO

Code:
public class ItemDAO extends GenericDAO<Item, Long> {
   public ItemDAO() {
      super(Item.class, Long.class);
   }
   
   public Bid getMaxBid(Long itemId) {
      //TODO ...
      return null;
   }

   public Bid getMinBid(Long itemId) {
      //TODO ...
      return null;
   }
}


public class UserDAO extends GenericDAO<User, Long> {
   public UserDAO() {
      super(User.class, Long.class);
   }
}

...


Actually, it would be nice if Hibernate tools could generate not
just entity code from database schema, but sceleton code for DAOs as well, creating a DAO for each selected entity (or all) based on
Entity class and Id class. So the programmer would add business
specific methods to DAO (as various finders, as custom queries),
and would not need to worry about things like load by id, save, etc.

I've just finished one real-world J2EE web application
with around 30 hibernate DAOs and I can say that most of them had
so many similarities that it would be nice to have some built in support
into hibernate tool to generate DAOs code too.

Thanks for your great book about hibernate. It helped a lot
in my development.

Thanks a lot,
--Mikhail Grushinskiy



[/quote]


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 3:42 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
One more note. The idea of such a DAO is not new.
see Eric Burke's blog

http://64.233.161.104/search?q=cache:SG ... rics&hl=en

My code is based on that proposal. I've just further parameterized it
by Id class, to make it more generic.

Thanks,
--Mikhail Grushinskiy


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 06, 2005 9:56 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
I've re-written the CaveatEmptor DAO layer completely, I think it is a very nice implementation now with generics. It might take a few hours until you see my latest commits though:

http://cvs.sourceforge.net/viewcvs.py/h ... ction/dao/

Note that CaveatEmptor tests currently don't run if you check it out, I doubt it even compiles from CVS right now :) I'll blog about this once the Embedded EJB3 container tests run.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 06, 2005 9:59 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
The "final" version is visible in CVS when you see several Factory classes.


Top
 Profile  
 
 Post subject: Generic DAO
PostPosted: Sat Aug 06, 2005 1:26 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

I see that you removed parametrization by ID class.
It makes this impementation very limited since most
in real-world application ID types will be different for different
entities. I understand in your application they all are Long,
but for most of people it will limit reusability of this code.

In my example there also was restiction on persitentClass
that it has to implement Serializable, which is the case here.
GenericHibernateDAO<T extends Serializable, K extends Serializable>

GenericHibernateDAO should not let you create a DAO to a class
which is not Serializable.

Another note regarding

Code:
public List<T> get(Criterion... criterion)
method in GenericHibernateDAO.


The point of this method is not to expose it to business layer, but
rather to have it there for implemeting (business oriented) finders in HibernateDAOs
such as ItemDAOHibernate, CategoryDAOHibernate.

So I think it would be better if you leave it uncommented,
but make it protected instead of public.

You can even have methods like this one for CategoryDAOHibernate.

Code:
    public List<Category> getRootCategories() {
        return get(Expression.isNull("parentCategory"));
    }


to illustrate using of List<T> get(Criterion... criterion)

You now implemented abstract Factory pattern for DAO, but
you client code (command) layer is not using it yet.
(It might be that I just to not see latest CVS commits on a cvsweb).

Current implementation looks much nicer.
I hope that you could integrate my comments too.
When all this compiles and runs it should be quite nice DAO implementation, which many people could benefit from. Thank you.

Regards,
--Mikhail


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 06, 2005 1:45 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Thanks for the review.

I use single ID because that is what people _should_ do. I will mention that this can be extended to support different ID types and I'll probably see this impact myself at some point and maybe change my mind. But for an example app it's always important to show how the world should be and not how it is :) Thinking about it, I already have one class with composite ID, so I might need it anyway.

A persistent class has not to implement Serializable, so I don't see the reason for this restriction. Certainly, it might be recommendation to make persistent classes Serializable as a replacement for DTOs, but its not a requirement.

About the Criterion builder: You are right, I was just copy/pasting the code and didn't think much about its uses. I still have to discuss the exposure of Hibernate APIs on the DAO interface in the book :)

The client code is not ready for commit at this time. Once it is I'll probably blog about the new DAOs. Also trying to find a way to use generics with EJB3 beans to avoid the instantiation of "empty" DAOs when a parameterized generic can do the job.


Top
 Profile  
 
 Post subject: Generic DAO
PostPosted: Mon Aug 22, 2005 12:58 am 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

I think

Code:
public Class getPersistentClass()


can be replaced with

Code:
public Class<T> getPersistentClass()


in GenericHibernateDAO as well.

I'm also (as you know) still a strong proponent for paramertization by Id class.
In real word some of the tables might not have a id mapped to Long,
and use composite Ids. Heere are few more reasons...
If generic DAO is parameterized by Id class, it would be possible
to go even further and intergrate DAO code generation (as well as
interfaces and factory, find by natural id methods) into hbm2java.
I believe all CRUD DAO code can be generated same way as
entities and mapping code via reverse engineering of database schema.
When you have a schema with ~100 tables it would save *a lot* of
manual work. I think there are plenty of people who would welcome
DAO code generation by hibernate tools. I've posted it in hibernate tools forum too
to get some attention. Looking forward for the next edition.

Thanks,
--MG


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 22, 2005 1:34 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Max is already aware of this JDK 5.0 DAO style and it is also under consideration for hbm2jsf. Yes, I'll probably add the ID parameter.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.