org.eclipse.persistence.internal.jpa.jpql.JPQLQueryHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2012, 2021 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
package org.eclipse.persistence.internal.jpa.jpql;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.jpql.Assert;
import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkTraverseChildrenVisitor;
import org.eclipse.persistence.jpa.jpql.parser.DefaultEclipseLinkJPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.DeleteClause;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.FromClause;
import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.UpdateClause;
import org.eclipse.persistence.queries.DatabaseQuery;
/**
* This helper can perform various operations over a JPQL query.
*
* @version 2.5
* @since 2.5
* @author Pascal Filion
*/
public class JPQLQueryHelper {
/**
* The grammar that defines how to parse a JPQL query.
*/
private JPQLGrammar jpqlGrammar;
/**
* Creates a new JPQLQueryHelper
which uses the latest JPQL grammar.
*/
public JPQLQueryHelper() {
this(DefaultEclipseLinkJPQLGrammar.instance());
}
/**
* Creates a new JPQLQueryHelper
.
*
* @param jpqlGrammar The {@link JPQLGrammar} that will determine how to parse JPQL queries
* @exception NullPointerException The {@link JPQLGrammar} cannot be null
*/
public JPQLQueryHelper(JPQLGrammar jpqlGrammar) {
super();
Assert.isNotNull(jpqlGrammar, "The JPQLGrammar cannot be null");
this.jpqlGrammar = jpqlGrammar;
}
/**
* Retrieves the list of {@link ClassDescriptor descriptors} corresponding to the entities used
* throughout the given JPQL query.
*
* @param jpqlQuery The JPQL query used to gather the descriptors defined in the declaration clause
* @param session The {@link AbstractSession} is used to retrieve the descriptors
* @return The list of {@link ClassDescriptor descriptors} used in the JPQL query or an empty
* list if the JPQL query is invalid or incomplete
*/
public List getClassDescriptors(CharSequence jpqlQuery, AbstractSession session) {
// Parse the JPQL query
JPQLExpression jpqlExpression = new JPQLExpression(jpqlQuery, jpqlGrammar, true);
// Create the context and cache the information
JPQLQueryContext queryContext = new JPQLQueryContext(jpqlGrammar);
queryContext.cache(session, null, jpqlExpression, jpqlQuery);
// Visit the JPQL query and collect the descriptors defined in the declaration clauses
DescriptorCollector collector = new DescriptorCollector(queryContext);
jpqlExpression.accept(collector);
return new ArrayList<>(collector.descriptors);
}
/**
* Retrieves the class names and the attribute names mapped to their types that are used in the
* constructor expressions defined in the SELECT
clause.
*
* For instance, from the following JPQL query:
*
* SELECT new test.example.Employee(e.name, e.id),
* new test.example.Address(a.zipcode)
* FROM Employee e, Address a
*
* The return object is
* |- test.example.Employee
* |- |- name : String
* |- |- id : int
* |- test.example.Address
* |- zipcode : int
*
* @param session The {@link AbstractSession} is used to retrieve the mappings used in the
* constructor items
* @return The holder of the result
*/
public List getConstructorQueryMappings(AbstractSession session) {
List allMappings = new LinkedList<>();
for (DatabaseQuery query : session.getJPAQueries()) {
ConstructorQueryMappings mappings = getConstructorQueryMappings(session, query);
allMappings.add(mappings);
}
return allMappings;
}
/**
* Retrieves the class names and the attribute names mapped to their types that are used in the
* constructor expressions defined in the SELECT
clause.
*
* For instance, from the following JPQL query:
*
*
SELECT new test.example.Address(a.streetName, a.zipcode)
* FROM Address a
*
* The return object is
* test.example.Address
* |- BasicMapping(streetName) : String
* |- BasicMapping(zipcode) : int
*
* @param session The {@link AbstractSession} is used to retrieve the mappings used in the
* constructor items
* @return The holder of the result
*/
public ConstructorQueryMappings getConstructorQueryMappings(AbstractSession session,
DatabaseQuery query) {
JPQLQueryContext queryContext = new JPQLQueryContext(query, jpqlGrammar);
ConstructorQueryMappings mappings = new ConstructorQueryMappings(query);
mappings.populate(jpqlGrammar);
return mappings;
}
/**
* Returns the JPQL grammar that will be used to define how to parse a JPQL query.
*
* @return The grammar that will determine how to parse a JPQL query
*/
public JPQLGrammar getGrammar() {
return jpqlGrammar;
}
private static class DescriptorCollector extends AbstractEclipseLinkTraverseChildrenVisitor {
private Set descriptors;
private JPQLQueryContext queryContext;
private DeclarationResolver resolver;
/**
* Creates a new DescriptorCollector
.
*/
private DescriptorCollector(JPQLQueryContext queryContext) {
super();
this.descriptors = new HashSet<>();
this.queryContext = queryContext;
}
private void collectDescriptors(Expression expression, DeclarationResolver resolver) {
resolver.populate(expression);
for (Declaration declaration : resolver.getDeclarations()) {
ClassDescriptor descriptor = declaration.getDescriptor();
if (descriptor != null) {
descriptors.add(descriptor);
}
}
}
@Override
public void visit(DeleteClause expression) {
resolver = new DeclarationResolver(queryContext, null);
collectDescriptors(expression, resolver);
}
@Override
public void visit(FromClause expression) {
resolver = new DeclarationResolver(queryContext, null);
collectDescriptors(expression, resolver);
}
@Override
public void visit(SimpleSelectStatement expression) {
resolver = new DeclarationResolver(queryContext, resolver);
collectDescriptors(expression, resolver);
resolver = resolver.getParent();
}
@Override
public void visit(UpdateClause expression) {
resolver = new DeclarationResolver(queryContext, null);
collectDescriptors(expression, resolver);
}
}
}