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

org.eclipse.persistence.internal.jpa.querydef.SubQueryImpl Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2011, 2018 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
//     02/03/2017 - Dalia Abo Sheasha
//       - 509693 : EclipseLink generates inconsistent SQL statements for SubQuery

package org.eclipse.persistence.internal.jpa.querydef;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.CollectionJoin;
import jakarta.persistence.criteria.CommonAbstractCriteria;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.ParameterExpression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import jakarta.persistence.criteria.SetJoin;
import jakarta.persistence.criteria.Subquery;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.Type.PersistenceType;

import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.expressions.ConstantExpression;
import org.eclipse.persistence.internal.expressions.SubSelectExpression;
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.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReportQuery;

/**
 * 

* Purpose: Contains the implementation of the SubQuery interface of the JPA * criteria API. *

* Description: This is the container class for the components that define a query to * be used in a sub select expression. *

* * @see jakarta.persistence.criteria CriteriaQuery * @see jakarta.persistence.criteria SubQuery * * @author gyorke * @since EclipseLink 2.0 */ public class SubQueryImpl extends AbstractQueryImpl implements Subquery , InternalExpression, InternalSelection{ protected SelectionImpl selection; protected SubSelectExpression currentNode; protected String alias; protected ReportQuery subQuery; protected Set> correlatedJoins; protected CommonAbstractCriteria parent; protected Set processedJoins; protected Set correlations; public SubQueryImpl(Metamodel metamodel, Class result, CriteriaBuilderImpl queryBuilder, CommonAbstractCriteria parent){ super(metamodel, ResultType.OTHER, queryBuilder, result); this.subQuery = new ReportQuery(); TypeImpl queryType = ((MetamodelImpl)metamodel).getType(result); if (queryType != null && queryType.getPersistenceType() == PersistenceType.ENTITY){ this.subQuery.setReferenceClass(result); } this.subQuery.setDistinctState(ObjectLevelReadQuery.DONT_USE_DISTINCT); this.correlatedJoins = new HashSet(); this.correlations = new HashSet(); this.currentNode = new SubSelectExpression(subQuery, new ExpressionBuilder()); this.parent = parent; } /** * 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 Subquery select(Expression selection) { findRootAndParameters(selection); for (Iterator iterator = this.getRoots().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } for (Iterator iterator = this.getCorrelatedJoins().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } this.selection = (SelectionImpl) selection; this.queryType = selection.getJavaType(); this.subQuery.getItems().clear(); if (selection.isCompoundSelection()) { int count = 0; for (Selection select : selection.getCompoundSelectionItems()) { this.subQuery.addItem(String.valueOf(count), ((InternalSelection) select).getCurrentNode()); ++count; } this.subQuery.setExpressionBuilder(((InternalSelection)selection.getCompoundSelectionItems().get(0)).getCurrentNode().getBuilder()); } else { TypeImpl type = ((MetamodelImpl)this.metamodel).getType(selection.getJavaType()); if (type != null && type.getPersistenceType().equals(PersistenceType.ENTITY)) { this.subQuery.addAttribute("", new ConstantExpression(1, ((InternalSelection)selection).getCurrentNode().getBuilder())); this.subQuery.addNonFetchJoinedAttribute(((InternalSelection)selection).getCurrentNode()); } else { String itemName = selection.getAlias(); if (itemName == null){ itemName = ((InternalSelection) selection).getCurrentNode().getName(); } this.subQuery.addItem(itemName, ((InternalSelection) selection).getCurrentNode()); } this.subQuery.setExpressionBuilder(((InternalSelection)selection).getCurrentNode().getBuilder()); } return this; } // 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 Subquery where(Expression restriction){ super.where(restriction); org.eclipse.persistence.expressions.Expression currentNode = ((InternalSelection)this.where).getCurrentNode(); for(org.eclipse.persistence.expressions.Expression exp: this.correlations){ currentNode = currentNode.and(exp); } this.subQuery.setSelectionCriteria(currentNode); for (Iterator iterator = this.getRoots().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } for (Iterator iterator = this.getCorrelatedJoins().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } return this; } /** * 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 Subquery where(Predicate... restrictions){ super.where(restrictions); org.eclipse.persistence.expressions.Expression currentNode = ((InternalSelection)this.where).getCurrentNode(); for(org.eclipse.persistence.expressions.Expression exp: this.correlations){ currentNode = currentNode.and(exp); } this.subQuery.setSelectionCriteria(currentNode); for (Iterator iterator = this.getRoots().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } for (Iterator iterator = this.getCorrelatedJoins().iterator(); iterator.hasNext();){ findJoins((FromImpl)iterator.next()); } 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 * zero or more grouping expressions * @return the modified query */ @Override public Subquery groupBy(Expression... grouping){ super.groupBy(grouping); this.subQuery.getGroupByExpressions().clear(); for (Expression groupby: grouping){ this.subQuery.addGrouping(((InternalSelection)groupby).getCurrentNode()); } 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 * zero or more grouping expressions * @return the modified query */ @Override public Subquery groupBy(List> grouping){ super.groupBy(grouping); this.subQuery.getGroupByExpressions().clear(); for (Expression groupby: grouping){ this.subQuery.addGrouping(((InternalSelection)groupby).getCurrentNode()); } 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 Subquery having(Expression restriction){ super.having(restriction); if (this.havingClause != null){ this.subQuery.setHavingExpression(((InternalSelection)restriction).getCurrentNode()); }else{ this.subQuery.setHavingExpression(null); } 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 Subquery having(Predicate... restrictions) { super.having(restrictions); if (this.havingClause != null){ this.subQuery.setHavingExpression(((InternalSelection) this.havingClause).getCurrentNode()); }else{ this.subQuery.setHavingExpression(null); } return this; } /** * Correlates a root of the enclosing query to a root of the subquery and * returns the subquery root. * * @param parentRoot * a root of the containing query * @return subquery root */ @Override public Root correlate(Root parentRoot){ RootImpl root = new RootImpl(parentRoot.getModel(), metamodel, parentRoot.getJavaType(), internalCorrelate((FromImpl)parentRoot), parentRoot.getModel(), (FromImpl) parentRoot); integrateRoot(root); return root; } /** * Correlates a join object of the enclosing query to a join object of the * subquery and returns the subquery join object. * * @param parentJoin * join target of the containing query * @return subquery join */ @Override public Join correlate(Join parentJoin){ this.correlatedJoins.add(parentJoin); JoinImpl join = new JoinImpl(parentJoin.getParentPath(), metamodel.managedType(parentJoin.getModel().getBindableJavaType()), metamodel, parentJoin.getJavaType(), internalCorrelate((FromImpl) parentJoin), parentJoin.getModel(), parentJoin.getJoinType(), (FromImpl) parentJoin); return join; } /** * Correlates a join to a Collection-valued association or element * collection in the enclosing query to a join object of the subquery and * returns the subquery join object. * * @param parentCollection * join target of the containing query * @return subquery join */ @Override public CollectionJoin correlate(CollectionJoin parentCollection){ this.correlatedJoins.add(parentCollection); return new CollectionJoinImpl(parentCollection.getParentPath(), metamodel.managedType(parentCollection.getModel().getBindableJavaType()), metamodel, parentCollection.getJavaType(), internalCorrelate((FromImpl) parentCollection), parentCollection.getModel(), parentCollection.getJoinType(), (FromImpl) parentCollection); } /** * Correlates a join to a Set-valued association or element collection in * the enclosing query to a join object of the subquery and returns the * subquery join object. * * @param parentSet * join target of the containing query * @return subquery join */ @Override public SetJoin correlate(SetJoin parentSet){ this.correlatedJoins.add(parentSet); return new SetJoinImpl(parentSet.getParentPath(), metamodel.managedType(parentSet.getModel().getBindableJavaType()), metamodel, parentSet.getJavaType(), ((InternalSelection)parentSet).getCurrentNode(), parentSet.getModel(), parentSet.getJoinType(), (FromImpl) parentSet); } /** * Correlates a join to a List-valued association or element collection in * the enclosing query to a join object of the subquery and returns the * subquery join object. * * @param parentList * join target of the containing query * @return subquery join */ @Override public ListJoin correlate(ListJoin parentList){ this.correlatedJoins.add(parentList); return new ListJoinImpl(parentList.getParentPath(), metamodel.managedType(parentList.getModel().getBindableJavaType()), metamodel, parentList.getJavaType(), internalCorrelate((FromImpl) parentList), parentList.getModel(), parentList.getJoinType(), (FromImpl) parentList); } /** * Correlates a join to a Map-valued association or element collection in * the enclosing query to a join object of the subquery and returns the * subquery join object. * * @param parentMap * join target of the containing query * @return subquery join */ @Override public MapJoin correlate(MapJoin parentMap){ this.correlatedJoins.add(parentMap); return new MapJoinImpl(parentMap.getParentPath(), metamodel.managedType(parentMap.getModel().getBindableJavaType()), metamodel, parentMap.getJavaType(), internalCorrelate((FromImpl) parentMap), parentMap.getModel(), parentMap.getJoinType(), (FromImpl) parentMap); } protected org.eclipse.persistence.expressions.Expression internalCorrelate(FromImpl from){ org.eclipse.persistence.expressions.Expression expression = ((InternalSelection)from).getCurrentNode(); ExpressionBuilder builder = new ExpressionBuilder(expression.getBuilder().getQueryClass()); org.eclipse.persistence.expressions.Expression correlated = expression.rebuildOn(builder); expression = expression.equal(correlated); this.correlations.add(expression); org.eclipse.persistence.expressions.Expression selectionCriteria = expression.and(this.subQuery.getSelectionCriteria()); this.subQuery.setSelectionCriteria(selectionCriteria); return correlated; } @Override public Set> getParameters() { return ((CommonAbstractCriteriaImpl)this.getContainingQuery()).getParameters(); } /** * Return the query of which this is a subquery. * @return the enclosing query or subquery */ @Override public AbstractQuery getParent(){ if (parent == null || parent instanceof AbstractQuery) { return (AbstractQuery) this.parent; } throw new IllegalStateException("TODO.. write a better message"); } /** * 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 Subquery distinct(boolean distinct){ super.distinct(distinct); if (!distinct){ this.subQuery.setDistinctState(ObjectLevelReadQuery.DONT_USE_DISTINCT); }else{ this.subQuery.setDistinctState(ObjectLevelReadQuery.USE_DISTINCT); } return this; } /** * Returns the current EclipseLink expression at this node in the criteria expression tree * @return the currentNode */ @Override public org.eclipse.persistence.expressions.Expression getCurrentNode() { return currentNode; } /** * Return the selection item of the query. This will correspond to the query type. * @return the selection item of the query */ @Override public Expression getSelection(){ return (Expression) this.selection; } /** * Return the joins that have been made from the subquery. * * @return joins made from this type */ @Override public java.util.Set> getCorrelatedJoins(){ return this.correlatedJoins; } @Override public void addParameter(ParameterExpression parameter){ ((CommonAbstractCriteriaImpl)this.getContainingQuery()).addParameter(parameter); } @Override public void addJoin(FromImpl join){ if (this.processedJoins == null ) { this.processedJoins = new HashSet(); } if (! this.processedJoins.contains(join)){ this.processedJoins.add(join); this.subQuery.addNonFetchJoinedAttribute(join.getCurrentNode()); } } //Expression @Override public Expression as(Class type) { return (Expression) this; } @Override public Predicate in(Object... values) { List list = new ArrayList(); list.add(this); return new CompoundExpressionImpl(this.metamodel, this.currentNode.in(values), list, "in"); } /** * Apply a predicate to test whether the expression is a member * of the argument list. * @param values * @return predicate testing for membership */ @Override public Predicate in(Expression... values) { List list = new ArrayList(); list.add(this); for (Expression exp: values){ if (!((InternalExpression)exp).isLiteral() && !((InternalExpression) exp).isParameter()){ Object[] params = new Object[]{exp}; throw new IllegalArgumentException(ExceptionLocalization.buildMessage("CRITERIA_NON_LITERAL_PASSED_TO_IN",params)); } } return new CompoundExpressionImpl(this.metamodel, this.currentNode.in(values), list, "in"); } /** * Apply a predicate to test whether the expression is a member * of the collection. * @param values collection * @return predicate testing for membership */ @Override public Predicate in(Collection values) { List list = new ArrayList(); list.add(this); return new CompoundExpressionImpl(this.metamodel, this.currentNode.in(values), list, "in"); } /** * Apply a predicate to test whether the expression is a member * of the collection. * @param values expression corresponding to collection * @return predicate testing for membership */ @Override public Predicate in(Expression> values) { List list = new ArrayList(); list.add(this); return new CompoundExpressionImpl(this.metamodel, this.currentNode.in(((InternalSelection)values).getCurrentNode()), list, "in"); } @Override public Predicate isNotNull() { List list = new ArrayList(); list.add(this); return new CompoundExpressionImpl(this.metamodel, this.currentNode.notNull(), list, "not null"); } @Override public Predicate isNull() { List list = new ArrayList(); list.add(this); return new CompoundExpressionImpl(this.metamodel, this.currentNode.isNull(), list, "is null"); } //SELECTION /** * Assign an alias to the selection. * * @param name * alias */ @Override public Selection alias(String name) { this.alias = name; return this; } @Override public String getAlias() { return this.alias; } @Override public Class getJavaType() { return this.queryType; } /** * Return selection items composing a compound selection * @return list of selection items * @throws IllegalStateException if selection is not a compound * selection */ @Override public List> getCompoundSelectionItems(){ throw new IllegalStateException(ExceptionLocalization.buildMessage("CRITERIA_NOT_A_COMPOUND_SELECTION")); } /** * Whether the selection item is a compound selection * @return boolean */ @Override public boolean isCompoundSelection(){ return false; } @Override public boolean isConstructor(){ return false; } @Override public boolean isJunction(){ return false; } @Override public boolean isPredicate(){ return false; } @Override public boolean isParameter(){ return false; } @Override public boolean isRoot(){ return false; } @Override public boolean isSubquery(){ return true; } @Override protected void integrateRoot(RootImpl root) { if (this.roots.isEmpty()) { TypeImpl type = ((MetamodelImpl)this.metamodel).getType(this.queryType); if ((type != null && type.getPersistenceType() == PersistenceType.ENTITY) || queryType.equals(ClassConstants.OBJECT)) { // this is the first root, set return type and selection and query type if (this.selection == null) { this.selection = root; this.subQuery.getItems().clear(); this.subQuery.addAttribute("", new ConstantExpression(1, root.getCurrentNode().getBuilder())); this.queryResult = ResultType.ENTITY; } } this.subQuery.setReferenceClass(root.getJavaType()); this.subQuery.setExpressionBuilder(root.getCurrentNode().getBuilder()); this.queryType = root.getJavaType(); this.currentNode.setBaseExpression(((CommonAbstractCriteriaImpl)this.parent).getBaseExpression()); } // If the parent of this SubQuery is a CriteriaQuery and the CriteriaQuery has multiple roots, // assign the baseExpression based on the SubQuery's roots - Bug 509693 if (!this.roots.contains(root) && this.parent instanceof CriteriaQueryImpl && ((CriteriaQueryImpl) this.parent).getRoots().size() > 1) { this.currentNode.setBaseExpression(((CriteriaQueryImpl) this.parent).getBaseExpression(root)); } super.integrateRoot(root); } @Override public boolean isCompoundExpression(){ return false; } @Override public boolean isExpression(){ return true; } @Override public boolean isFrom(){ return false; } @Override public boolean isLiteral(){ return false; } @Override public void findRootAndParameters(CommonAbstractCriteriaImpl query){ for (Join join: this.correlatedJoins){ ((JoinImpl)join).findRootAndParameters(query); } } @Override protected org.eclipse.persistence.expressions.Expression getBaseExpression() { return this.currentNode.getSubQuery().getExpressionBuilder(); } @Override public CommonAbstractCriteria getContainingQuery() { return this.parent; } @Override public DatabaseQuery getDatabaseQuery() { return this.subQuery; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy