Design of a Java Application Framework Part 3 - DAO Layer using iBATIS

This is a sequel to my previous posts on AppFuse. For AppFuse, for the persistence layer you can have the flexibility of using Hibernate, iBATIS or JPA.

Whether to use Hibernate or iBATIS depends on whether you have full control over the data model, as described in many articles.

For iBATIS support, all DAO interfaces must extends GenericDao.

package org.appfuse.dao;
 
import java.io.Serializable;
import java.util.List;
 
 
/**
 * 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);
} 

The interface is different from Hibernate.

Take UserDao interface, it extends GenericDao, as shown below

package org.appfuse.dao;
 
import java.util.List;
 
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.appfuse.model.User;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
 
/**
 * 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);
}

Spring Framework is used in this case, as shown by the code.

For iBATIS, GenericDaoiBatis class is as shown below.

package org.appfuse.dao.ibatis;
 
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.appfuse.dao.GenericDao;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.ClassUtils;
 
import java.io.Serializable;
import java.util.List;
 
/**
 * 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>
 *      &lt;bean id=”fooDao” class=”org.appfuse.dao.ibatis.GenericDaoiBatis”&gt;
 *          &lt;constructor-arg value=”org.appfuse.model.Foo”/&gt;
 *          &lt;property name=”sessionFactory” ref=”sessionFactory”/&gt;
 *      &lt;/bean&gt;
 * </pre>
 *
 * @param <T> a type variable
 * @param <PK> the primary key for that type
 */
public class GenericDaoiBatis<T, PK extends Serializable> extends SqlMapClientDaoSupport 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 GenericDaoiBatis(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }
 
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(“unchecked”)
    public List<T> getAll() {
        return getSqlMapClientTemplate().queryForList(
                iBatisDaoUtils.getSelectQuery(ClassUtils.getShortName(this.persistentClass)), null);
    }
 
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(“unchecked”)
    public T get(PK id) {
        T object = (T) getSqlMapClientTemplate().queryForObject(
                iBatisDaoUtils.getFindQuery(ClassUtils.getShortName(this.persistentClass)), id);
        if (object == null) {
            log.warn(“Uh oh, ‘” + this.persistentClass + “‘ object with id ‘” + id + “‘ not found…”);
            throw new ObjectRetrievalFailureException(ClassUtils.getShortName(this.persistentClass), id);
        }
        return object;
    }
 
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(“unchecked”)
    public boolean exists(PK id) {
        T object = (T) getSqlMapClientTemplate().queryForObject(
                iBatisDaoUtils.getFindQuery(ClassUtils.getShortName(this.persistentClass)), id);
        return object != null;
    }
 
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(“unchecked”)
    public T save(final T object) {
        String className = ClassUtils.getShortName(object.getClass());
        Object primaryKey = iBatisDaoUtils.getPrimaryKeyValue(object);
        Class primaryKeyClass = iBatisDaoUtils.getPrimaryKeyFieldType(object);
        String keyId = null;
 
        // check for null id
        if (primaryKey != null) {
            keyId = primaryKey.toString();
        }
 
        // check for new record
        if (StringUtils.isBlank(keyId)) {
            iBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
            primaryKey = getSqlMapClientTemplate().insert(iBatisDaoUtils.getInsertQuery(className), object);
            iBatisDaoUtils.setPrimaryKey(object, primaryKeyClass, primaryKey);
        } else {
            iBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
            getSqlMapClientTemplate().update(iBatisDaoUtils.getUpdateQuery(className), object);
        }
 
        // check for null id
        if (iBatisDaoUtils.getPrimaryKeyValue(object) == null) {
            throw new ObjectRetrievalFailureException(className, object);
        } else {
            return object;
        }
    }
 
    /**
     * {@inheritDoc}
     */
    public void remove(PK id) {
        getSqlMapClientTemplate().update(
                iBatisDaoUtils.getDeleteQuery(ClassUtils.getShortName(this.persistentClass)), id);
    }
}

And here is the UserDaoiBatis implementation



Trackback URL

Did you enjoy this post? Why not leave a comment below and continue the conversation, or subscribe to my feed and get articles like this delivered automatically to your feed reader.

Comments

No comments yet.

Leave a comment

(required)

(required)