| /* |
| * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2021 IBM Corporation. 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: |
| // Gordon Yorke - Initial development |
| // 10/01/2018: Will Dazey |
| // - #253: Add support for embedded constructor results with CriteriaBuilder |
| package org.eclipse.persistence.internal.jpa.querydef; |
| |
| import java.lang.reflect.Constructor; |
| import java.security.AccessController; |
| import java.util.*; |
| |
| import jakarta.persistence.Tuple; |
| import jakarta.persistence.criteria.CriteriaQuery; |
| import jakarta.persistence.criteria.Expression; |
| import jakarta.persistence.criteria.Order; |
| import jakarta.persistence.criteria.Predicate; |
| import jakarta.persistence.criteria.Root; |
| import jakarta.persistence.criteria.Selection; |
| import jakarta.persistence.metamodel.Metamodel; |
| import jakarta.persistence.metamodel.Type.PersistenceType; |
| |
| import org.eclipse.persistence.expressions.ExpressionBuilder; |
| import org.eclipse.persistence.internal.helper.BasicTypeHelperImpl; |
| import org.eclipse.persistence.internal.helper.ClassConstants; |
| import org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl; |
| import org.eclipse.persistence.internal.jpa.metamodel.TypeImpl; |
| import org.eclipse.persistence.internal.localization.ExceptionLocalization; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| import org.eclipse.persistence.queries.ObjectLevelReadQuery; |
| import org.eclipse.persistence.queries.ReadAllQuery; |
| import org.eclipse.persistence.queries.ReportQuery; |
| |
| /** |
| * <p> |
| * <b>Purpose</b>: Contains the implementation of the CriteriaQuery interface of |
| * the JPA criteria API. |
| * <p> |
| * <b>Description</b>: This is the container class for the components that |
| * define a query. |
| * <p> |
| * |
| * @see jakarta.persistence.criteria CriteriaQuery |
| * |
| * @author gyorke |
| * @since EclipseLink 1.2 |
| */ |
| public class CriteriaQueryImpl<T> extends AbstractQueryImpl<T> implements CriteriaQuery<T> { |
| |
| protected SelectionImpl<?> selection; |
| protected List<Order> orderBy; |
| |
| protected Set<FromImpl> joins; |
| |
| public CriteriaQueryImpl(Metamodel metamodel, ResultType queryResult, Class result, CriteriaBuilderImpl queryBuilder) { |
| super(metamodel, queryResult, queryBuilder, result); |
| } |
| |
| /** |
| * Specify the item that is to be returned in the query result. Replaces the |
| * previously specified selection, if any. |
| * |
| * @param selection |
| * selection specifying the item that is to be returned in the |
| * query result |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> select(Selection<? extends T> selection) { |
| this.selection = (SelectionImpl<? extends T>) selection; |
| this.selection.findRootAndParameters(this); |
| if (selection.isCompoundSelection()) { |
| //bug 366386: validate that aliases are not reused |
| if (this.selection.isCompoundSelection() && ((CompoundSelectionImpl)this.selection).getDuplicateAliasNames() != null) { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa_criteriaapi_alias_reused", |
| new Object[] { ((CompoundSelectionImpl)this.selection).getDuplicateAliasNames() })); |
| } |
| if (selection.getJavaType().equals(Tuple.class)) { |
| this.queryResult = ResultType.TUPLE; |
| this.queryType = Tuple.class; |
| } else if (((InternalSelection) selection).isConstructor()) { |
| Selection[] selectArray = selection.getCompoundSelectionItems().toArray(new Selection[selection.getCompoundSelectionItems().size()]); |
| populateAndSetConstructorSelection((ConstructorSelectionImpl)selection, this.selection.getJavaType(), selectArray); |
| this.queryType = selection.getJavaType(); |
| } else { |
| this.queryResult = ResultType.OBJECT_ARRAY; |
| this.queryType = ClassConstants.AOBJECT; |
| } |
| } else { |
| // Update query type only when it's not null in selection argument. |
| Class queryType = selection.getJavaType(); |
| if (queryType != null) { |
| this.queryType = queryType; |
| } |
| TypeImpl type = ((MetamodelImpl)this.metamodel).getType(this.queryType); |
| if (type != null && type.getPersistenceType().equals(PersistenceType.ENTITY)) { |
| this.queryResult = ResultType.ENTITY; // this will be a selection item in a report query |
| } else { |
| this.queryResult = ResultType.OTHER; |
| } |
| } |
| return this; |
| } |
| |
| /** |
| * Specify the items that are to be returned in the query result. Replaces |
| * the previously specified selection(s), if any. |
| * |
| * The type of the result of the query execution depends on the |
| * specification of the criteria query object as well as the arguments to |
| * the multiselect method as follows: |
| * |
| * If the type of the criteria query is CriteriaQuery<Tuple>, a Tuple object |
| * corresponding to the arguments of the multiselect method will be |
| * instantiated and returned for each row that results from the query |
| * execution. |
| * |
| * If the type of the criteria query is CriteriaQuery<X> for some |
| * user-defined class X, then the arguments to the multiselect method will |
| * be passed to the X constructor and an instance of type X will be returned |
| * for each row. The IllegalStateException will be thrown if a constructor |
| * for the given argument types does not exist. |
| * |
| * If the type of the criteria query is CriteriaQuery<X[]> for some class X, |
| * an instance of type X[] will be returned for each row. The elements of |
| * the array will correspond to the arguments of the multiselect method. The |
| * IllegalStateException will be thrown if the arguments to the multiselect |
| * method are not of type X. |
| * |
| * If the type of the criteria query is CriteriaQuery<Object>, and only a |
| * single argument is passed to the multiselect method, an instance of type |
| * Object will be returned for each row. |
| * |
| * If the type of the criteria query is CriteriaQuery<Object>, and more than |
| * one argument is passed to the multiselect method, an instance of type |
| * Object[] will be instantiated and returned for each row. The elements of |
| * the array will correspond to the arguments to the multiselect method. |
| * |
| * @param selections |
| * expressions specifying the items that are to be returned in |
| * the query result |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> multiselect(Selection<?>... selections) { |
| if (selections == null || selections.length == 0) { |
| this.selection = null; |
| return this; |
| } |
| for (Selection select : selections) { |
| ((SelectionImpl)select).findRootAndParameters(this); |
| } |
| if (this.queryResult == ResultType.CONSTRUCTOR) { |
| populateAndSetConstructorSelection(null, this.queryType, selections); |
| } else if (this.queryResult.equals(ResultType.ENTITY)) { |
| if (selections.length == 1 && selections[0].getJavaType().equals(this.queryType)) { |
| this.selection = (SelectionImpl<?>) selections[0]; |
| } else { |
| try { |
| populateAndSetConstructorSelection(null, this.queryType, selections);//throws IllegalArgumentException if it doesn't exist |
| } catch(IllegalArgumentException constructorDoesNotExist){ |
| this.queryResult = ResultType.PARTIAL; |
| this.selection = new CompoundSelectionImpl(this.queryType, selections); |
| } |
| } |
| } else if (this.queryResult.equals(ResultType.TUPLE)) { |
| this.selection = new CompoundSelectionImpl(this.queryType, selections); |
| } else if (this.queryResult.equals(ResultType.OTHER)) { |
| if (selections.length == 1 && selections[0].getJavaType().equals(this.queryType)) { |
| this.selection = (SelectionImpl<?>) selections[0]; |
| } else { |
| if (!BasicTypeHelperImpl.getInstance().isDateClass(this.queryType)) { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("MULTIPLE_SELECTIONS_PASSED_TO_QUERY_WITH_PRIMITIVE_RESULT")); |
| } |
| populateAndSetConstructorSelection(null, this.queryType, selections); |
| } |
| } else { // unknown |
| this.selection = new CompoundSelectionImpl(this.queryType, selections); |
| } |
| //bug 366386: validate that aliases are not reused |
| if (this.selection.isCompoundSelection() && ((CompoundSelectionImpl)this.selection).getDuplicateAliasNames() != null) { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa_criteriaapi_alias_reused", |
| new Object[] { ((CompoundSelectionImpl)this.selection).getDuplicateAliasNames() })); |
| } |
| return this; |
| } |
| |
| /** |
| * Specify the items that are to be returned in the query result. Replaces |
| * the previously specified selection(s), if any. |
| * |
| * The type of the result of the query execution depends on the |
| * specification of the criteria query object as well as the arguments to |
| * the multiselect method as follows: |
| * |
| * If the type of the criteria query is CriteriaQuery<Tuple>, a Tuple object |
| * corresponding to the items in the selection list passed to the |
| * multiselect method will be instantiated and returned for each row that |
| * results from the query execution. |
| * |
| * If the type of the criteria query is CriteriaQuery<X> for some |
| * user-defined class X, then the items in the selection list passed to the |
| * multiselect method will be passed to the X constructor and an instance of |
| * type X will be returned for each row. The IllegalStateException will be |
| * thrown if a constructor for the given argument types does not exist. |
| * |
| * If the type of the criteria query is CriteriaQuery<X[]> for some class X, |
| * an instance of type X[] will be returned for each row. The elements of |
| * the array will correspond to the items in the selection list passed to |
| * the multiselect method. The IllegalStateException will be thrown if the |
| * elements in the selection list passed to the multiselect method are not |
| * of type X. |
| * |
| * If the type of the criteria query is CriteriaQuery<Object>, and the |
| * selection list passed to the multiselect method contains only a single |
| * item, an instance of type Object will be returned for each row. |
| * |
| * If the type of the criteria query is CriteriaQuery<Object>, and the |
| * selection list passed to the multiselect method contains more than one |
| * item, an instance of type Object[] will be instantiated and returned for |
| * each row. The elements of the array will correspond to the items in the |
| * selection list passed to the multiselect method. |
| * |
| * @param selectionList |
| * list of expressions specifying the items that to be are |
| * returned in the query result |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> multiselect(List<Selection<?>> selectionList) { |
| if (selectionList == null) { |
| this.selection = null; |
| return this; |
| } |
| return this.multiselect(selectionList.toArray(new Selection[selectionList.size()])); |
| } |
| |
| // override the return type only: |
| /** |
| * Modify the query to restrict the query result according to the specified |
| * boolean expression. Replaces the previously added restriction(s), if any. |
| * This method only overrides the return type of the corresponding |
| * AbstractQuery method. |
| * |
| * @param restriction |
| * a simple or compound boolean expression |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> where(Expression<Boolean> restriction) { |
| return (CriteriaQuery<T>) super.where(restriction); |
| } |
| |
| /** |
| * Modify the query to restrict the query result according to the |
| * conjunction of the specified restriction predicates. Replaces the |
| * previously added restriction(s), if any. If no restrictions are |
| * specified, any previously added restrictions are simply removed. This |
| * method only overrides the return type of the corresponding AbstractQuery |
| * method. |
| * |
| * @param restrictions |
| * zero or more restriction predicates |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> where(Predicate... restrictions) { |
| return (CriteriaQuery<T>) super.where(restrictions); |
| } |
| |
| /** |
| * Specify the expressions that are used to form groups over the query |
| * results. Replaces the previous specified grouping expressions, if any. If |
| * no grouping expressions are specified, any previously added grouping |
| * expressions are simply removed. This method only overrides the return |
| * type of the corresponding AbstractQuery method. |
| * |
| * @param grouping |
| * zero or more grouping expressions |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> groupBy(Expression<?>... grouping) { |
| super.groupBy(grouping); |
| return this; |
| } |
| |
| /** |
| * Specify the expressions that are used to form groups over the query |
| * results. Replaces the previous specified grouping expressions, if any. If |
| * no grouping expressions are specified, any previously added grouping |
| * expressions are simply removed. This method only overrides the return |
| * type of the corresponding AbstractQuery method. |
| * |
| * @param grouping |
| * list of zero or more grouping expressions |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> groupBy(List<Expression<?>> grouping) { |
| super.groupBy(grouping); |
| return this; |
| } |
| |
| /** |
| * Specify a restriction over the groups of the query. Replaces the previous |
| * having restriction(s), if any. This method only overrides the return type |
| * of the corresponding AbstractQuery method. |
| * |
| * @param restriction |
| * a simple or compound boolean expression |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> having(Expression<Boolean> restriction) { |
| super.having(restriction); |
| return this; |
| } |
| |
| /** |
| * Specify restrictions over the groups of the query according the |
| * conjunction of the specified restriction predicates. Replaces the |
| * previously added restriction(s), if any. If no restrictions are |
| * specified, any previously added restrictions are simply removed. This |
| * method only overrides the return type of the corresponding AbstractQuery |
| * method. |
| * |
| * @param restrictions |
| * zero or more restriction predicates |
| * @return the modified query |
| */ |
| @Override |
| public CriteriaQuery<T> having(Predicate... restrictions) { |
| super.having(restrictions); |
| return this; |
| } |
| |
| /** |
| * Specify the ordering expressions that are used to order the query |
| * results. Replaces the previous ordering expressions, if any. If no |
| * ordering expressions are specified, the previous ordering, if any, is |
| * simply removed, and results will be returned in no particular order. The |
| * left-to-right sequence of the ordering expressions determines the |
| * precedence, whereby the leftmost has highest precedence. |
| * |
| * @param o |
| * zero or more ordering expressions |
| * @return the modified query. |
| */ |
| @Override |
| public CriteriaQuery<T> orderBy(Order... o) { |
| this.orderBy = new ArrayList(); |
| for (Order order : o) { |
| this.orderBy.add(order); |
| } |
| return this; |
| } |
| |
| /** |
| * Specify the ordering expressions that are used to order the query |
| * results. Replaces the previous ordering expressions, if any. If no |
| * ordering expressions are specified, the previous ordering, if any, is |
| * simply removed, and results will be returned in no particular order. The |
| * order of the ordering expressions in the list determines the precedence, |
| * whereby the first element in the list has highest precedence. |
| * |
| * @param o |
| * list of zero or more ordering expressions |
| * @return the modified query. |
| */ |
| @Override |
| public CriteriaQuery<T> orderBy(List<Order> o) { |
| this.orderBy = o; |
| return this; |
| } |
| |
| /** |
| * This method will set this queryImpl's selection to a ConstructorSelectionImpl, creating a new |
| * instance or populating the one passed in as necessary. |
| * Throws IllegalArgumentException if a constructor taking arguments represented |
| * by the selections array doesn't exist for the given class. |
| * |
| * Also sets the query result to ResultType.CONSTRUCTOR |
| * |
| * @param class1 |
| * @param selections |
| * @throws IllegalArgumentException |
| */ |
| public void populateAndSetConstructorSelection(ConstructorSelectionImpl constructorSelection, Class<?> class1, Selection<?>... selections) throws IllegalArgumentException{ |
| Class[] constructorArgs = new Class[selections.length]; |
| int count = 0; |
| for (Selection select : selections) { |
| if(select instanceof ConstructorSelectionImpl) { |
| ConstructorSelectionImpl constructorSelect = (ConstructorSelectionImpl)select; |
| Selection[] selectArray = constructorSelect.getCompoundSelectionItems().toArray(new Selection[constructorSelect.getCompoundSelectionItems().size()]); |
| populateAndSetConstructorSelection(constructorSelect, constructorSelect.getJavaType(), selectArray); |
| } |
| constructorArgs[count++] = select.getJavaType(); |
| } |
| Constructor constructor = null; |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| constructor = AccessController.doPrivileged(new PrivilegedGetConstructorFor(class1, constructorArgs, false)); |
| } else { |
| constructor = PrivilegedAccessHelper.getConstructorFor(class1, constructorArgs, false); |
| } |
| if (constructorSelection == null){ |
| constructorSelection = new ConstructorSelectionImpl(class1, selections); |
| } |
| this.queryResult = ResultType.CONSTRUCTOR; |
| constructorSelection.setConstructor(constructor); |
| constructorSelection.setConstructorArgTypes(constructorArgs); |
| this.selection = constructorSelection; |
| } catch (Exception e){ |
| //PrivilegedActionException and NoSuchMethodException are possible |
| Object[] params = new Object[1]; |
| params[0] = this.queryType; |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("criteria_no_constructor_found", params), e); |
| } |
| } |
| |
| /** |
| * Specify whether duplicate query results will be eliminated. A true value |
| * will cause duplicates to be eliminated. A false value will cause |
| * duplicates to be retained. If distinct has not been specified, duplicate |
| * results must be retained. This method only overrides the return type of |
| * the corresponding AbstractQuery method. |
| * |
| * @param distinct |
| * boolean value specifying whether duplicate results must be |
| * eliminated from the query result or whether they must be |
| * retained |
| * @return the modified query. |
| */ |
| @Override |
| public CriteriaQuery<T> distinct(boolean distinct) { |
| super.distinct(distinct); |
| return this; |
| } |
| |
| @Override |
| public void addJoin(FromImpl from) { |
| if (this.joins == null) { |
| this.joins = new LinkedHashSet<FromImpl>(); |
| } |
| this.joins.add(from); |
| } |
| |
| @Override |
| protected DatabaseQuery getDatabaseQuery() { |
| ObjectLevelReadQuery query = null; |
| if (this.selection == null || !this.selection.isCompoundSelection()) { |
| query = createSimpleQuery(); |
| } else { |
| query = createCompoundQuery(); |
| } |
| return query; |
| } |
| |
| /** |
| * Return the ordering expressions in order of precedence. |
| * |
| * @return the list of ordering expressions |
| */ |
| @Override |
| public List<Order> getOrderList() { |
| return this.orderBy; |
| } |
| |
| /** |
| * Return the selection item of the query. This will correspond to the query |
| * type. |
| * |
| * @return the selection item of the query |
| */ |
| @Override |
| public Selection<T> getSelection() { |
| return (Selection<T>) this.selection; |
| } |
| |
| /** |
| * Translates from the criteria query to a EclipseLink Database Query. |
| */ |
| @SuppressWarnings("deprecation") |
| protected ObjectLevelReadQuery createCompoundQuery() { |
| ObjectLevelReadQuery query = null; |
| if (this.queryResult == ResultType.UNKNOWN) { |
| if (this.selection.isConstructor()) { |
| this.queryResult = ResultType.CONSTRUCTOR; |
| } else if (this.selection.getJavaType().equals(Tuple.class)) { |
| this.queryResult = ResultType.TUPLE; |
| } else { |
| this.queryResult = ResultType.OBJECT_ARRAY; |
| } |
| } |
| |
| if (this.queryResult.equals(ResultType.PARTIAL)) { |
| ReadAllQuery raq = new ReadAllQuery(this.queryType); |
| for (Selection selection : this.selection.getCompoundSelectionItems()) { |
| raq.addPartialAttribute(((SelectionImpl) selection).currentNode); |
| } |
| raq.setExpressionBuilder(((InternalSelection) this.selection.getCompoundSelectionItems().get(0)).getCurrentNode().getBuilder()); |
| query = raq; |
| } else { |
| ReportQuery reportQuery = null; |
| if (this.queryResult.equals(ResultType.CONSTRUCTOR) || this.queryResult.equals(ResultType.OTHER)) { |
| // other is also a constructor type if multi-select was called. |
| // with a type other than the query type. |
| reportQuery = new ReportQuery(); |
| reportQuery.addConstructorReportItem(((ConstructorSelectionImpl) this.selection).translate()); |
| reportQuery.setShouldReturnSingleAttribute(true); |
| } else { |
| if (this.queryResult.equals(ResultType.TUPLE)) { |
| reportQuery = new TupleQuery(this.selection == null ? new ArrayList() : this.selection.getCompoundSelectionItems()); |
| } else { |
| reportQuery = new ReportQuery(); |
| reportQuery.setShouldReturnWithoutReportQueryResult(true); |
| } |
| reportQuery.setExpressionBuilder(((InternalSelection) this.selection.getCompoundSelectionItems().get(0)).getCurrentNode().getBuilder()); |
| for (Selection nested : this.selection.getCompoundSelectionItems()) { |
| if (((SelectionImpl) nested).isConstructor()) { |
| reportQuery.addConstructorReportItem(((ConstructorSelectionImpl) nested).translate()); |
| } else if (nested.isCompoundSelection()) { |
| throw new IllegalStateException(ExceptionLocalization.buildMessage("NESTED_COMPOUND_SELECTION_OTHER_THAN_CONSTRUCTOR_NOT_SUPPORTED")); |
| } else { |
| if (((InternalSelection) nested).isFrom()) { |
| reportQuery.addItem(nested.getAlias(), ((SelectionImpl) nested).getCurrentNode(), ((FromImpl) nested).findJoinFetches()); |
| } else if (((InternalExpression) nested).isCompoundExpression() && ((FunctionExpressionImpl) nested).getOperation() == CriteriaBuilderImpl.SIZE) { |
| //selecting size not all databases support subselect in select clause so convert to count/groupby |
| PathImpl collectionExpression = (PathImpl) ((FunctionExpressionImpl) nested).getChildExpressions().get(0); |
| ExpressionImpl fromExpression = (ExpressionImpl) collectionExpression.getParentPath(); |
| reportQuery.addAttribute(nested.getAlias(), collectionExpression.getCurrentNode().count(), ClassConstants.INTEGER); |
| reportQuery.addGrouping(fromExpression.getCurrentNode()); |
| } else { |
| reportQuery.addAttribute(nested.getAlias(), ((SelectionImpl) nested).getCurrentNode(), nested.getJavaType()); |
| } |
| } |
| } |
| } |
| |
| ExpressionBuilder builder = null; |
| Class<?> queryClazz = null; |
| // First check the WHERE clause |
| if(this.where != null && ((InternalSelection) this.where).getCurrentNode() != null) { |
| builder = ((InternalSelection) this.where).getCurrentNode().getBuilder(); |
| queryClazz = builder.getQueryClass(); |
| } |
| // Check all the SELECTION items next |
| if(queryClazz == null && this.selection != null) { |
| for(Selection<?> s : this.selection.getCompoundSelectionItems()) { |
| if(((InternalSelection) s).getCurrentNode() != null ) { |
| builder = ((InternalSelection) s).getCurrentNode().getBuilder(); |
| queryClazz = builder.getQueryClass(); |
| if(queryClazz != null) { |
| break; |
| } |
| } |
| } |
| } |
| // Fallback on the root |
| if(queryClazz == null && this.roots != null) { |
| for(Root<?> r : this.roots) { |
| if(((RootImpl<?>) r).getCurrentNode() != null ) { |
| builder = ((RootImpl<?>) r).getCurrentNode().getBuilder(); |
| queryClazz = builder.getQueryClass(); |
| if(queryClazz != null) { |
| break; |
| } |
| } |
| } |
| } |
| reportQuery.setExpressionBuilder(builder); |
| reportQuery.setReferenceClass(queryClazz); |
| |
| query = reportQuery; |
| if (this.groupBy != null && !this.groupBy.isEmpty()) { |
| for (Expression<?> exp : this.groupBy) { |
| reportQuery.addGrouping(((InternalSelection) exp).getCurrentNode()); |
| } |
| } |
| if (this.havingClause != null) { |
| reportQuery.setHavingExpression(((InternalSelection) this.havingClause).getCurrentNode()); |
| } |
| } |
| return query; |
| } |
| |
| protected ObjectLevelReadQuery createSimpleQuery() { |
| ObjectLevelReadQuery query = null; |
| |
| if (this.queryResult == ResultType.UNKNOWN) { |
| // unknown type so let's figure this out. |
| if (selection == null) { |
| if (this.roots != null && !this.roots.isEmpty()) { |
| this.selection = (SelectionImpl<?>) this.roots.iterator().next(); |
| query = new ReadAllQuery(((FromImpl) this.selection).getJavaType()); |
| List<org.eclipse.persistence.expressions.Expression> list = ((FromImpl) this.roots.iterator().next()).findJoinFetches(); |
| for (org.eclipse.persistence.expressions.Expression fetch : list) { |
| query.addJoinedAttribute(fetch); |
| } |
| if (!list.isEmpty()) { |
| query.setExpressionBuilder(list.get(0).getBuilder()); |
| } |
| } else if (this.roots == null || this.roots.isEmpty()) { |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("CRITERIA_NO_ROOT_FOR_COMPOUND_QUERY")); |
| } |
| |
| } else { |
| // Selection is not null set type to selection |
| TypeImpl type = ((MetamodelImpl)this.metamodel).getType(selection.getJavaType()); |
| if (type != null && type.getPersistenceType().equals(PersistenceType.ENTITY)) { |
| query = new ReadAllQuery(type.getJavaType()); |
| List<org.eclipse.persistence.expressions.Expression> list = ((FromImpl) this.roots.iterator().next()).findJoinFetches(); |
| for (org.eclipse.persistence.expressions.Expression fetch : list) { |
| query.addJoinedAttribute(fetch); |
| } |
| query.setExpressionBuilder(((InternalSelection)selection).getCurrentNode().getBuilder()); |
| |
| } else { |
| query = new ReportQuery(); |
| query.setReferenceClass(this.selection.getCurrentNode().getBuilder().getQueryClass()); |
| if (!this.selection.isCompoundSelection() && ((InternalExpression) this.selection).isCompoundExpression()) { |
| if (((FunctionExpressionImpl) this.selection).getOperation() == CriteriaBuilderImpl.SIZE) { |
| //selecting size not all databases support subselect in select clause so convert to count/groupby |
| PathImpl collectionExpression = (PathImpl) ((FunctionExpressionImpl) this.selection).getChildExpressions().get(0); |
| ExpressionImpl fromExpression = (ExpressionImpl) collectionExpression.getParentPath(); |
| ((ReportQuery) query).addAttribute(this.selection.getAlias(), collectionExpression.getCurrentNode().count(), ClassConstants.INTEGER); |
| ((ReportQuery) query).addGrouping(fromExpression.getCurrentNode()); |
| } |
| ((ReportQuery) query).addAttribute(this.selection.getAlias(), this.selection.getCurrentNode(), this.selection.getJavaType()); |
| |
| } else { |
| ((ReportQuery) query).addItem(this.selection.getAlias(), this.selection.getCurrentNode()); |
| ((ReportQuery) query).setShouldReturnSingleAttribute(true); |
| } |
| } |
| } |
| } else if (this.queryResult.equals(ResultType.ENTITY)) { |
| |
| if (this.selection != null && (!((InternalSelection) this.selection).isRoot())) { |
| query = new ReportQuery(); |
| query.setReferenceClass(this.queryType); |
| ((ReportQuery) query).addItem(this.selection.getAlias(), this.selection.getCurrentNode(), ((FromImpl) this.selection).findJoinFetches()); |
| ((ReportQuery) query).setShouldReturnSingleAttribute(true); |
| } else { |
| query = new ReadAllQuery(this.queryType); |
| if (this.roots != null && !this.roots.isEmpty()) { |
| List<org.eclipse.persistence.expressions.Expression> list = ((FromImpl) this.roots.iterator().next()).findJoinFetches(); |
| if (!list.isEmpty()) { |
| query.setExpressionBuilder(list.get(0).getBuilder()); // set the builder to one of the fetches bases. |
| } |
| for (org.eclipse.persistence.expressions.Expression fetch : list) { |
| query.addJoinedAttribute(fetch); |
| } |
| } |
| if (selection != null) { |
| query.setExpressionBuilder(this.selection.currentNode.getBuilder()); |
| } |
| } |
| } else { |
| ReportQuery reportQuery = null; |
| if (this.queryResult.equals(ResultType.TUPLE)) { |
| List list = new ArrayList(); |
| list.add(this.selection); |
| reportQuery = new TupleQuery(list); |
| } else { |
| reportQuery = new ReportQuery(); |
| reportQuery.setShouldReturnWithoutReportQueryResult(true); |
| } |
| if (this.selection != null) { |
| if (!this.selection.isCompoundSelection() && ((InternalExpression)this.selection).isCompoundExpression()){ |
| if(((FunctionExpressionImpl)this.selection).getOperation() == CriteriaBuilderImpl.SIZE){ |
| //selecting size not all databases support subselect in select clause so convert to count/groupby |
| PathImpl collectionExpression = (PathImpl) ((FunctionExpressionImpl)this.selection).getChildExpressions().get(0); |
| ExpressionImpl fromExpression = (ExpressionImpl) collectionExpression.getParentPath(); |
| reportQuery.addAttribute(this.selection.getAlias(), collectionExpression.getCurrentNode().count(), ClassConstants.INTEGER); |
| reportQuery.addGrouping(fromExpression.getCurrentNode()); |
| }else{ |
| reportQuery.addAttribute(this.selection.getAlias(), this.selection.getCurrentNode(), this.selection.getJavaType()); |
| |
| }}else{ |
| if (((InternalSelection) selection).isFrom()) { |
| reportQuery.addItem(selection.getAlias(), selection.getCurrentNode(), ((FromImpl) selection).findJoinFetches()); |
| } else { |
| reportQuery.addAttribute(selection.getAlias(), selection.getCurrentNode(), selection.getJavaType()); |
| }} |
| reportQuery.setReferenceClass(((InternalSelection) this.selection).getCurrentNode().getBuilder().getQueryClass()); |
| reportQuery.setExpressionBuilder(((InternalSelection) this.selection).getCurrentNode().getBuilder()); |
| } |
| query = reportQuery; |
| if (this.groupBy != null && !this.groupBy.isEmpty()) { |
| for (Expression<?> exp : this.groupBy) { |
| reportQuery.addGrouping(((InternalSelection) exp).getCurrentNode()); |
| } |
| } |
| if (this.havingClause != null) { |
| reportQuery.setHavingExpression(((InternalSelection) this.havingClause).getCurrentNode()); |
| } |
| } |
| if (query.getReferenceClass() == null){ |
| if (this.where != null && ((InternalSelection) this.where).getCurrentNode() != null && ((InternalSelection) this.where).getCurrentNode().getBuilder() != null && ((InternalSelection) this.where).getCurrentNode().getBuilder().getQueryClass() != null) { |
| query.setReferenceClass(((InternalSelection) this.where).getCurrentNode().getBuilder().getQueryClass()); |
| } else if (roots != null && ! roots.isEmpty()){ |
| Root root = this.getRoots().iterator().next(); |
| query.setReferenceClass(root.getJavaType()); |
| } |
| } |
| |
| if (selection == null) { |
| //the builder in the where clause may not be the correct builder for this query. Search for a root that matches the query type. |
| if (roots != null && ! roots.isEmpty()){ |
| for (Root root : this.getRoots()){ |
| if (root.getJavaType().equals(this.queryType)){ |
| query.setExpressionBuilder(((RootImpl) root).getCurrentNode().getBuilder()); |
| break; |
| } |
| } |
| } |
| } |
| |
| return query; |
| } |
| |
| /** |
| * Translates from the criteria query to a EclipseLink Database Query. |
| */ |
| @Override |
| public DatabaseQuery translate() { |
| ObjectLevelReadQuery query = (ObjectLevelReadQuery)super.translate(); |
| |
| for (Iterator iterator = this.getRoots().iterator(); iterator.hasNext();) { |
| findJoins((FromImpl) iterator.next()); |
| } |
| |
| if (this.joins != null && !joins.isEmpty()) { |
| for (FromImpl join : this.joins) { |
| query.addNonFetchJoinedAttribute(((InternalSelection) join).getCurrentNode()); |
| } |
| } |
| if (this.distinct) { |
| query.setDistinctState(ObjectLevelReadQuery.USE_DISTINCT); |
| } else { |
| query.setDistinctState(ObjectLevelReadQuery.DONT_USE_DISTINCT); |
| if (query.hasJoining()) { |
| query.setShouldFilterDuplicates(false); |
| } |
| } |
| if (this.orderBy != null && !this.orderBy.isEmpty()) { |
| for (Order order : this.orderBy) { |
| OrderImpl orderImpl = (OrderImpl) order; |
| org.eclipse.persistence.expressions.Expression orderExp = ((ExpressionImpl) orderImpl.getExpression()).getCurrentNode(); |
| if (orderImpl.isAscending()) { |
| orderExp = orderExp.ascending(); |
| } else { |
| orderExp = orderExp.descending(); |
| } |
| query.addOrdering(orderExp); |
| } |
| } |
| |
| return query; |
| } |
| |
| } |