Saturday, January 31, 2009

Hibernate DAO using Spring and Java Generics

Having created a number of Hibernate DAOs it does not take long to see a pattern emerge which would seem to lend itself to a typed generic DAO. As any good engineer will do, I first set off to see if someone else had already thought about this. It is sometimes easier to stand on the shoulders of giants, and I found one.

The work I am going to present was really inspired by the following blog posting:

http://www.nileshk.com/node/66

But I wanted to change the implementation somewhat, and add more methods that made sense to my problem domain. So what I am going to present is an extension to the above blog posting. The purpose of this is not to suggest that one implementation is better or worse, they are just different and I actually hope that perhaps my implementation will inspire someone else in their work.

The AbstractDao interface looks like the following - with nearly every comment removed for clarity. I hope the method names are suggestive enough:

package com.redpointtech.dao;

import java.util.Collection;
import java.util.List;

/**
* borrowed heavily from: http://www.nileshk.com/node/66
*/
public interface AbstractDao <DomainObject, KeyType> {

void update(DomainObject object);
void refresh(DomainObject object);
void save(DomainObject object);
Long saveOrUpdate(DomainObject object);
void saveAll(Collection<DomainObject> objects);


DomainObject load(KeyType id);
List<DomainObject> findAll();
List<DomainObject> findAllOrderById();
DomainObject findById(KeyType id);
List<DomainObject> findByIds(Collection<KeyType> idList);
List<DomainObject> findAllOrderByProp(String propertyName, Boolean asc);


int deleteAll();
void deleteAll(Collection<DomainObject> objects);
void delete(DomainObject object);
void deleteById(KeyType id);
int deleteByIds(Collection<KeyType> ids);

int count();

}



The Implementation looks as follows:

package com.redpointtech.dao;

import static org.springframework.util.CollectionUtils.isEmpty;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.Query;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

/**
*
* borrowed heavily from: http://www.nileshk.com/node/66
*/
public abstract class AbstractHibernateDaoImpl<T extends Serializable, KeyType extends Serializable>
extends HibernateDaoSupport implements AbstractDao<T, KeyType>{

@SuppressWarnings("unchecked")
protected Class<T> domainClass = getDomainClass();

public void refresh(T t) {
getHibernateTemplate().refresh(t);
}

@SuppressWarnings("unchecked")
public T load(KeyType id) {
return (T) getHibernateTemplate().load(domainClass, id);
}

@SuppressWarnings("unchecked")
public T findById(KeyType id) {
return (T) getHibernateTemplate().get(domainClass, id);
}

@SuppressWarnings("unchecked")
public List<T> findByIds(Collection<KeyType> idList) {
Assert.notNull(idList, "idList parameter cannot be null");
ClassMetadata cmd = getHibernateTemplate().getSessionFactory().getClassMetadata(domainClass);
String idProp = cmd.getIdentifierPropertyName();

if( idList.size() > 0 ) {
Criteria criteria = getHibernateTemplate().getSessionFactory()
.getCurrentSession().createCriteria(domainClass);
criteria.add(Restrictions.in(idProp, idList));

return criteria.list();
} else {
return new ArrayList<T>();
}
}

public void update(T t) {
getHibernateTemplate().update(t);
}

public void save(T t) {
getHibernateTemplate().saveOrUpdate(t);
}

public Long saveOrUpdate(T t) {
getHibernateTemplate().saveOrUpdate(t);
Long id = (Long)getHibernateTemplate().getSessionFactory().getClassMetadata(domainClass).getIdentifier(t, EntityMode.POJO);
return id;
}

public void saveAll(Collection<T> list) {
if(!isEmpty(list)) {
getHibernateTemplate().saveOrUpdateAll(list);
}
}

public void delete(T t) {
getHibernateTemplate().delete(t);
}

@SuppressWarnings("unchecked")
public List<T> findAll() {
return (getHibernateTemplate().find("from " + domainClass.getName()
+ " x"));
}

@SuppressWarnings("unchecked")
public List<T> findAllOrderById() {
ClassMetadata cmd = getHibernateTemplate().getSessionFactory().getClassMetadata(domainClass);
String idProp = cmd.getIdentifierPropertyName();

return findAllOrderByProp(idProp,true);
}

@SuppressWarnings("unchecked")
public List<T> findAllOrderByProp(String propertyName, Boolean asc) {

Criteria criteria = getHibernateTemplate().getSessionFactory()
.getCurrentSession().createCriteria(domainClass);
if( asc ) {
criteria.addOrder(Order.asc(propertyName));
} else {
criteria.addOrder(Order.desc(propertyName));
}


return criteria.list();
}


public void deleteById(KeyType id) {
Object obj = load(id);
getHibernateTemplate().delete(obj);
}

@SuppressWarnings("unchecked")
public int deleteByIds(Collection<KeyType> ids) {
int count = 0;

if(!isEmpty(ids)) {

ClassMetadata cmd = getHibernateTemplate().getSessionFactory().getClassMetadata(domainClass);
String idProp = cmd.getIdentifierPropertyName();

String hqlDelete = "delete " + domainClass.getName() + " x where x." + idProp + " in ( :ids )";

Query deleteQuery = getHibernateTemplate().getSessionFactory().getCurrentSession().createQuery(hqlDelete);
deleteQuery.setParameterList("ids", ids);

count = deleteQuery.executeUpdate();

}
return count;
}

public void deleteAll(Collection<T> objects) {
if(!isEmpty(objects)) {
getHibernateTemplate().deleteAll(objects);
}
}

public int deleteAll() {

String hqlDelete = "delete " + domainClass.getName();
Query deleteQuery = getHibernateTemplate().getSessionFactory().getCurrentSession().createQuery(hqlDelete);

int count = deleteQuery.executeUpdate();
return count;
}

public int count() {
ClassMetadata cmd = getHibernateTemplate().getSessionFactory().getClassMetadata(domainClass);
String idProp = cmd.getIdentifierPropertyName();

Criteria criteria = getHibernateTemplate().getSessionFactory()
.getCurrentSession().createCriteria(domainClass);
criteria.setProjection(Projections.countDistinct(idProp));
Object countValue = criteria.uniqueResult();

return Integer.parseInt(countValue.toString());
}

protected Class getDomainClass() {
if (domainClass == null) {
ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();
domainClass = (Class) thisType.getActualTypeArguments()[0];
}
return domainClass;
}

}



I incorporated this into my Redpoint Notes reference project and I will show how that looks for the DAO hierarchy.

NotesDao
package com.redpointtech.dao;

import java.util.List;

import com.redpointtech.domain.Note;

public interface NotesDao {

List<Note> getAllNotes();

Long saveOrUpdate(Note note);

}



HibernateNotesDao Interface
package com.redpointtech.dao;

import com.redpointtech.domain.Note;

public interface HibernateNotesDao extends NotesDao,
AbstractDao<Note, Long> {

}



HibernateNotesDaoImpl
package com.redpointtech.dao.impl;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.redpointtech.dao.AbstractHibernateDaoImpl;
import com.redpointtech.dao.HibernateNotesDao;
import com.redpointtech.domain.Note;

@Repository("hibernateNotesDao")
public class HibernateNotesDaoImpl extends AbstractHibernateDaoImpl<Note, Long>
implements HibernateNotesDao {



@Override
public List<Note> getAllNotes() {
List<Note> notes = this.findAll();
return notes;
}


}



As you can see I wrote virtually no code beyond what the abstract generic implementation gives me - and the getAllNotes is really contrived just to show how you would add a specific DAO method.

Using this kind of generic Hibernate DAO has saved us a lot of time and I hope you find this useful as well.

Sunday, January 25, 2009

Spring Blaze Integration 1.0 M1 with Hibernate

I have a reference project that I have been using to stitch the different Spring/Hibernate/Flex/AIR technologies together.

Well today, I wanted to upgrade to Spring3 and look at the new Spring BlazeDS Integration. You can find all of the information about the Spring BlazeDS integration here.

This particular project used as Flex 3 front end, communicating to a Tomcat/Spring2.5/SpringJDBC to MySql database. Today I decided to upgrade the application to use Spring3 and Hibernate 3.3.1 which I did not anticipate being a big project - but I was wrong. Well ok, it was not a big project but it was bigger than I expected it to be.

If you following along with the sample application from the Spring/BlazeDS integration it goes really well.

However when you go to integrate Hibernate and use the OpenSessionInViewFilter things get a little tricky.

Conventional Spring wisdom would have us use the ContextLoaderListener to load the Spring context much like the following:

    <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/main-application-config.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>



However, when I did that the MessageBroker complained that the ServletConfig was null so I decided I would initialize the Spring application context as the Spring BlazeDS Integration suggested. However - when I did that, the OpenSessionInView Filter complained that the web application context was not initialized.

So I was between the proverbial rock and a hard place.

I spent some time looking through the code to see how I could 'fix' this problem. Then it occurred to me that perhaps it was not a problem with the code. Maybe, this could fixed with a different configuration. The OpenSessionInView filter just needs information about the Hibernate Session, SessionFactory, DataSource, etc. It does not need to know about the MessageBroker and the MessageBroker does not need the Hibernate information.

So instead of trying to fix the code, I looked at how to fix my configuration.

I looked at my application config, and removed all of the elements that were related to Blaze integration and created a separate configuration for the servlet to load. I now have the ContextLoaderListener initialize part of the application config and the servlet the other part.

Here is a portion of the web.xml file configuration:

    <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/main-application-config.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


<filter>
<filter-name>hibernate-session</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernate-session</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>redpointnotes</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/blaze-config.xml</param-value>
</init-param>

<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>redpointnotes</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>




The blaze-config.xml file looks like the following:
  <bean id="mySpringManagedMessageBroker" 
class="org.springframework.flex.messaging.MessageBrokerFactoryBean" />

<bean id="flexMessageBroker" abstract="true">
<property name="messageBroker" ref="mySpringManagedMessageBroker" />
</bean>

<!-- bean id name is the name that will be exposed to Flex remoting as the destination name -->
<bean id="redpointNotesServiceDestination" parent="flexMessageBroker"
class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter">
<property name="service" ref="notesService" />
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

<!-- map request paths at /messagebroker to the BlazeDS MessageBroker -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/messagebroker/*=mySpringManagedMessageBroker
</value>
</property>
</bean>

<!-- dispatches requests mapped to a message broker -->
<bean class="org.springframework.flex.messaging.servlet.MessageBrokerHandlerAdapter" />




So by breaking up the configuration I was able to get around this 'chicken-n-egg' problem but I will still look to see if there is a more elegant way to handle this. If anyone has a better way of dealing with this, please post a comment so we can all benefit.

I hope this helps save someone some time.

Saturday, January 24, 2009

Flex Camp Chicago 2009

Hi Everyone

I just got back from Flex Camp Chicago 2009 and I would first like to thank everyone that worked so hard to make this event happen. It was a very informative camp with a wide range technical perspectives - both from presenters and the people in attendance.

There was so much covered during those two days, so instead of trying to summarize it all I am going to take certain presentations and topics and blog on those over the next few days.

The second day Keynote was from James Ward. He created his 'Top 10 things to like' and Flex. Here is a summary of that list. I am also including some audio clips of each of the 10 topics he discussed. Since some of what he did was actual code, it will be a little hard to follow just the audio but it is still worth the listen.:

#1 Threads with PixelBender
As you may know - the FlashPlayer is single threaded, at least as far as our applications are concerned, and this therefore makes long running calculations very difficult to do. With PixelBender, you have the ability to create very lightweight threads of execution. Now - there are plenty of reasons to not get too excited about this. First, it is not a robust threading model that those of us in the Java world have come to know and love ( or not love ). Second, you have to know how to program with the PixelBender language. What I did find interesting is the number of times threading the Flash Player came up during the conference. There definitely seems to be mounting pressure for a multithreaded environment capability.




#2 Text Layout Framework
This was very cool and I am really looking forward to these new features. You can find all of the details on the Adobe Labs site, but here is a quick summary.
* Flowing text around columns - including selection. True multi-column output is now going to be part of the framework.
* Text alpha for fading, etc. You can fade text in/out with a single font.
* Bidirectional text, vertical text in over 30 writing systems. Now that is 'International'




#3 Openness
Adobe continues to make more of its platform open which allows developers like us to really embrace and build upon the great work at Adobe. Currently AMF, Flex SDK, BlazeDS are all examples of their open source. Adobe recently announced the RTMP spec will be made open allowing for more choices in the streaming media. Last - but not least - check out bugs.adobe.com. If you are find a bug check to see if it is in the bug database, and if not submit. If you do find, please vote for it. This is our communication channel back to Adobe on what we feel is important to get fixed.




#4 Tour de Flex
This one is really awesome! You can take the Tour here but finish reading the top 10 list before you leave.
If you are fan of the component explorer - you are going to love Tour de Flex. It is similar to the component explorer except it is an AIR application, and it has much more content. You can see all of the components, interact with examples, see source code - just like with component explorer, but now the documentation is also integrated in. In addition to this, components offered by the community will also be available through Tour de Flex. There is also a great Eclipse plugin that integrates with Tour de Flex using another very cool new technology called Merapi ( thats my second topic for the blog post ).





#5 Portable RIAS
What does this mean? I can hear you ask. We are all now familiar with running Flex applications via a web browser - but have you ever thought about running a Flex application from a PDF document? For detailed information checkout James Ward's posting on Portable RIAs. James showed a demo of how he took the dashboard application, exported it to PDF and was able to actually run the application from the PDF document. The PDF was also able to update its data from the server. PDF Reader 9 and above comes with the flash player embedded which makes all of this possible.





#6 Flex 4 Gumbo
This is the new version of Flex and the Flex builder. There are a lot of exciting changes coming and the Gumbo SDK is available now from Adobe Labs. There are some great resource links on this page and I highly recommend you check them out.





#7 Rapid Application Development without the Rapids.
In this part of his presentation he talked about the new workflow and wizard capabilities to get applications up and running quickly. He was able to very quickly get a data driven, synchronized application up and running.




#8 AMF
James discussed and showed the Census demo which shows you how much better AMF is to all other protocols to communicate between Flex and the server. He made an interesting comments as part of an answer to a question. James commented that he would always use AMF over all other protocols unless there was a good reason not to. He also showed the group how easy it was to use RemoteObject with a remote server.




#9 RIA Best Practices.
James did not enumerate any particular best practices, but commented on the state of best practices and what RIA developers should consider. These practices are independent of the RIA technology and he is working with people from the Microsoft and JavaFX team. They have a site that you can reach here called RIA Practices.




#10 You
The developer community that maintains the interest level in the technology and all of the developers contributing to the open source movement.




Thanks so much for checking out my blog and come back often as I will be adding additional entries for other Flex Camp Chicago 2009 topics. My next blog entry will talk about Merapi which is a project to allow AIR and Java to communicate with one another. I see some really cool possibilities with this.