Package com.anasoft.os.daofusion.criteria

Source Code of com.anasoft.os.daofusion.criteria.AssociationPathRegister

package com.anasoft.os.daofusion.criteria;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.Subcriteria;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Register of {@link AssociationPath} instances and corresponding
* Hibernate {@link Criteria} to be reused when modifying the root
* {@link Criteria} instance.
*
* <p>
*
* This class is used by {@link NestedPropertyCriteria} to initialize
* {@link Subcriteria} mappings in a safe way, avoiding the
* <a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-879">
* duplicate association path</a> Hibernate Criteria API issue.
*
* <p>
*
* {@link AssociationPathRegister} is a thin wrapper around the given
* {@link Criteria} instance that gets initialized with existing
* {@link Subcriteria} mappings at construction time. It is therefore
* safe to create multiple {@link AssociationPathRegister} instances
* operating on the same {@link Criteria}.
*
* <p>
*
* You can use this class to modify {@link Criteria} instances in a safe
* way on your own as well (always prefer {@link #get(AssociationPath)} in
* favor of calling the {@link Criteria#createCriteria(String, String, int)
* createCriteria} method directly).
*
* @see AssociationPath
* @see NestedPropertyCriteria
*
* @author michal.jemala
* @author vojtech.szocs
*/
public class AssociationPathRegister {

    private static final Logger LOG = LoggerFactory.getLogger(AssociationPathRegister.class);
   
  private final Map<AssociationPath, Criteria> pathToCriteriaMap = new HashMap<AssociationPath, Criteria>();
 
  /**
   * Creates a new association path register, rooted
   * at the given {@link Criteria} instance.
   *
   * <p>
   *
   * Note that the register is initialized with Hibernate
   * {@link Criteria} mappings according to the current
   * state of <tt>rootCriteria</tt>.
   *
   * @param rootCriteria Root {@link Criteria} instance.
   */
  @SuppressWarnings("unchecked")
    public AssociationPathRegister(Criteria rootCriteria) {
      if (CriteriaImpl.class.isAssignableFrom(rootCriteria.getClass())) {
          Iterator<Subcriteria> subCriteriaIterator = CriteriaImpl.class.cast(rootCriteria).iterateSubcriteria();
         
          while (subCriteriaIterator.hasNext()) {
                Subcriteria subCriteria = subCriteriaIterator.next();
                AssociationPath associationPath = getSubCriteriaAssociationPath(subCriteria);
               
                pathToCriteriaMap.put(associationPath, subCriteria);
            }
      } else {
          LOG.warn("rootCriteria is not a Hibernate CriteriaImpl but {}", rootCriteria.getClass().getName());
      }
     
    pathToCriteriaMap.put(new AssociationPath(), rootCriteria);
  }
 
  /**
     * Returns an association path for the given <tt>subCriteria</tt>
     * by traversing its {@link Subcriteria} parents.
     *
     * @param subCriteria {@link Subcriteria} instance to check.
     * @return {@link AssociationPath} for the given <tt>subCriteria</tt>.
     */
    private AssociationPath getSubCriteriaAssociationPath(Subcriteria subCriteria){
        List<AssociationPathElement> elementList = new ArrayList<AssociationPathElement>();
        elementList.add(new AssociationPathElement(subCriteria.getPath()));
       
        Subcriteria currentSubCriteria = subCriteria;
       
        while (currentSubCriteria.getParent() != null
                && Subcriteria.class.isAssignableFrom(currentSubCriteria.getParent().getClass())) {
            currentSubCriteria = Subcriteria.class.cast(currentSubCriteria.getParent());
            elementList.add(0, new AssociationPathElement(currentSubCriteria.getPath()));
        }
       
        return new AssociationPath(elementList.toArray(new AssociationPathElement[0]));
    }
 
  /**
   * Returns a {@link Criteria} instance for the given
   * {@link AssociationPath}.
   *
   * <p>
   *
   * This method ensures that Hibernate {@link Criteria}
   * mappings are lazily initialized (with existing criteria
   * instances being reused) prior to returning the target
   * {@link Criteria}.
   *
   * <p>
   *
   * Resulting {@link Criteria} instances have unique aliases
   * (based on their association paths) so that they can be
   * referenced in complex Hibernate queries like this:
   *
   * <pre>
   * Criteria criteria1 = apRegister.get(associationPath1);
   * String alias1 = criteria1.getAlias();
   *
   * Criteria criteria2 = apRegister.get(associationPath2);
   * String alias2 = criteria2.getAlias();
   *
   * rootCriteria.add(
   *     Restrictions.or(
   *         Restrictions.eq(alias1 + "." + targetPropertyName1, value1),
   *         Restrictions.eq(alias2 + "." + targetPropertyName2, value2)
   *     ));
   * </pre>
   *
   * You can safely call this method multiple times with
     * same association path argument. Note that unused
     * association path criteria instances might break your
     * query behavior when using certain join types (e.g.
     * {@link NestedPropertyJoinType#INNER_JOIN inner join}).
     *
   * @param associationPath Association path for which
   * to obtain the {@link Criteria} instance.
   * @return {@link Criteria} instance for the given
     * {@link AssociationPath}.
   */
  public Criteria get(AssociationPath associationPath) {
      if (!pathToCriteriaMap.containsKey(associationPath)) {
          for (AssociationPath partialPath : associationPath) {
                if (!pathToCriteriaMap.containsKey(partialPath)) {
                    AssociationPath superPath = partialPath.getSuperPath();
                    AssociationPathElement lastElement = partialPath.getLastElement();
                   
                    Criteria parentCriteria = pathToCriteriaMap.get(superPath);
                    Criteria criteria = parentCriteria.createCriteria(
                            lastElement.getValue(), partialPath.getAlias(),
                            lastElement.getJoinType().getHibernateJoinType());
                   
                    pathToCriteriaMap.put(partialPath, criteria);
                }
            }
      }
     
    return pathToCriteriaMap.get(associationPath);
  }
 
}
TOP

Related Classes of com.anasoft.os.daofusion.criteria.AssociationPathRegister

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.