Design of a Java Application Framework Part 2 – DAO Layer using Hibernate
By admin on Aug 5, 2008 in Framework, Java, Programming
This is the sequel of my previous post on the AppFuse source code, a good MVC framework. Thanks to Matt Raible and the team for this wonderful framework.
By reading the code, I can understand the framework better in order to use it correctly.
For AppFuse, for the data access layer you can use Hibernate, iBATIS or JPA. Here I am going through the Hibernate code.
All the DAO interface shall implement the GenericDao interface.
/**
* Generic DAO (Data Access Object) with common methods to CRUD POJOs.
*
* <p>Extend this interface if you want typesafe (no casting necessary) DAO's for your
* domain objects.
*
* @param <T> a type variable
* @param <PK> the primary key for that type
*/
public interface GenericDao <T, PK extends Serializable> {
/**
* Generic method used to get all objects of a particular type. This
* is the same as lookup up all rows in a table.
* @return List of populated objects
*/
List<T> getAll();
/**
* Generic method to get an object based on class and identifier. An
* ObjectRetrievalFailureException Runtime Exception is thrown if
* nothing is found.
*
* @param id the identifier (primary key) of the object to get
* @return a populated object
* @see org.springframework.orm.ObjectRetrievalFailureException
*/
T get(PK id);
/**
* Checks for existence of an object of type T using the id arg.
* @param id the id of the entity
* @return - true if it exists, false if it doesn't
*/
boolean exists(PK id);
/**
* Generic method to save an object - handles both update and insert.
* @param object the object to save
* @return the persisted object
*/
T save(T object);
/**
* Generic method to delete an object based on class and id
* @param id the identifier (primary key) of the object to remove
*/
void remove(PK id);
/**
* Gets all records without duplicates.
* <p>Note that if you use this method, it is imperative that your model
* classes correctly implement the hashcode/equals methods</p>
* @return List of populated objects
*/
List<T> getAllDistinct();
/**
* Find a list of records by using a named query
* @param queryName query name of the named query
* @param queryParams a map of the query names and the values
* @return a list of the records found
*/
List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams);
}
In this interface, common CRUD methods are defined. The interface accepts 2 parameters, which is the type variable and the PK for that type.
And GenericDaoHibernate class implements GenericDao interface
/**
* This class serves as the Base class for all other DAOs - namely to hold
* common CRUD methods that they might all use. You should only need to extend
* this class when your require custom CRUD logic.
*
* <p>To register this class in your Spring context file, use the following XML.
* <pre>
* <bean id="fooDao" class="org.appfuse.dao.hibernate.GenericDaoHibernate">
* <constructor-arg value="org.appfuse.model.Foo"/>
* <property name="sessionFactory" ref="sessionFactory"/>
* </bean>
* </pre>
* @param <T> a type variable
* @param <PK> the primary key for that type
*/
public class GenericDaoHibernate<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {
/**
* Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging
*/
protected final Log log = LogFactory.getLog(getClass());
private Class<T> persistentClass;
/**
* Constructor that takes in a class to see which type of entity to persist
* @param persistentClass the class type you'd like to persist
*/
public GenericDaoHibernate(final Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public List<T> getAll() {
return super.getHibernateTemplate().loadAll(this.persistentClass);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public List<T> getAllDistinct() {
Collection result = new LinkedHashSet(getAll());
return new ArrayList(result);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T get(PK id) {
T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);
if (entity == null) {
log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
throw new ObjectRetrievalFailureException(this.persistentClass, id);
}
return entity;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public boolean exists(PK id) {
T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);
return entity != null;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T save(T object) {
return (T) super.getHibernateTemplate().merge(object);
}
/**
* {@inheritDoc}
*/
public void remove(PK id) {
super.getHibernateTemplate().delete(this.get(id));
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public List<T> findByNamedQuery(
String queryName,
Map<String, Object> queryParams) {
String []params = new String[queryParams.size()];
Object []values = new Object[queryParams.size()];
int index = 0;
Iterator<String> i = queryParams.keySet().iterator();
while (i.hasNext()) {
String key = i.next();
params[index] = key;
values[index++] = queryParams.get(key);
}
return getHibernateTemplate().findByNamedQueryAndNamedParam(
queryName,
params,
values);
}
}
Here is the example of the UserDao interface
/**
* User Data Access Object (GenericDao) interface.
*
*/
public interface UserDao extends GenericDao<User, Long> {
/**
* Gets users information based on login name.
* @param username the user's username
* @return userDetails populated userDetails object
* @throws org.springframework.security.userdetails.UsernameNotFoundException thrown when user not found in database
*/
@Transactional
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
/**
* Gets a list of users ordered by the uppercase version of their username.
*
* @return List populated list of users
*/
List<User> getUsers();
/**
* Saves a user's information.
* @param user the object to be saved
* @return the persisted User object
*/
User saveUser(User user);
/**
* Retrieves the password in DB for a user
* @param username the user's username
* @return the password in DB, if the user is already persisted
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
String getUserPassword(String username);
}
Note that Spring Framework is used for security and transactional control here.
And the UserDaoHibernate class shall extends GenericDaoHibernate and implements UserDao.
/**
* This class interacts with Spring's HibernateTemplate to save/delete and
* retrieve User objects.
*
*/
public class UserDaoHibernate extends GenericDaoHibernate<User, Long> implements UserDao, UserDetailsService {
/**
* Constructor that sets the entity to User.class.
*/
public UserDaoHibernate() {
super(User.class);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public List<User> getUsers() {
return getHibernateTemplate().find("from User u order by upper(u.username)");
}
/**
* {@inheritDoc}
*/
public User saveUser(User user) {
log.debug("user's id: " + user.getId());
getHibernateTemplate().saveOrUpdate(user);
// necessary to throw a DataIntegrityViolation and catch it in UserManager
getHibernateTemplate().flush();
return user;
}
/**
* Overridden simply to call the saveUser method. This is happenening
* because saveUser flushes the session and saveObject of BaseDaoHibernate
* does not.
*
* @param user the user to save
* @return the modified user (with a primary key set if they're new)
*/
@Override
public User save(User user) {
return this.saveUser(user);
}
/**
* {@inheritDoc}
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List users = getHibernateTemplate().find("from User where username=?", username);
if (users == null || users.isEmpty()) {
throw new UsernameNotFoundException("user '" + username + "' not found...");
} else {
return (UserDetails) users.get(0);
}
}
/**
* {@inheritDoc}
*/
public String getUserPassword(String username) {
SimpleJdbcTemplate jdbcTemplate =
new SimpleJdbcTemplate(SessionFactoryUtils.getDataSource(getSessionFactory()));
Table table = AnnotationUtils.findAnnotation(User.class, Table.class);
return jdbcTemplate.queryForObject(
"select password from " + table.name() + " where username=?", String.class, username);
}
}
Spring Framework is used heavily here since it has good support for Hibernate. Emm…. is there any other choice?
vojtech.szocs | Mar 20, 2009 | Reply
Hi, we have released an OpenSource project that goes far beyond a generic CRUD-like DAO.
You can find it here: http://opensource.anasoft.com/daofusion-site/ or http://opensource.anasoft.sk/daofusion-site/
DAO Fusion is a lightweight yet comprehensive tool for building reliable, maintainable and testable DAO layers using JPA / Hibernate.
It covers five main areas related to DAO layer development:
– persistent entity model
– standard persistent entity DAO interfaces / abstract implementations
– persistent entity criteria API with advanced filtering, sorting and paging capabilities
– criteria transfer object (CTO) pattern that allows clients to specify entity criteria easily (this is especially useful for grid-like operations)
– integration test support using JUnit / Spring TestContext framework (core DAO functionality is integration-tested out-of-the-box for many popular databases)
The project is open to everyone interested in JPA, Hibernate and related technologies.
Cheers,
Vojtech