This blog for Hibernate Generic DAO Project has been moved to WordPress http://hibernategenericdao.wordpress.com/. Bye bye Blogger!

Friday, December 19, 2008

Filtering by collection properties

The Search capability needs to be able to handle collection properties. Here is a proposal for how we might do it.

Add four new filter types:

* SOME: Takes another filter as the value. SOME evaluates to true if the specified filter evaluates to true for at least one element in the collection specified by the property.

* ALL: Same as SOME except that the filter must evaluate to true for all elements in the collection.

* EMPTY: Takes no value. True if
(a) the property is NULL,
(b) the property is a collection and it is empty, or
(c) the property is a string and it is the empty string ("")

* NOT_EMPTY: Inverse of EMPTY


Here are some examples:

Use case: projects that I am a member of

- if projects.members is a list of people

search.addFilterSome('members', Filter.equal('', me));
search.addFilterSome('members', Filter.equal('id', me.id));
These filter options would produce HQL something like this:

from Project p
where exists (from p.members m where m = :me)
[or optimized using inverse relationship]
where exists (from p.members m where m = :me)
- if projects.members is a list of ProjectMember many-to-many relationship class which in turn has an id component with 'project' and 'person' properties

search.addFilterSome('members', Filter.equal('id.person', me));
where exists (from p.members pm where pm.id.person = :me)

Use case: project has members

search.addFilterNotEmpty('members');
where size(members) > 0

Use case: project has at least one of the given members

search.addFilterSome('members', Filter.in('', us));
where exists (from p.members where m in (:us))

Use case: project has all the given members

search.addFilterSome('members', Filter.equal('', member1));
search.addFilterSome('members', Filter.equal('', member2));
search.addFilterSome('members', Filter.equal('', member3));
// I ran this and the subX_'s don't seem to be needed, but I might include them just in case.
where exists (from p.members sub1_m where sub1_m = :member1)
and exists (from p.members sub2_m where sub2_m = :member2)
and exists (from p.members sub3_m where sub3_m = :member3)

Use case: project has only the given members

search.addFilterAll('members', Filter.in('', us));
//same as not some (not condition)
where not exists (from p.members m where m not in (:us))

Use case: project has only members with top-secret clearance

search.addFilterAll(Filter.greaterOrEqual('clearance', TOP_SECRET));
where not exists (from p.members m where not (m.clearance >= :TOP_SECRET))
[or]
where not exists (from p.members m where (m.clearance is null or m.clearance < :TOP_SECRET))

Use case: project has fewer than 4 members

search.addFilterLessThan('members.size', 4);
where members.size <>

Use case: project has me as primary member

search.addFilterSome('members', Filter.and( Filter.equal('id.person', me), Filter.equal('primary', true) ) );

where exists (from p.members m where m.id.person = :me and m.primary = 1 )

Tuesday, December 16, 2008

Thoughts on new DAO methods

1.) One of the considerations is that if we provide two or more methods that do something similar (ex. merge and update), both methods will need to be overridden when the user wants to alter the functionality. This could lead to code duplication and possible inconsistencies.

2.) In Hibernate persist() and save() have the following distinction: persist() doesn't guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time. save() does guarantee to return an identifier.

3.) In Hibernate the difference between update() and merge() is significant. update() will make the given object persistent and throw an error if another object with the same ID is already persistent in the Session. merge() doesn't care if another object is already persistent, but it also doesn't make the given object persistent; it just copies over the values to the datastore.


4.) "find" doesn't make the most sense to me on the remote side. "fetch" seems to describe better what's happening.
    The other side is keeping things uniform with ourselves and with JPA. If the method names are the same it's easier to get up to speed and remember.

5.) I believe I prefer merge() to update(). update() can cause complicated exceptions when the same entity is already associated with the session. update() can be a hard method to override because of the necessity of keeping the same object; I've had trouble with this myself.
    So we can avoid this sort of problem by using
cat = dao.merge(catinstead of  dao.update(cat)
    The only hard part about this that I can think of right now is if the original cat is referenced by other entities or collections.

6.) Another consideration is that when we provide a JPA implementation it will be impossible to implement update() or save(). I wonder if we want to have an extended interface for the Hibernate implementation that also includes such methods, while keeping the common interface simpler.
    An extended interface might include simply an attach() method that corresponds to saveOrUpdate(), or we could even call it saveOrUpdate() (why complicate things?).

Please comment if you have any wisdom, throughts or opinions.

New DAO Methods

For the 0.3.5 build, I'm planning to add a new DAO implementation as the default DAO option. Framework users will have the option of the original DAO, the new default DAO or creating their own DAO with whatever methods they like.

I've been debating for a while what methods to use in the new DAO. I've been looking around for ideas (http://code.google.com/p/hibernate-generic-dao/wiki/DAOMethodDebateReferences). If you have any wisdom or opinions on this, please comment!

Here's what I've come up with so far:
void persist(Object... entities)
Object merge(Object... entities)
T find
(Class<T> klass, Serializable id)
boolean remove(Object... entities)
boolean removeById(Serializable klass, Object... ids)
List<T> findAll(Class<T> klass)
List search(Search search)
SearchResult searchAndCount(Search search)
long count(Class klass)
long count(Search search)
Object searchUnique(Search search)
boolean isAttached(Object entity)
void flush()
void refresh(Object... entities)
The basic idea is to stay close to the JPA EntityManager methods since that is a wide standard and we intend to make an implementation for JPA (in addition to the original Hibernate implementation). On top of that, some methods were added for searching and others for convenience.

Also, many methods have variable length argument lists for convenience. This could be a small inconvenience for developers wishing to override these methods.

Using both merge and persist means developers will need to override both (if they need to override the functionality). We may be able to simplify this by having both call some sort of common method(s) that are called before and after save.

To go with this DAO, a remote DAO would look like this:
Object find(String className, Serializable id)
List findAll(String className)
Object save(Object entity) //create or update based on whether id already exist in datastore
void remove(Object entity)
void removeById(String className, Serializable id)
void removeMulti(Object[] entities)
void removeMultiById(String className, Serializable[] ids)
List search(RemoteSearch search)
long count(RemoteSearch search)
SearchResult searchAndCount(RemoteSearch search)
Object searchUnique(RemoteSearch search)
The approach here is different than with GeneralDAO because we're not dealing with objects in the same way; however, we want to keep the naming fairly consistent.

Introducing the Hibernate Generic DAO project

The open source Hibernate Generic DAO project is hosted at Google Code:
http://code.google.com/p/hibernate-generic-dao/

The purpose of this framework is to save the time, tedium and possible inconsistency of hand coding DAO layer objects. To this end, the framework provides a generic implementation of DAO layer objects for an application using Hibernate. The generic implementation can be extended and overridden on a method-by-method basis for each domain object individually or across the entire layer. This means that it is always possible to write code for an exceptional case, entirely bypassing the framework, yet for the usual case no code needs to be written at all.

The framework also builds on top of these generic DAOs with an interface that is easily exposed as a web service or remote object. (At The Revere Group (Open Systems division), we are using this for Adobe Flex and GWT R.I.A.s.) This becomes extremely powerful because of the extensive search options available in the framework.

The search capability is what sets this generic DAO implementation apart. It uses a powerful search object with extensive filtering options, sorting, paging and result transformers. The search is simpler to use than Hibernate Criteria objects, and it can be passed to and from remote clients. This greatly simplifies the creation of flexible UI searches and filtered lists.

The purpose of this blog is to help bring out ways to improve the framework to make it as useful, helpful and easy as possible.