All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.queries.QueryByExamplePolicy Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.queries;

import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;

import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
* 

Purpose: * This policy defines the configuration options for a Query By Example query. * *

Description: * A Query By Example query is an ObjectLevelReadQuery where the * selection criteria is built from an example domain object passed in via setExampleObject. *

* If no policy is specified the selection criteria is built from the example * object in the following way: *

    *
  • Attributes of the example object set to null are ignored. * *
  • Attributes set to the default value for their primitive type (such as * 0 for int) are ignored. * *
  • Unmapped attributes are ignored. * *
  • A domain object is returned by the query only if its values for all the * included attributes equal those set in the example object. *

* A policy can be set on the query to: *

    *
  • Always consider an attribute even if set to null * or the default value for its type. See {@link #alwaysIncludeAttribute alwaysIncludeAttribute}. * *
  • Ignore attributes set to some other special value. See * {@link #excludeValue(Object) excludeValue}. * *
  • Match a null attribute on the example object with domain objects that have * either null for that attribute also, or have set a meaningful (notNull) value * for that attribute. See {@link #setShouldUseEqualityForNulls}. * *
  • Use specialized operations when comparing attributes set in the example object * with those set in the domain objects. Matching attributes can be those with * values greater than, less than, like, or not equal to that set in the example * object. See {@link #addSpecialOperation}. *
*

* Note: When setting an attribute on the example object which is itself a java * object with an ObjectReferenceMapping, the mapped components of that * attribute will be considered, not the entire object. There is no limit to * how many mapped objects can be nested inside the example object. *

* Note: setExampleObject is different from setSelectionObject in * ReadObjectQuery which reads a single object by first extracting * the primary key from the selection object. *

* Restrictions: *

    *
  • Only attributes whose mappings are DirectToField, Aggregate (Embeddable), ObjectReference * (OneToOne) or Collection type OneToMany/ManyToMany are considered in a Query By Example object. The behaviour when an example object has attribute values for other mappings types is undefined. *
    • To ensure the example does not include any unsupported mappings the flag {@link #setValidateExample} * should be set to true on the corresponding QueryByExamplePolicy to ensure no unsupported relationship types are used in the example.
    • *
    • For OneToMany and ManyToMany mappings the elements within the collections and the references attribute values will be added to the expression as disjuncts (OR)
    • *
    *
  • *
*

* Example: *

* // This example uses like for Strings and the salary must be greater
* // than zero.
* ReadAllQuery query = new ReadAllQuery();
* Employee employee = new Employee();
* employee.setFirstName("B%");
* employee.setLastName("S%");
* employee.setSalary(0);
* query.setExampleObject(employee);
* QueryByExamplePolicy policy = new QueryByExamplePolicy();
* policy.addSpecialOperation(String.class, "like");
* policy.addSpecialOperation(Integer.class, "greaterThan");
* policy.alwaysIncludeAttribute(Employee.class, "salary");
* query.setQueryByExamplePolicy(policy);
* Vector results = (Vector) session.executeQuery(query);
* 
* @see ObjectLevelReadQuery#setExampleObject * @see ObjectLevelReadQuery#setQueryByExamplePolicy * * @since TOPLink/Java 3.0 */ public class QueryByExamplePolicy implements java.io.Serializable { //CR3400 Make Serializable public Map valuesToExclude = new HashMap(); public Map attributesToAlwaysInclude = new HashMap(); public Map specialOperations = new HashMap(); public boolean shouldUseEqualityForNulls; protected boolean validateExample; /** * PUBLIC: * Constructs a default policy equal to that used when no policy is specified. *

* Sets the default values to be excluded, * (that includes 0, false, empty String, etc).

* By default if an attribute is null, and yet has to be included at all times, equality (isNull) * is used for the comparison. This is used for searching for an object with a null in a certain field. * @see #excludeDefaultPrimitiveValues * @see #setShouldUseEqualityForNulls setShouldUseEqualityForNulls(true) */ public QueryByExamplePolicy() { this.valuesToExclude = new HashMap(10); this.attributesToAlwaysInclude = new HashMap(5); this.specialOperations = new HashMap(5); this.shouldUseEqualityForNulls = true; this.excludeDefaultPrimitiveValues(); } /** * PUBLIC: * Allows operations other than Expression.equal to be used * for comparisons. *

* For example if an attribute of type int is * set to x in the example object, normally the query will be on all objects * whose attributes are also equal to x. The query could however be all * objects whose attributes are not x, greater than x, or even less than or * equal to x. *

* Any comparison operation in {@link org.eclipse.persistence.expressions.Expression Expression} which takes the example attribute as a parameter * can be used. A list of supported operations is provided below. *

* Note: A special operation can not be used for attributes set to null. The only * options are {@link org.eclipse.persistence.expressions.Expression#isNull() isNull} (default) and * {@link org.eclipse.persistence.expressions.Expression#notNull() notNull}. See * {@link #setShouldUseEqualityForNulls}. * @param attributeValueClass Attribute values of which type, for instance * Integer, to apply to. Note for int attributes the * class is Integer.class not int.class. This is not * the Class of the example object the attribute is an instance variable of. * @param operation Name of method in Expression used * @see org.eclipse.persistence.expressions.Expression#equal equal (default) * @see org.eclipse.persistence.expressions.Expression#notEqual notEqual * @see org.eclipse.persistence.expressions.Expression#equalsIgnoreCase equalsIgnoreCase * @see org.eclipse.persistence.expressions.Expression#lessThan lessThan * @see org.eclipse.persistence.expressions.Expression#lessThanEqual lessThanEqual * @see org.eclipse.persistence.expressions.Expression#greaterThan greaterThan * @see org.eclipse.persistence.expressions.Expression#greaterThanEqual greaterThanEqual * @see org.eclipse.persistence.expressions.Expression#like like * @see org.eclipse.persistence.expressions.Expression#likeIgnoreCase likeIgnoreCase * @see org.eclipse.persistence.expressions.Expression#containsAllKeyWords containsAllKeyWords * @see org.eclipse.persistence.expressions.Expression#containsAnyKeyWords containsAnyKeyWords * @see org.eclipse.persistence.expressions.Expression#containsSubstring(java.lang.String) containsSubstring * @see org.eclipse.persistence.expressions.Expression#containsSubstringIgnoringCase(java.lang.String) containsSubstringIgnoringCase */ public void addSpecialOperation(Class attributeValueClass, String operation) { this.getSpecialOperations().put(attributeValueClass, operation); } /** * PUBLIC: * Always considers the value for a particular attribute as meaningful in a * query by example. *

* Required to override the normal behavior which is to ignore an * attribute of the example object if set to null, or an excluded value * like 0. *

* Example: To find all projects without a budget set budget to 0 in the * example object and call alwaysIncludeAttribute(Project.class, "budget") * on the policy. * * @param exampleClass The class that the attribute belongs to, normally this is the example class unless using nested QBE. * @param attributeName The name of a mapped attribute. */ public void alwaysIncludeAttribute(Class exampleClass, String attributeName) { Vector included = (Vector)getAttributesToAlwaysInclude().get(exampleClass); if (included == null) { included = new Vector(3); } included.add(attributeName); getAttributesToAlwaysInclude().put(exampleClass, included); } /** * INTERNAL: * This method is used to determine which operation to use for comparison (equal, or a special operation). */ public Expression completeExpression(Expression expression, Object attributeValue, Class attributeValueClass) { String operation = this.getOperation(attributeValue.getClass()); if (operation == null) { //it means no special operation used. Use equal. return expression.equal(attributeValue); } Class[] argTypes = { attributeValueClass }; Object[] args = { attributeValue }; try { java.lang.reflect.Method anOperator = Helper.getDeclaredMethod(ClassConstants.Expression_Class, operation, argTypes); if (anOperator == null) { throw QueryException.methodDoesNotExistOnExpression(operation, argTypes); } if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try{ expression = (Expression)AccessController.doPrivileged(new PrivilegedMethodInvoker(anOperator, expression, args)); }catch (PrivilegedActionException ex){ throw (RuntimeException) ex.getCause(); } }else{ expression = PrivilegedAccessHelper.invokeMethod(anOperator, expression, args); } } catch (NoSuchMethodException nsme) { Class superClass = attributeValueClass.getSuperclass(); if (superClass != null) { return completeExpression(expression, attributeValue, superClass); } else { throw QueryException.methodDoesNotExistOnExpression(operation, argTypes); } } catch (IllegalAccessException | InvocationTargetException iae) { throw QueryException.methodDoesNotExistOnExpression(operation, argTypes); } return expression; } /** * INTERNAL: * This method is used when the attribute value is null, but it has * to be included at all times. It determines whether to use isNull, or notNull. */ public Expression completeExpressionForNull(Expression expression) { if (shouldUseEqualityForNulls()) { return expression.isNull(); } else { return expression.notNull(); } } /** * PUBLIC: * Ignores attributes set to the default value for their primitive type. *

* For instance 0 is used as null for deciding * which int attributes of the example object can be ignored in a * query by example. *

* Called by the constructor. */ public void excludeDefaultPrimitiveValues() { excludeValue(0); excludeValue(0.0); excludeValue(false); excludeValue((short)0); excludeValue('\u0000'); excludeValue((long)0); excludeValue((byte)0); excludeValue(0.0f); excludeValue(""); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for byte is 0. */ public void excludeValue(byte value) { excludeValue(Byte.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for char is '\u0000'. */ public void excludeValue(char value) { excludeValue(Character.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for double is 0.0. */ public void excludeValue(double value) { excludeValue(Double.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for float is 0.0f. */ public void excludeValue(float value) { excludeValue(Float.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to be an excluded value will be * ignored in a Query By Example.

* The default excluded value for int is 0. */ public void excludeValue(int value) { excludeValue(Integer.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for long is 0. */ public void excludeValue(long value) { excludeValue(Long.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for String is "".

* Note: null is special and always considered an excluded value. */ public void excludeValue(Object value) { this.valuesToExclude.put(value, value); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for short is 0. */ public void excludeValue(short value) { excludeValue(Short.valueOf(value)); } /** * PUBLIC: * An attribute in the example object set to an excluded value will be * ignored in a Query By Example.

* The default excluded value for boolean is false. */ public void excludeValue(boolean value) { excludeValue(Boolean.valueOf(value)); } /** * PUBLIC: * Attributes to always consider even if set to null or an excluded * value like 0 or false. * @see #alwaysIncludeAttribute */ public Map getAttributesToAlwaysInclude() { return attributesToAlwaysInclude; } /** * INTERNAL: * determines which operation to use for comparison. */ public String getOperation(Class aClass) { String operation = (String)this.getSpecialOperations().get(aClass); if (operation != null) { if (!operation.equals("equal")) { return operation; } } return null; } /** * PUBLIC: * The special operations to use in place of equal. * @return A hashtable where the keys are Class objects and the values * are the names of operations to use for attributes of that Class. * @see #addSpecialOperation */ public Map getSpecialOperations() { return specialOperations; } /** * PUBLIC: * Decides which attributes to ignore based on the values they are set to. *

* If an attribute of the example domain object is set to one of these values it will * be ignored, and not considered in the query. *

* Attributes set to excluded values are not always ignored. * See {@link #alwaysIncludeAttribute alwaysIncludeAttribute}. * @return valuesToExclude The keys and values are values to exclude (key == value). Primitives are * wrapped, so int 0 will be stored as Integer(0). * @see #excludeValue * @see #excludeDefaultPrimitiveValues * @see #includeAllValues */ public Map getValuesToExclude() { return valuesToExclude; } /** * PUBLIC: * Considers all mapped attributes in the example object as meaningful in a * Query By Example.

* Note: Even attributes of the example object that are * not set, and therefore zero or empty by default, will be included.

* Reverses a previous call to {@link #excludeDefaultPrimitiveValues}. */ public void includeAllValues() { setValuesToExclude(new HashMap(5)); } /** * INTERNAL: * returns whether the attributeName is to be always included. */ public boolean isAlwaysIncluded(Class theClass, String attributeName) { Vector values = (Vector)this.getAttributesToAlwaysInclude().get(theClass); if (values != null) { return (values.contains(attributeName)); } return false; } /** * INTERNAL: * returns if the value is in the values to be excluded automatically. */ public boolean isExcludedValue(Object value) { return this.getValuesToExclude().containsKey(value); } /** * PUBLIC: * Considers all attributes set to a previously excluded value on the example object. *

* Primitive values to be removed must first be wrapped inside an Object. * * @param value No attributes set to value will be excluded from a Query By Example. * value.getClass() is a key of the Hashtable returned by {@link #getValuesToExclude}. *

Note: There is a distinction between an attribute and the value * it is set to. An attribute can be included independently of its value with * {@link #alwaysIncludeAttribute alwaysIncludeAttribute} (recommended). It can also be included * by making the value it is set to no longer excluded. *

Note: null values are special and will always be excluded. * @see #excludeDefaultPrimitiveValues * @see #includeAllValues * @see #excludeValue(Object) */ public void removeFromValuesToExclude(Object value) { getValuesToExclude().remove(value); } /** * INTERNAL: * It is possible to generate a Hashtable (keys are the Class, and values the attribute names) * of the attributes to be included at all times (even if the value is null, or the value * belongs to the values to be excluced automatically). */ public void setAttributesToAlwaysInclude(Map newAttributesToAlwaysInclude) { attributesToAlwaysInclude = newAttributesToAlwaysInclude; } /** * PUBLIC: * Matches an included null attribute in the example object * either to objects with that attribute also set to null or to any * value other than null. *

* Set to false to only select objects where certain attributes have been set. *

* Example: to find all Employees with an assigned address, set * attribute address to null in the example object, * call alwaysIncludeAttribute(Employee.class, "address") and then * call setShouldUseEqualityForNulls(false). *

* Note: Unless an attribute set to null is specifically included, it * will not be considered at all in the Query By Example. * @param shouldUseEqualityForNulls If true (by default) uses isNull else notNull. * @see #addSpecialOperation addSpecialOperation * @see #alwaysIncludeAttribute alwaysIncludeAttribute */ public void setShouldUseEqualityForNulls(boolean shouldUseEqualityForNulls) { this.shouldUseEqualityForNulls = shouldUseEqualityForNulls; } /** * PUBLIC: * The special operations to use in place of equal. * @param newOperations A hashtable where the keys are Class objects and the values * are the names of operations to use for attributes of that Class. * @see #addSpecialOperation */ public void setSpecialOperations(Map newOperations) { specialOperations = newOperations; } /** * PUBLIC: * When set to true the example object will be validated for unsupported mapping types. * If you wish these mapping types to be ignored either set this flag to false or add the attribute * to the list of ignored attributes in this policy */ public void setValidateExample(boolean validate){ this.validateExample = validate; } /** * PUBLIC: * Decides which attributes to ignore based on the values they are set to. *

* An attribute of the example domain object set to one of these values will * be ignored, and not considered in the query. *

* Attributes set to excluded values are not always ignored. * See {@link #alwaysIncludeAttribute alwaysIncludeAttribute}. * @param newValuesToExclude The keys and values are values to exclude (key == value). Primitives are * wrapped, so int 0 will be stored as Integer(0). * @see #excludeValue * @see #excludeDefaultPrimitiveValues * @see #includeAllValues */ public void setValuesToExclude(Map newValuesToExclude) { valuesToExclude = newValuesToExclude; } /** * INTERNAL: * This method determines whether an attribute pair is be included in the query. */ public boolean shouldIncludeInQuery(Class aClass, String attributeName, Object attributeValue) { if (attributeValue == null) { if (this.isAlwaysIncluded(aClass, attributeName)) { //this attribute is to be included always, even if its value is null. return true; } else { return false; } } if (this.isExcludedValue(attributeValue)) { if (this.isAlwaysIncluded(aClass, attributeName)) { //this attribute is to be included always, even if its value belongs to the list of values to be excluded. return true; } else { return false; } } return true; } /** * PUBLIC: * Matches an included null attribute in the example object * either to objects with that attribute also set to null or to any * value other than null. *

* Set to false to only select objects where certain attributes have been set. *

* Example: to find all Employees with an assigned address, set * attribute address to null in the example object, * call alwaysIncludeAttribute(Employee.class, "address") and then * call setShouldUseEqualityForNulls(false). * * @return If true (by default) uses isNull else notNull. * @see #addSpecialOperation addSpecialOperation * @see #alwaysIncludeAttribute alwaysIncludeAttribute */ public boolean shouldUseEqualityForNulls() { return shouldUseEqualityForNulls; } /** * PUBLIC: * Returns true if the example object used with this policy should be validated for attributes * with unsupported mappings. */ public boolean shouldValidateExample(){ return this.validateExample; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy