net.solarnetwork.central.dras.dao.ibatis.DrasIbatisGenericDaoSupport.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.central.dras.dao.ibatis.DrasIbatisGenericDaoSupport.java

Source

/* ==================================================================
 * DrasIbatisGenericDaoSupport.java - Jun 4, 2011 5:38:42 PM
 * 
 * Copyright 2007-2011 SolarNetwork.net Dev Team
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ==================================================================
 * $Id$
 * ==================================================================
 */

package net.solarnetwork.central.dras.dao.ibatis;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import net.solarnetwork.central.dao.ibatis.IbatisGenericDaoSupport;
import net.solarnetwork.central.domain.Entity;
import net.solarnetwork.central.dras.domain.Member;

import org.joda.time.DateTime;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.orm.ibatis.SqlMapClientCallback;

import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.ibatis.sqlmap.client.event.RowHandler;

/**
 * Abstract base Ibatis GenericDao for DRAS support.
 * 
 * @author matt
 * @version $Revision$
 */
public abstract class DrasIbatisGenericDaoSupport<T extends Entity<Long>> extends IbatisGenericDaoSupport<T> {

    /** A query property for an {@code Effective} ID value. */
    public static final String EFFECTIVE_ID_PROPERTY = "effectiveId";

    /** A query property for a general member ID value, e.g. group membership. */
    public static final String MEMBER_ID_PROPERTY = "memberId";

    /**
     * Constructor.
     * 
     * @param domainClass the domain class
     */
    public DrasIbatisGenericDaoSupport(Class<? extends T> domainClass) {
        super(domainClass);
    }

    @Override
    protected void preprocessInsert(T datum) {
        if (datum.getCreated() == null) {
            // get creation date from current DB transaction
            Object o = getSqlMapClientTemplate().queryForObject("NOW");
            if (o != null) {
                BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(datum);
                wrapper.setPropertyValue("created", o);
            }
        }
    }

    /**
     * Persist a domain object ID set related to the entity managed by this DAO.
     * 
     * <p>Calls {@link #storeRelatedSet(Long, Class, Set, Long, Map)} passing no
     * additional properties.</p>
     * 
     * @param parentId the ID of the parent of the member set
     * @param memberClass the class of the members
     * @param memberIdSet the set of member IDs to store
     * @param effectiveId the effective ID
     */
    protected void storeRelatedSet(final Long parentId, Class<?> memberClass, final Set<?> memberIdSet,
            final Long effectiveId) {
        storeRelatedSet(parentId, memberClass, memberIdSet, effectiveId, null);
    }

    /**
     * Persist a domain object ID set related to the entity managed by this DAO.
     * 
     * <p>This method will use the {@link #getRelationDelete()} and 
     * {@link #getRelationInsert()} query names to persist a set of related IDs to 
     * a single parent entity. It works by issuing a {@code DELETE} based
     * on the parent ID and effective ID (normally this should not actually
     * delete anything) and then one {@code INSERT} per ID in {@code memberIdSet}.</p>
     * 
     * <p>This method will pass the following query properties:</p>
     * 
     * <dl>
     *   <dt>{@link #ID_PROPERTY}</dt>
     *   <dd>The {@code parentId} value, passed to all queries.</dd>
     *   
     *   <dt>{@link #EFFECTIVE_ID_PROPERTY}</dt>
     *   <dd>The {@code effectiveId} value, passed to all queries.</dd>
     *   
     *   <dt>{@link #MEMBER_ID_PROPERTY}</dt>
     *   <dd>A single value from the {@code memberIdSet}, passed to 
     *   just the insert query.</dd>
     * </dl>
     * 
     * @param parentId the ID of the parent of the member set
     * @param memberClass the class of the members
     * @param memberIdSet the set of member IDs to store
     * @param effectiveId the effective ID
     * @param additionalProps any extra properties to pass to the SQL script
     * @see #getMemberSet(Long, Class, DateTime)
     */
    protected void storeRelatedSet(final Long parentId, Class<?> memberClass, final Set<?> memberIdSet,
            final Long effectiveId, final Map<String, ?> additionalProperties) {
        final String memberDomain = getMemberDomainKey(memberClass);
        final String deleteQuery = getRelationDelete() + memberDomain;
        final String insertQuery = getRelationInsert() + memberDomain;

        if (log.isDebugEnabled()) {
            log.debug("Assigning " + (memberIdSet == null ? 0 : memberIdSet.size()) + " members to "
                    + getDomainClass().getSimpleName() + " " + parentId + "'s " + memberDomain + " set effective "
                    + effectiveId);
        }

        getSqlMapClientTemplate().execute(new SqlMapClientCallback<Object>() {
            @Override
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                executor.startBatch();

                // delete all rows for the given effective ID (usually there should be none)
                Map<String, Object> props = new HashMap<String, Object>(2);
                if (additionalProperties != null) {
                    props.putAll(additionalProperties);
                }
                props.put(ID_PROPERTY, parentId);
                if (effectiveId != null) {
                    props.put(EFFECTIVE_ID_PROPERTY, effectiveId);
                }
                executor.delete(deleteQuery, props);

                // now insert rows for every member
                if (memberIdSet != null) {
                    for (Object memberId : memberIdSet) {
                        props.put(MEMBER_ID_PROPERTY, memberId);
                        executor.insert(insertQuery, props);
                    }
                }
                executor.executeBatch();
                return null;
            }
        });
    }

    /**
     * Get a set of members for a given class and parent ID.
     * 
     * <p>Calls {@link #getRelatedSet(Long, Class, DateTime, Map)} passing
     * in no additional properties.</p>
     * 
     * @param parentId the parent ID
     * @param memberClass the member class type
     * @param effectiveDate the effective date
     * @return the found members, never <em>null</em>
     */
    protected <E> Set<E> getRelatedSet(Long parentId, Class<? extends E> memberClass, DateTime effectiveDate) {
        return getRelatedSet(parentId, memberClass, effectiveDate, null);
    }

    /**
     * Get a set of members for a given class and parent ID.
     * 
     * <p>Use this method to get the complete set of members. The query
     * used will be based on {@link #getRelationQueryForParent()}
     * with {@link #getMemberDomainKey(Class)} appended.</p>
     * 
     * <p>This method will pass the following query properties:</p>
     * 
     * <dl>
     *   <dt>{@link #ID_PROPERTY}</dt>
     *   <dd>The {@code parentId} value.</dd>
     *   
     *   <dt>{@link #DATE_PROPERTY}</dt>
     *   <dd>The {@code effectiveDate} value.</dd>
     * </dl>
     * 
     * @param parentId the parent ID
     * @param memberClass the member class type
     * @param effectiveDate the effective date
     * @param additionalProps any extra properties to pass to the SQL script
     * @return the found members, never <em>null</em>
     * @see #storeMemberSet(Long, Class, Set, Long)
     */
    protected <E> Set<E> getRelatedSet(Long parentId, Class<? extends E> memberClass, DateTime effectiveDate,
            Map<String, ?> additionalProps) {
        final String memberDomain = getMemberDomainKey(memberClass);
        final String query = getRelationQueryForParent() + memberDomain;
        Map<String, Object> sqlProps = new HashMap<String, Object>(2);
        if (additionalProps != null) {
            sqlProps.putAll(additionalProps);
        }
        sqlProps.put(ID_PROPERTY, parentId);
        if (effectiveDate != null) {
            sqlProps.put(DATE_PROPERTY, effectiveDate);
        }
        final Set<E> members = new LinkedHashSet<E>();
        getSqlMapClientTemplate().queryWithRowHandler(query, sqlProps, new RowHandler() {
            @SuppressWarnings("unchecked")
            @Override
            public void handleRow(Object valueObject) {
                members.add((E) valueObject);
            }
        });
        return members;
    }

    /**
     * Get a set of members for a given class and parent ID.
     * 
     * <p>Use this method to get the complete set of members. The query
     * used will be based on {@link #getRelationQueryForParent()}
     * with {@link #getMemberDomainKey(Class)} appended.</p>
     * 
     * <p>This method will pass the following query properties:</p>
     * 
     * <dl>
     *   <dt>{@link #ID_PROPERTY}</dt>
     *   <dd>The {@code parentId} value.</dd>
     *   
     *   <dt>{@link #DATE_PROPERTY}</dt>
     *   <dd>The {@code effectiveDate} value.</dd>
     * </dl>
     * 
     * @param parentId the parent ID
     * @param memberClass the member class type
     * @param effectiveDate the effective date
     * @return the found members, never <em>null</em>
     * @see #storeMemberSet(Long, Class, Set, Long)
     */
    protected <E> SortedSet<E> getRelatedSortedSet(Long parentId, Class<? extends E> memberClass,
            DateTime effectiveDate) {
        final String memberDomain = getMemberDomainKey(memberClass);
        final String query = getRelationQueryForParent() + memberDomain;
        Map<String, Object> sqlProps = new HashMap<String, Object>(2);
        sqlProps.put(ID_PROPERTY, parentId);
        if (effectiveDate != null) {
            sqlProps.put(DATE_PROPERTY, effectiveDate);
        }
        final SortedSet<E> members = new TreeSet<E>();
        getSqlMapClientTemplate().queryWithRowHandler(query, sqlProps, new RowHandler() {
            @SuppressWarnings("unchecked")
            @Override
            public void handleRow(Object valueObject) {
                members.add((E) valueObject);
            }
        });
        return members;
    }

    /**
     * Persist a domain object ID set related to the entity managed by this DAO.
     * 
     * @param parentId the ID of the parent of the member set
     * @param memberClass the class of the members
     * @param memberIdSet the set of member IDs to store
     * @param effectiveId the effective ID
     * @see #storeRelatedSet(Long, Class, Set, Long)
     * @see #getMemberSet(Long, Class, DateTime)
     */
    protected void storeMemberSet(final Long parentId, Class<?> memberClass, final Set<Long> memberIdSet,
            final Long effectiveId) {
        storeRelatedSet(parentId, memberClass, memberIdSet, effectiveId);
    }

    /**
     * Get a set of members for a given class and parent ID.
     * 
     * @param parentId the parent ID
     * @param memberClass the member class type
     * @param effectiveDate the effective date
     * @return the found members, never <em>null</em>
     * @see #getRelatedSet(Long, Class, DateTime)
     * @see #storeMemberSet(Long, Class, Set, Long)
     */
    protected Set<Member> getMemberSet(Long parentId, Class<? extends Member> memberClass, DateTime effectiveDate) {
        return getRelatedSet(parentId, memberClass, effectiveDate);
    }

    /**
     * Query for a 1-to-1 related object.
     * 
     * <p>This uses the {@link #getRelationQueryForParent()} query appended
     * with {@link #getMemberDomainKey(Class)} to query for a single object.</p>
     * 
     * <p>This method will pass the following query properties:</p>
     * 
     * <dl>
     *   <dt>{@link #ID_PROPERTY}</dt>
     *   <dd>The {@code parentId} value.</dd>
     *   
     *   <dt>{@link #DATE_PROPERTY}</dt>
     *   <dd>The {@code effectiveDate} value.</dd>
     * </dl>
     * 
     * @param parentId the parent ID
     * @param effectiveDate the effective date, or <em>null</em> for current time
     * @param relatedClass the related object class
     * @return the related object, or <em>null</em> if not found
     */
    protected <R> R getRelated(Long parentId, DateTime effectiveDate, Class<? extends R> relatedClass) {
        Map<String, Object> sqlProps = new HashMap<String, Object>(2);
        sqlProps.put(ID_PROPERTY, parentId);
        if (effectiveDate != null) {
            sqlProps.put(DATE_PROPERTY, effectiveDate);
        }
        final String query = getRelationQueryForParent() + getMemberDomainKey(relatedClass);

        @SuppressWarnings("unchecked")
        R result = (R) getSqlMapClientTemplate().queryForObject(query, sqlProps);
        return result;
    }

    /**
     * Set a 1-to-1 related object relationship.
     * 
     * <p>The query name is {@link #getRelationInsert()} appended with
     * {@link #getMemberDomainKey(Class)}.</p>
     * 
     * <p>This method will pass the following query properties:</p>
     * 
     * <dl>
     *   <dt>{@link #ID_PROPERTY}</dt>
     *   <dd>The {@code parentId} value.</dd>
     *   
     *   <dt>{@link #EFFECTIVE_ID_PROPERTY}</dt>
     *   <dd>The {@code effectiveId} value.</dd>
     *   
     *   <dt>{@link #MEMBER_ID_PROPERTY}</dt>
     *   <dd>The {@code relatedId} value.</dd>
     * </dl>
     * 
     * @param parentId the parent ID
     * @param relatedId the related object ID
     * @param effectiveId the effective ID
     * @param relatedClass the related object class
     */
    protected void setRelated(Long parentId, Long relatedId, Long effectiveId, Class<?> relatedClass) {
        Map<String, Object> sqlProps = new HashMap<String, Object>(2);
        sqlProps.put(ID_PROPERTY, parentId);
        if (effectiveId != null) {
            sqlProps.put(EFFECTIVE_ID_PROPERTY, effectiveId);
        }
        sqlProps.put(MEMBER_ID_PROPERTY, relatedId);
        final String query = getRelationInsert() + getMemberDomainKey(relatedClass);
        getSqlMapClientTemplate().insert(query, sqlProps);
    }

}