blob: 0b747563bd7b3df156287b7fed89c3c873c62033 [file] [log] [blame]
/*
* Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 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
// 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.HashSet;
import java.util.List;
import java.util.Set;
import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.Metamodel;
import org.eclipse.persistence.expressions.ExpressionBuilder;
/**
* <p>
* <b>Purpose</b>: Contains the implementation of the AbstractQuery interface of
* the JPA criteria API.
* <p>
* <b>Description</b>: This is the container class for the components that
* define a query. This is the superclass of both the CriteriaQuery and the
* SubQuery.
*
* @see jakarta.persistence.criteria CriteriaQuery
*
* @author gyorke
* @since EclipseLink 1.2
*/
public abstract class AbstractQueryImpl<T> extends CommonAbstractCriteriaImpl<T> implements AbstractQuery<T> {
private static final long serialVersionUID = -5270020290752637882L;
protected ResultType queryResult;
protected boolean distinct;
protected Predicate havingClause;
protected List<Expression<?>> groupBy;
protected Set<Root<?>> roots;
protected org.eclipse.persistence.expressions.Expression baseExpression;
protected enum ResultType{
UNKNOWN, OBJECT_ARRAY, PARTIAL, TUPLE, ENTITY, CONSTRUCTOR, OTHER
}
public AbstractQueryImpl(Metamodel metamodel, ResultType queryResult, CriteriaBuilderImpl queryBuilder, Class<T> resultType){
super(metamodel, queryBuilder, resultType);
this.roots = new HashSet<Root<?>>();
this.queryResult = queryResult;
this.baseExpression = new ExpressionBuilder();
}
/**
* 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.
* @param grouping list of zero or more grouping expressions
* @return the modified query
*/
@Override
public AbstractQuery<T> groupBy(List<Expression<?>> grouping){
this.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.
*
* @param grouping
* zero or more grouping expressions
* @return the modified query
*/
@Override
public AbstractQuery<T> groupBy(Expression<?>... grouping){
this.groupBy = new ArrayList<Expression<?>>();
for (Expression<?> exp : grouping){
this.groupBy.add(exp);
}
return this;
}
/**
* Specify a restriction over the groups of the query. Replaces the previous
* having restriction(s), if any.
*
* @param restriction
* a simple or compound boolean expression
* @return the modified query
*/
@Override
public AbstractQuery<T> having(Expression<Boolean> restriction) {
findRootAndParameters(restriction);
if (((InternalExpression)restriction).isCompoundExpression() || ((InternalExpression)restriction).isPredicate()) {
this.havingClause = (Predicate) restriction;
} else {
this.havingClause = queryBuilder.isTrue(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.
*
* @param restrictions
* zero or more restriction predicates
* @return the modified query
*/
@Override
public AbstractQuery<T> having(Predicate... restrictions){
if (restrictions != null && restrictions.length > 0) {
Predicate conjunction = this.queryBuilder.conjunction();
for (Predicate predicate : restrictions) {
conjunction = this.queryBuilder.and(conjunction, predicate);
}
findRootAndParameters(conjunction);
this.havingClause = conjunction;
}
return this;
}
public abstract void addJoin(FromImpl join);
/**
* 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 AbstractQuery<T> distinct(boolean distinct){
this.distinct= distinct;
return this;
}
@Override
protected org.eclipse.persistence.expressions.Expression getBaseExpression() {
return getBaseExpression(null);
}
protected org.eclipse.persistence.expressions.Expression getBaseExpression(Root root) {
if (this.roots.isEmpty()) {
baseExpression = new ExpressionBuilder();
} else if (this.roots.size() == 1) {
baseExpression = ((RootImpl) this.roots.iterator().next()).getCurrentNode();
} else if (root != null) {
for (Root r : this.roots) {
if (r == root) {
baseExpression = ((RootImpl) r).getCurrentNode();
}
}
}
return baseExpression;
}
/**
* Return a list of the grouping expressions
* @return the list of grouping expressions
*/
@Override
public List<Expression<?>> getGroupList(){
if (this.groupBy == null){
this.groupBy = new ArrayList<Expression<?>>();
}
return this.groupBy;
}
/**
* Return the predicate that corresponds to the restriction(s) over the
* grouping items.
*
* @return having clause predicate
*/
@Override
public Predicate getGroupRestriction(){
return this.havingClause;
}
/**
* Return the query roots.
*
* @return the set of query roots
*/
@Override
public Set<Root<?>> getRoots(){
return this.roots;
}
@Override
protected void integrateRoot(RootImpl root) {
if (!this.roots.contains(root)) {
this.roots.add(root);
}
}
/**
* Return whether duplicate query results must be eliminated or retained.
*
* @return boolean indicating whether duplicate query results must be
* eliminated
*/
@Override
public boolean isDistinct(){
return this.distinct;
}
protected void findJoins(FromImpl root) {
root.findJoins(this);
}
/**
* Add a query root corresponding to the given entity, forming a Cartesian
* product with any existing roots.
*
* @param entity
* metamodel entity representing the entity of type X
* @return query root corresponding to the given entity
*/
@Override
public <X> Root<X> from(EntityType<X> entity) {
return this.internalFrom(entity);
}
/**
* Add a query root corresponding to the given entity, forming a Cartesian
* product with any existing roots.
*
* @param entityClass
* the entity class
* @return query root corresponding to the given entity
*/
@Override
public <X> Root<X> from(Class<X> entityClass) {
return this.internalFrom(entityClass);
}
// 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 AbstractQuery<T> where(Expression<Boolean> restriction){
return (AbstractQuery<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 AbstractQuery<T> where(Predicate... restrictions) {
return (AbstractQuery<T>) super.where(restrictions);
}
}