Package org.hibernate.criterion

Source Code of org.hibernate.criterion.Example$PropertySelector

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.criterion;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;

/**
* Support for query by example.
*
* <pre>
* List results = session.createCriteria(Parent.class)
*     .add( Example.create(parent).ignoreCase() )
*     .createCriteria("child")
*         .add( Example.create( parent.getChild() ) )
*     .list();
* </pre>
*
* "Examples" may be mixed and matched with "Expressions" in the same Criteria.
*
* @see org.hibernate.Criteria
* @author Gavin King
*/

public class Example implements Criterion {
  private final Object exampleEntity;
  private PropertySelector selector;

  private boolean isLikeEnabled;
  private Character escapeCharacter;
  private boolean isIgnoreCaseEnabled;
  private MatchMode matchMode;

  private final Set<String> excludedProperties = new HashSet<String>();

  /**
   * Create a new Example criterion instance, which includes all non-null properties by default
   *
   * @param exampleEntity The example bean to use.
   *
   * @return a new instance of Example
   */
  public static Example create(Object exampleEntity) {
    if ( exampleEntity == null ) {
      throw new NullPointerException( "null example entity" );
    }
    return new Example( exampleEntity, NotNullPropertySelector.INSTANCE );
  }

  /**
   * Allow subclasses to instantiate as needed.
   *
   * @param exampleEntity The example bean
   * @param selector The property selector to use
   */
  protected Example(Object exampleEntity, PropertySelector selector) {
    this.exampleEntity = exampleEntity;
    this.selector = selector;
  }

  /**
   * Set escape character for "like" clause if like matching was enabled
   *
   * @param escapeCharacter The escape character
   *
   * @return {@code this}, for method chaining
   *
   * @see #enableLike
   */
  public Example setEscapeCharacter(Character escapeCharacter) {
    this.escapeCharacter = escapeCharacter;
    return this;
  }

  /**
   * Use the "like" operator for all string-valued properties.  This form implicitly uses {@link MatchMode#EXACT}
   *
   * @return {@code this}, for method chaining
   */
  public Example enableLike() {
    return enableLike( MatchMode.EXACT );
  }

  /**
   * Use the "like" operator for all string-valued properties
   *
   * @param matchMode The match mode to use.
   *
   * @return {@code this}, for method chaining
   */
  public Example enableLike(MatchMode matchMode) {
    this.isLikeEnabled = true;
    this.matchMode = matchMode;
    return this;
  }

  /**
   * Ignore case for all string-valued properties
   *
   * @return {@code this}, for method chaining
   */
  public Example ignoreCase() {
    this.isIgnoreCaseEnabled = true;
    return this;
  }

  /**
   * Set the property selector to use.
   *
   * The property selector operates separate from excluding a property.
   *
   * @param selector The selector to use
   *
   * @return {@code this}, for method chaining
   *
   * @see #excludeProperty
   */
  public Example setPropertySelector(PropertySelector selector) {
    this.selector = selector;
    return this;
  }

  /**
   * Exclude zero-valued properties.
   *
   * Equivalent to calling {@link #setPropertySelector} passing in {@link NotNullOrZeroPropertySelector#INSTANCE}
   *
   * @return {@code this}, for method chaining
   *
   * @see #setPropertySelector
   */
  public Example excludeZeroes() {
    setPropertySelector( NotNullOrZeroPropertySelector.INSTANCE );
    return this;
  }

  /**
   * Include all properties.
   *
   * Equivalent to calling {@link #setPropertySelector} passing in {@link AllPropertySelector#INSTANCE}
   *
   * @return {@code this}, for method chaining
   *
   * @see #setPropertySelector
   */
  public Example excludeNone() {
    setPropertySelector( AllPropertySelector.INSTANCE );
    return this;
  }

  /**
   * Exclude a particular property by name.
   *
   * @param name The name of the property to exclude
   *
   * @return {@code this}, for method chaining
   *
   * @see #setPropertySelector
   */
  public Example excludeProperty(String name) {
    excludedProperties.add( name );
    return this;
  }

  @Override
  public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
    final StringBuilder buf = new StringBuilder().append( '(' );
    final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
        criteriaQuery.getEntityName( criteria )
    );
    final String[] propertyNames = meta.getPropertyNames();
    final Type[] propertyTypes = meta.getPropertyTypes();

    final Object[] propertyValues = meta.getPropertyValues( exampleEntity );
    for ( int i=0; i<propertyNames.length; i++ ) {
      final Object propertyValue = propertyValues[i];
      final String propertyName = propertyNames[i];

      final boolean isVersionProperty = i == meta.getVersionProperty();
      if ( ! isVersionProperty && isPropertyIncluded( propertyValue, propertyName, propertyTypes[i] ) ) {
        if ( propertyTypes[i].isComponentType() ) {
          appendComponentCondition(
            propertyName,
            propertyValue,
            (CompositeType) propertyTypes[i],
            criteria,
            criteriaQuery,
            buf
          );
        }
        else {
          appendPropertyCondition(
            propertyName,
            propertyValue,
            criteria,
            criteriaQuery,
            buf
          );
        }
      }
    }

    if ( buf.length()==1 ) {
      buf.append( "1=1" );
    }

    return buf.append( ')' ).toString();
  }

  @SuppressWarnings("SimplifiableIfStatement")
  private boolean isPropertyIncluded(Object value, String name, Type type) {
    if ( excludedProperties.contains( name ) ) {
      // was explicitly excluded
      return false;
    }

    if ( type.isAssociationType() ) {
      // associations are implicitly excluded
      return false;
    }

    return selector.include( value, name, type );
  }

  @Override
  public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
    final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
        criteriaQuery.getEntityName( criteria )
    );
    final String[] propertyNames = meta.getPropertyNames();
    final Type[] propertyTypes = meta.getPropertyTypes();

    final Object[] values = meta.getPropertyValues( exampleEntity );
    final List<TypedValue> list = new ArrayList<TypedValue>();
    for ( int i=0; i<propertyNames.length; i++ ) {
      final Object value = values[i];
      final Type type = propertyTypes[i];
      final String name = propertyNames[i];

      final boolean isVersionProperty = i == meta.getVersionProperty();

      if ( ! isVersionProperty && isPropertyIncluded( value, name, type ) ) {
        if ( propertyTypes[i].isComponentType() ) {
          addComponentTypedValues( name, value, (CompositeType) type, list, criteria, criteriaQuery );
        }
        else {
          addPropertyTypedValue( value, type, list );
        }
      }
    }

    return list.toArray( new TypedValue[ list.size() ] );
  }

  protected void addPropertyTypedValue(Object value, Type type, List<TypedValue> list) {
    if ( value != null ) {
      if ( value instanceof String ) {
        String string = (String) value;
        if ( isIgnoreCaseEnabled ) {
          string = string.toLowerCase();
        }
        if ( isLikeEnabled ) {
          string = matchMode.toMatchString( string );
        }
        value = string;
      }
      list.add( new TypedValue( type, value ) );
    }
  }

  protected void addComponentTypedValues(
      String path,
      Object component,
      CompositeType type,
      List<TypedValue> list,
      Criteria criteria,
      CriteriaQuery criteriaQuery) {
    if ( component != null ) {
      final String[] propertyNames = type.getPropertyNames();
      final Type[] subtypes = type.getSubtypes();
      final Object[] values = type.getPropertyValues( component, getEntityMode( criteria, criteriaQuery ) );
      for ( int i=0; i<propertyNames.length; i++ ) {
        final Object value = values[i];
        final Type subtype = subtypes[i];
        final String subpath = StringHelper.qualify( path, propertyNames[i] );
        if ( isPropertyIncluded( value, subpath, subtype ) ) {
          if ( subtype.isComponentType() ) {
            addComponentTypedValues( subpath, value, (CompositeType) subtype, list, criteria, criteriaQuery );
          }
          else {
            addPropertyTypedValue( value, subtype, list );
          }
        }
      }
    }
  }

  private EntityMode getEntityMode(Criteria criteria, CriteriaQuery criteriaQuery) {
    final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
        criteriaQuery.getEntityName( criteria )
    );
    final EntityMode result = meta.getEntityMode();
    if ( ! meta.getEntityMetamodel().getTuplizer().isInstance( exampleEntity ) ) {
      throw new ClassCastException( exampleEntity.getClass().getName() );
    }
    return result;
  }

  protected void appendPropertyCondition(
      String propertyName,
      Object propertyValue,
      Criteria criteria,
      CriteriaQuery cq,
      StringBuilder buf) {
    final Criterion condition;
    if ( propertyValue != null ) {
      final boolean isString = propertyValue instanceof String;
      if ( isLikeEnabled && isString ) {
        condition = new LikeExpression(
            propertyName,
            (String) propertyValue,
            matchMode,
            escapeCharacter,
            isIgnoreCaseEnabled
        );
      }
      else {
        condition = new SimpleExpression( propertyName, propertyValue, "=", isIgnoreCaseEnabled && isString );
      }
    }
    else {
      condition = new NullExpression(propertyName);
    }

    final String conditionFragment = condition.toSqlString( criteria, cq );
    if ( conditionFragment.trim().length() > 0 ) {
      if ( buf.length() > 1 ) {
        buf.append( " and " );
      }
      buf.append( conditionFragment );
    }
  }

  protected void appendComponentCondition(
      String path,
      Object component,
      CompositeType type,
      Criteria criteria,
      CriteriaQuery criteriaQuery,
      StringBuilder buf) {
    if ( component != null ) {
      final String[] propertyNames = type.getPropertyNames();
      final Object[] values = type.getPropertyValues( component, getEntityMode( criteria, criteriaQuery ) );
      final Type[] subtypes = type.getSubtypes();
      for ( int i=0; i<propertyNames.length; i++ ) {
        final String subPath = StringHelper.qualify( path, propertyNames[i] );
        final Object value = values[i];
        if ( isPropertyIncluded( value, subPath, subtypes[i] ) ) {
          final Type subtype = subtypes[i];
          if ( subtype.isComponentType() ) {
            appendComponentCondition(
                subPath,
                value,
                (CompositeType) subtype,
                criteria,
                criteriaQuery,
                buf
            );
          }
          else {
            appendPropertyCondition(
                subPath,
                value,
                criteria,
                criteriaQuery,
                buf
            );
          }
        }
      }
    }
  }

  @Override
  public String toString() {
    return "example (" + exampleEntity + ')';
  }


  // PropertySelector definitions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  /**
   * A strategy for choosing property values for inclusion in the query criteria.  Note that
   * property selection (for inclusion) operates separately from excluding a property.  Excluded
   * properties are not even passed in to the PropertySelector for consideration.
   */
  public static interface PropertySelector extends Serializable {
    /**
     * Determine whether the given property should be used in the criteria.
     *
     * @param propertyValue The property value (from the example bean)
     * @param propertyName The name of the property
     * @param type The type of the property
     *
     * @return {@code true} indicates the property should be included; {@code false} indiates it should not.
     */
    public boolean include(Object propertyValue, String propertyName, Type type);
  }

  /**
   * Property selector that includes all properties
   */
  public static final class AllPropertySelector implements PropertySelector {
    /**
     * Singleton access
     */
    public static final AllPropertySelector INSTANCE = new AllPropertySelector();

    @Override
    public boolean include(Object object, String propertyName, Type type) {
      return true;
    }

    private Object readResolve() {
      return INSTANCE;
    }
  }

  /**
   * Property selector that includes only properties that are not {@code null}
   */
  public static final class NotNullPropertySelector implements PropertySelector {
    /**
     * Singleton access
     */
    public static final NotNullPropertySelector INSTANCE = new NotNullPropertySelector();

    @Override
    public boolean include(Object object, String propertyName, Type type) {
      return object!=null;
    }

    private Object readResolve() {
      return INSTANCE;
    }
  }

  /**
   * Property selector that includes only properties that are not {@code null} and non-zero (if numeric)
   */
  public static final class NotNullOrZeroPropertySelector implements PropertySelector {
    /**
     * Singleton access
     */
    public static final NotNullOrZeroPropertySelector INSTANCE = new NotNullOrZeroPropertySelector();

    @Override
    public boolean include(Object object, String propertyName, Type type) {
      return object != null
          && ( !(object instanceof Number) || ( (Number) object ).longValue()!=0
      );
    }

    private Object readResolve() {
      return INSTANCE;
    }
  }
}
TOP

Related Classes of org.hibernate.criterion.Example$PropertySelector

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.
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');