Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.babyfish.hibernate.internal.QueryTemplateImpl Maven / Gradle / Ivy
/*
* BabyFish, Object Model Framework for Java and JPA.
* https://github.com/babyfish-ct/babyfish
*
* Copyright (c) 2008-2015, Tao Chen
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*/
package org.babyfish.hibernate.internal;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.criteria.CompoundSelection;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.Subquery;
import org.babyfish.collection.ArrayList;
import org.babyfish.collection.LinkedHashMap;
import org.babyfish.collection.MACollections;
import org.babyfish.collection.XOrderedMap;
import org.babyfish.hibernate.ejb.HibernateXEntityManagerFactory;
import org.babyfish.lang.Arguments;
import org.babyfish.lang.Nulls;
import org.babyfish.persistence.XTypedQuery;
import org.babyfish.persistence.criteria.Assignment;
import org.babyfish.persistence.criteria.XAbstractQuery;
import org.babyfish.persistence.criteria.XCommonAbstractCriteria;
import org.babyfish.persistence.criteria.XCriteriaDelete;
import org.babyfish.persistence.criteria.XCriteriaQuery;
import org.babyfish.persistence.criteria.XCriteriaUpdate;
import org.babyfish.persistence.criteria.XSubquery;
import org.babyfish.persistence.criteria.expression.Aggregation;
import org.babyfish.persistence.criteria.expression.AsExpression;
import org.babyfish.persistence.criteria.expression.BetweenPredicate;
import org.babyfish.persistence.criteria.expression.BinaryArithmeticExpression;
import org.babyfish.persistence.criteria.expression.CoalesceExpression;
import org.babyfish.persistence.criteria.expression.ComparisonPredicate;
import org.babyfish.persistence.criteria.expression.CompoundPredicate;
import org.babyfish.persistence.criteria.expression.ConcatExpression;
import org.babyfish.persistence.criteria.expression.ConstantExpression;
import org.babyfish.persistence.criteria.expression.ConvertExpression;
import org.babyfish.persistence.criteria.expression.ExistsPredicate;
import org.babyfish.persistence.criteria.expression.InPredicate;
import org.babyfish.persistence.criteria.expression.InPredicate.Partition;
import org.babyfish.persistence.criteria.expression.IsEmptyPredicate;
import org.babyfish.persistence.criteria.expression.IsMemberPredicate;
import org.babyfish.persistence.criteria.expression.IsTruePredicate;
import org.babyfish.persistence.criteria.expression.LikePredicate;
import org.babyfish.persistence.criteria.expression.ListIndexExpression;
import org.babyfish.persistence.criteria.expression.LiteralExpression;
import org.babyfish.persistence.criteria.expression.MapEntryExpression;
import org.babyfish.persistence.criteria.expression.NullLiteralExpression;
import org.babyfish.persistence.criteria.expression.NullifExpression;
import org.babyfish.persistence.criteria.expression.NullnessPredicate;
import org.babyfish.persistence.criteria.expression.PathTypeExpression;
import org.babyfish.persistence.criteria.expression.PriorityConstants;
import org.babyfish.persistence.criteria.expression.SearchedCaseExpression;
import org.babyfish.persistence.criteria.expression.SimpleCaseExpression;
import org.babyfish.persistence.criteria.expression.SizeExpression;
import org.babyfish.persistence.criteria.expression.SubqueryComparisonModifierExpression;
import org.babyfish.persistence.criteria.expression.TrimExpression;
import org.babyfish.persistence.criteria.expression.UnaryArithmeticExpression;
import org.babyfish.persistence.criteria.spi.AbstractExpression;
import org.babyfish.persistence.criteria.spi.AbstractFunction;
import org.babyfish.persistence.criteria.spi.AbstractNode;
import org.babyfish.persistence.criteria.spi.AbstractQueryTemplate;
import org.babyfish.persistence.criteria.spi.MapKeyPath;
import org.babyfish.persistence.criteria.spi.PathId;
import org.babyfish.persistence.criteria.spi.QueryContext;
import org.babyfish.persistence.criteria.spi.QueryContext.Entity;
import org.babyfish.persistence.criteria.spi.QueryContext.PathNode;
import org.babyfish.persistence.criteria.spi.Visitor;
import org.babyfish.util.GraphTravelAction;
import org.babyfish.util.GraphTravelContext;
import org.babyfish.util.GraphTraveler;
import org.babyfish.util.LazyResource;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;
/**
* @author Tao Chen
*/
public class QueryTemplateImpl extends AbstractQueryTemplate {
public QueryTemplateImpl(XCommonAbstractCriteria commonAbstractCriteria) {
super(commonAbstractCriteria);
}
@Override
protected void init(XCommonAbstractCriteria commonAbstractCriteria) {
try (QueryContext queryContext = new QueryContext(commonAbstractCriteria)) {
VisitorImpl visitorImpl = new VisitorImpl(queryContext);
visitorImpl.visit(commonAbstractCriteria);
if (!visitorImpl.pathIdAllocator.isEmpty()) {
throw new AssertionError();
}
this.init(visitorImpl.toJPQL(), visitorImpl.getLiteralParameters());
}
}
@Override
protected void setTupleTransfromer(XTypedQuery> query, CompoundSelection tupleSelection) {
org.hibernate.Query hqlQuery = query.unwrap(org.hibernate.Query.class);
hqlQuery.setResultTransformer(new TupleResultTransformer(tupleSelection));
}
protected static class VisitorImpl implements Visitor {
protected QueryContext queryContext;
protected StringBuilder jpqlBuilder = new StringBuilder(256);
protected XOrderedMap literalParameters;
protected int currentPriority;
protected PathId.Allocator pathIdAllocator;
public VisitorImpl(QueryContext queryContext) {
this.queryContext = queryContext;
this.pathIdAllocator = queryContext.secondaryPathIdAllocator();
}
public String toJPQL() {
return this.jpqlBuilder.toString();
}
public Collection getLiteralParameters() {
if (this.literalParameters == null) {
return MACollections.emptySet();
}
return MACollections.unmodifiable(this.literalParameters.values());
}
@Override
public void visitCriteriaQuery(XCriteriaQuery> query) {
/*
* It is very important to keep the visit order as:
* selection, restriction, grouping, groupRestriction, orders
*
* QueryContext must visit the tree by the same order
*
* because the PathId.getAppearancePosition() is very important.
*/
this.visitAbstractQuery(query);
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.pathIdAllocator.push(PathId.Allocator.ORDER_LIST);
try {
List orderList = query.getOrderList();
if (!orderList.isEmpty()) {
jpqlBuilder.append(" order by ");
boolean addComma = false;
for (Order order : orderList) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(order);
}
}
} finally {
this.pathIdAllocator.pop();
}
}
@Override
public void visitSubquery(XSubquery> query) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append('(');
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
this.visitAbstractQuery(query);
} finally {
this.currentPriority = oldPriority;
}
jpqlBuilder.append(')');
}
@Override
public void visitCriteriaUpdate(XCriteriaUpdate> update) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("update ");
this.pathIdAllocator.push(PathId.Allocator.ON_TREE);
try {
Entity entity = this.queryContext.getRootEntities(update).get(0);
jpqlBuilder
.append(entity.getManagedType().getJavaType().getName())
.append(' ')
.append(entity.getRenderAlias());
this.renderJoin(entity);
} finally {
this.pathIdAllocator.pop();
}
jpqlBuilder.append(" set ");
this.pathIdAllocator.push(PathId.Allocator.ASSIGNMENT_LIST);
try {
boolean addComma = false;
for (Assignment assignment : update.getAssignments()) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(assignment);
}
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.RESTRICTION);
try {
Predicate restriction = update.getRestriction();
if (restriction != null) {
jpqlBuilder.append(" where ");
this.visit(restriction);
}
} finally {
this.pathIdAllocator.pop();
}
}
@Override
public void visitCriteriaDelete(XCriteriaDelete> delete) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("delete from ");
this.pathIdAllocator.push(PathId.Allocator.ON_TREE);
try {
Entity entity = this.queryContext.getRootEntities(delete).get(0);
jpqlBuilder
.append(entity.getManagedType().getJavaType().getName())
.append(' ')
.append(entity.getRenderAlias());
this.renderJoin(entity);
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.RESTRICTION);
try {
Predicate restriction = delete.getRestriction();
if (restriction != null) {
jpqlBuilder.append(" where ");
this.visit(restriction);
}
} finally {
this.pathIdAllocator.pop();
}
}
@Override
public void visitOrder(Order order) {
this.visit(order.getExpression());
this.jpqlBuilder.append(order.isAscending() ? " asc" : " desc");
}
@Override
public void visitAssignment(Assignment assignment) {
this.visit(assignment.getPath());
this.jpqlBuilder.append(" = ");
this.visit(assignment.getExpression());
}
@Override
public void visitPath(Path> path) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (path instanceof MapKeyPath>) {
jpqlBuilder.append("key(");
this.visit(path.getParentPath());
jpqlBuilder.append(')');
} else {
PathId pathId = this.pathIdAllocator.allocate();
if (pathId.getPath() != path) {
throw new AssertionError();
}
PathNode pathNode = this.queryContext.getPathNodes().get(pathId);
if (pathNode == null) {
throw new AssertionError();
}
this.renderPathNode(pathNode);
}
}
@Override
public void visitCompoundSelection(CompoundSelection> compoundSelection) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
boolean constructor =
!compoundSelection.getJavaType().isArray() &&
!Tuple.class.isAssignableFrom(compoundSelection.getJavaType());
if (constructor) {
jpqlBuilder
.append("new ")
.append(compoundSelection.getJavaType().getName())
.append('(');
}
boolean addComma = false;
for (Selection> selection : compoundSelection.getCompoundSelectionItems()) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(selection);
}
if (constructor) {
jpqlBuilder.append(')');
}
}
@Override
public void visitFunction(AbstractFunction> function) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
boolean addComma = false;
jpqlBuilder
.append(function.getFunctionName())
.append('(');
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
for (Expression> expr : function.getArguments()) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(expr);
}
} finally {
this.currentPriority = oldPriority;
}
jpqlBuilder.append(')');
}
@Override
public void visitAggregation(Aggregation, ?> aggregation) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (aggregation instanceof Aggregation.Count) {
jpqlBuilder.append("count(");
if (((Aggregation.Count)aggregation).isDistinct()) {
jpqlBuilder.append("distinct ");
}
} else if (aggregation instanceof Aggregation.Sum>) {
jpqlBuilder.append("sum(");
} else if (aggregation instanceof Aggregation.SumAsLong) {
jpqlBuilder.append("sum(");
} else if (aggregation instanceof Aggregation.SumAsDouble) {
jpqlBuilder.append("sum(");
} else if (aggregation instanceof Aggregation.Least>) {
jpqlBuilder.append("min(");
} else if (aggregation instanceof Aggregation.Greatest>) {
jpqlBuilder.append("max(");
} else if (aggregation instanceof Aggregation.Min>) {
jpqlBuilder.append("min(");
} else if (aggregation instanceof Aggregation.Max>) {
jpqlBuilder.append("max(");
} else if (aggregation instanceof Aggregation.Avg>) {
jpqlBuilder.append("avg(");
}else {
throw new AssertionError("Internal bug");
}
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
this.visit(aggregation.getOperand());
jpqlBuilder.append(')');
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitConvertExpression(ConvertExpression, ?> convertExpression) {
this.visit(convertExpression.getOperand());
}
@Override
public void visitAsExpression(AsExpression> asExpression) {
SessionFactoryImplementor factory =
(SessionFactoryImplementor)
((HibernateXEntityManagerFactory)asExpression.getCriteriaBuilder().getEntityManagerFactory())
.getSessionFactoryImplementor();
Class> javaType = asExpression.getJavaType();
Type hibernateType = factory.getTypeResolver().heuristicType(javaType.getName());
if ( hibernateType == null ) {
throw new IllegalArgumentException(
"Could not convert java type [" + javaType.getName() + "] to Hibernate type"
);
}
String targetTypeName = hibernateType.getName();
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("cast(");
this.visit(asExpression.getOperand());
jpqlBuilder
.append(" as ")
.append(targetTypeName)
.append(")");
}
@Override
public void visitLiteralExpression(LiteralExpression> literalExpression) {
LiteralParameter parameter = this.getLiteralParameter(literalExpression);
this.jpqlBuilder.append(':').append(parameter.getName());
}
@Override
public void visitNullLiteralExpression(
NullLiteralExpression> nullLiteralExpression) {
this.jpqlBuilder.append("null");
}
@Override
public void visitConstantExpression(ConstantExpression> constantExpression) {
this.renderConstant(constantExpression.getValue());
}
@Override
public void visitBinaryArithmeticExpression(
BinaryArithmeticExpression> binaryArithmeticExpression) {
this.visit(binaryArithmeticExpression.getLeftOperand());
this
.jpqlBuilder
.append(' ')
.append(binaryArithmeticExpression.getOperator())
.append(' ');
this.visit(binaryArithmeticExpression.getRightOperand());
}
@Override
public void visitUnaryArithmeticExpression(
UnaryArithmeticExpression> unaryArithmeticExpression) {
this.jpqlBuilder.append(unaryArithmeticExpression.getOperator());
this.visit(unaryArithmeticExpression.getOperand());
}
@Override
public void visitTrimExpression(TrimExpression trimExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder
.append("trim(" );
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
jpqlBuilder
.append(trimExpression.getTrimspec().name().toLowerCase())
.append(' ');
this.visit(trimExpression.getTrimCharacter());
jpqlBuilder.append(" from ");
this.visit(trimExpression.getTrimSource());
jpqlBuilder.append(')');
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitNullifExpression(NullifExpression> nullifExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("nullif(");
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
this.visit(nullifExpression.getPrimaryOperand());
jpqlBuilder.append(", ");
this.visit(nullifExpression.getSecondaryOperand());
} finally {
this.currentPriority = oldPriority;
}
jpqlBuilder.append(')');
}
@Override
public void visitSubqueryComparisonModifierExpression(
SubqueryComparisonModifierExpression> subqueryComparisonModifierExpression) {
this.jpqlBuilder.append(subqueryComparisonModifierExpression.getClass().getSimpleName().toLowerCase());
this.visit(subqueryComparisonModifierExpression.getSubquery());
}
@Override
public void visitSizeExpression(SizeExpression> sizeExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("size(");
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
this.visit(sizeExpression.getCollection());
jpqlBuilder.append(')');
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitSimpleCaseExpression(
SimpleCaseExpression, ?> simpleCaseExpression) {
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("case "); //here must append a space for simple case
this.visit(simpleCaseExpression.getExpression());
for (SimpleCaseExpression.WhenClause, ?> whenClause : simpleCaseExpression.getWhenClauses()) {
jpqlBuilder
.append(" when ");
this.renderConstant(whenClause.getCondition());
jpqlBuilder
.append(" then ");
this.visit(whenClause.getResult());
}
jpqlBuilder.append(" else ");
this.visit(simpleCaseExpression.getOtherwiseResult());
jpqlBuilder.append(" end");
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitSearchedCaseExpression(
SearchedCaseExpression> searchedCaseExpression) {
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("case"); //here must not append a space for searched case
for (SearchedCaseExpression.WhenClause> whenClause : searchedCaseExpression.getWhenClauses()) {
jpqlBuilder.append(" when ");
this.visit(whenClause.getCondition());
jpqlBuilder.append(" then ");
this.visit(whenClause.getResult());
}
jpqlBuilder.append(" else ");
this.visit(searchedCaseExpression.getOtherwiseResult());
jpqlBuilder.append(" end");
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitConcatExpression(ConcatExpression concatExpression) {
List> values = concatExpression.getValues();
int size = values.size();
StringBuilder jpqlBuilder = this.jpqlBuilder;
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
if (size == 1) {
this.visit(values.get(0));
} else {
for (int i = size - 2; i >= 0; i--) {
jpqlBuilder.append("concat(");
}
this.visit(values.get(0));
jpqlBuilder.append(", ");
this.visit(values.get(1));
jpqlBuilder.append(')');
for (int i = 2; i < size; i++) {
jpqlBuilder.append(", ");
this.visit(values.get(i));
jpqlBuilder.append(')');
}
}
} finally {
this.currentPriority = oldPriority;
}
}
@Override
public void visitPathTypeExpression(PathTypeExpression> pathTypeExpression) {
this.jpqlBuilder.append("type(");
this.visit(pathTypeExpression.getPath());
this.jpqlBuilder.append(')');
}
@Override
public void visitParameterExpression(
ParameterExpression> parameterExpression) {
if (parameterExpression.getName() != null) {
this.jpqlBuilder.append(':').append(parameterExpression.getName());
} else {
this.jpqlBuilder.append('?').append(parameterExpression.getPosition());
}
}
@Override
public void visitCoalesceExpression(CoalesceExpression> coalesceExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (coalesceExpression.getExpressions().isEmpty()) {
jpqlBuilder.append("null");
} else {
jpqlBuilder.append("coalesce(");
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
boolean addComma = false;
for (Expression> expr : coalesceExpression.getExpressions()) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(expr);
}
} finally {
this.currentPriority = oldPriority;
}
jpqlBuilder.append(')');
}
}
@Override
public void visitMapEntryExpression(MapEntryExpression, ?> mapEntryExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("entry(");
this.visit(mapEntryExpression.getMapAttributeJoin());
jpqlBuilder.append(')');
}
@Override
public void visitListIndexExpression(ListIndexExpression listIndexExpression) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("index(");
this.visit(listIndexExpression.getListAttributeJoin());
jpqlBuilder.append(')');
}
@Override
public void visitCompoundPredicate(CompoundPredicate compoundPredicate) {
String op = compoundPredicate.getOperator().name().toLowerCase();
StringBuilder jpqlBuilder = this.jpqlBuilder;
boolean addOp = false;
for (Expression> expr : compoundPredicate.getExpressions()) {
if (addOp) {
jpqlBuilder.append(' ').append(op).append(' ');
} else {
addOp = true;
}
this.visit(expr);
}
}
@Override
public void visitComparisonPredicate(ComparisonPredicate comparisonPredicate) {
String op;
if (comparisonPredicate instanceof ComparisonPredicate.Equal) {
op = "=";
} else if (comparisonPredicate instanceof ComparisonPredicate.NotEqual) {
op = "!=";
} else if (comparisonPredicate instanceof ComparisonPredicate.LessThan) {
op = "<";
} else if (comparisonPredicate instanceof ComparisonPredicate.LessThanOrEqual) {
op = "<=";
} else if (comparisonPredicate instanceof ComparisonPredicate.GreaterThan) {
op = ">";
} else if (comparisonPredicate instanceof ComparisonPredicate.GreaterThanOrEqual) {
op = ">=";
} else {
throw new AssertionError();
}
this.visit(comparisonPredicate.getLeftOperand());
this.jpqlBuilder.append(' ').append(op).append(' ');
this.visit(comparisonPredicate.getRightOperand());
}
@Override
public void visitBetweenPredicate(BetweenPredicate> betweenPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (betweenPredicate.getLowerBound() == null) {
this.visit(betweenPredicate.getSource());
if (betweenPredicate.isNegated()) {
jpqlBuilder.append(" > ");
} else {
jpqlBuilder.append(" <= ");
}
this.visit(betweenPredicate.getUpperBound());
} else if (betweenPredicate.getUpperBound() == null) {
this.visit(betweenPredicate.getSource());
if (betweenPredicate.isNegated()) {
jpqlBuilder.append(" < ");
} else {
jpqlBuilder.append(" >= ");
}
this.visit(betweenPredicate.getLowerBound());
} else {
this.visit(betweenPredicate.getSource());
jpqlBuilder.append(betweenPredicate.isNegated() ? " not between " : " between ");
this.visit(betweenPredicate.getLowerBound());
jpqlBuilder.append(" and ");
this.visit(betweenPredicate.getUpperBound());
}
}
@Override
public void visitLikePredicate(LikePredicate likePredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.visit(likePredicate.getSource());
if (likePredicate.isNegated()) {
jpqlBuilder.append(" not");
}
jpqlBuilder.append(" like ");
this.visit(likePredicate.getPattern());
if (likePredicate.getEscapeCharacter() != null) {
jpqlBuilder.append(" escape ");
this.visit(likePredicate.getEscapeCharacter());
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void visitInPredicate(InPredicate> inPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
List> partitions = (List)inPredicate.getPartitions();
String renderedExpression;
/*
* Disable the jpqlBuilder add visit the main expression
* in another StringBuilder.
* Because the main expression should not be rendered or
* should be be rendered for several times but the path id
* should only be allocated once.
*/
this.jpqlBuilder = new StringBuilder();
try {
this.visit(inPredicate.getExpression());
renderedExpression = this.jpqlBuilder.toString();
} finally {
this.jpqlBuilder = jpqlBuilder;
}
if (partitions.size() == 0) {
jpqlBuilder.append(inPredicate.isNegated() ? "1 = 1" : "1 = 0");
} else if (partitions.size() == 1 && partitions.get(0).getValues().size() == 1) {
Expression> firstElementExpression = partitions.get(0).getValues().get(0);
jpqlBuilder.append(renderedExpression);
if (firstElementExpression instanceof Subquery>) {
if (inPredicate.isNegated()) {
jpqlBuilder.append(" not");
}
jpqlBuilder.append(" in");
this.visit(firstElementExpression);
} else {
if (inPredicate.isNegated()) {
jpqlBuilder.append(" != ");
} else {
jpqlBuilder.append(" = ");
}
this.visit(firstElementExpression);
}
} else {
boolean addOuterParentheses = partitions.size() > 1;
if (addOuterParentheses) {
jpqlBuilder.append('(');
}
String op = inPredicate.isNegated() ? " and " : " or ";
boolean addOp = false;
for (Partition> partition : partitions) {
if (addOp) {
jpqlBuilder.append(op);
} else {
addOp = true;
}
jpqlBuilder.append(renderedExpression);
if (inPredicate.isNegated()) {
jpqlBuilder.append(" not");
}
List> values = (List)partition.getValues();
jpqlBuilder.append(" in(");
if (!partition.isNeedExpand()) {
jpqlBuilder
.append(':')
.append(this.getLiteralParameter(partition).getName());
} else {
int oldPriority = this.currentPriority;
this.currentPriority = PriorityConstants.LOWEST;
try {
boolean addComma = false;
for (Expression> value : values) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(value);
}
} finally {
this.currentPriority = oldPriority;
}
}
jpqlBuilder.append(')');
}
if (addOuterParentheses) {
jpqlBuilder.append(')');
}
}
}
@Override
public void visitExistsPredicate(ExistsPredicate existsPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (existsPredicate.isNegated()) {
jpqlBuilder.append("not ");
}
jpqlBuilder.append("exists");
this.visit(existsPredicate.getSubquery());
}
@Override
public void visitIsEmptyPredicate(IsEmptyPredicate> isEmptyPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.visit(isEmptyPredicate.getCollection());
jpqlBuilder.append(" is ");
if (isEmptyPredicate.isNegated()) {
jpqlBuilder.append("not ");
}
jpqlBuilder.append("empty");
}
@Override
public void visitIsMemberPredicate(IsMemberPredicate, ?> isMemberPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.visit(isMemberPredicate.getElement());
if (isMemberPredicate.isNegated()) {
jpqlBuilder.append(" not");
}
jpqlBuilder.append(" member of ");
this.visit(isMemberPredicate.getCollection());
}
@Override
public void visitIsTruePredicate(IsTruePredicate isTruePredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.visit(isTruePredicate.getOperand());
if (isTruePredicate.isNegated()) {
jpqlBuilder.append(" = 0");
} else {
jpqlBuilder.append(" = 1");
}
}
@Override
public void visitNullnessPredicate(NullnessPredicate nullnessPredicate) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
this.visit(nullnessPredicate.getOperand());
if (nullnessPredicate.isNegated()) {
jpqlBuilder.append(" is not null");
} else {
jpqlBuilder.append(" is null");
}
}
protected void visitAbstractQuery(XAbstractQuery> query) {
final StringBuilder jpqlBuilder = this.jpqlBuilder;
boolean addComma;
/*
* It is very important to keep the visit order as:
* selection, restriction, grouping, groupRestriction, orders
*
* QueryContext must visit the tree by the same order
*
* because the PathId.getAppearancePosition() is very important.
*/
jpqlBuilder.append("select ");
if (query.isDistinct()) {
jpqlBuilder.append("distinct ");
}
this.pathIdAllocator.push(PathId.Allocator.SELECTION);
try {
if (query.getSelection() != null) {
this.visit(query.getSelection());
} else {
GraphTraveler graphTraveler = new GraphTraveler() {
private boolean addComma;
@Override
protected Iterator getNeighborNodeIterator(Entity node) {
List entities = node.getEntities();
return entities != null ? entities.iterator() : null;
}
@Override
protected void preTravelNeighborNodes(
GraphTravelContext ctx,
GraphTravelAction optionalGraphTravelAction) {
Entity entity = ctx.getNode();
if (entity.isUsed() && entity.isExplicit() && !entity.isFetch()) {
if (this.addComma) {
jpqlBuilder.append(", ");
} else {
this.addComma = true;
}
jpqlBuilder.append(entity.getRenderAlias());
}
}
};
for (Entity entity : this.queryContext.getRootEntities(query)) {
graphTraveler.depthFirstTravel(entity);
}
}
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.ON_TREE);
try {
jpqlBuilder.append(" from ");
addComma = false;
for (Entity entity : this.queryContext.getRootEntities(query)) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
jpqlBuilder
.append(entity.getManagedType().getJavaType().getName())
.append(' ')
.append(entity.getRenderAlias());
}
for (Entity entity : this.queryContext.getRootEntities(query)) {
this.renderJoin(entity);
}
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.RESTRICTION);
try {
Predicate restriction = query.getRestriction();
if (restriction != null) {
jpqlBuilder.append(" where ");
this.visit(restriction);
}
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.GROUP_LIST);
try {
List> groupList = query.getGroupList();
if (!groupList.isEmpty()) {
jpqlBuilder.append(" group by ");
addComma = false;
for (Expression> expr : groupList) {
if (addComma) {
jpqlBuilder.append(", ");
} else {
addComma = true;
}
this.visit(expr);
}
}
} finally {
this.pathIdAllocator.pop();
}
this.pathIdAllocator.push(PathId.Allocator.GROUP_RESTRICTION);
try {
Predicate groupRestriction = query.getGroupRestriction();
if (groupRestriction != null) {
jpqlBuilder.append(" having ");
this.visit(groupRestriction);
}
} finally {
this.pathIdAllocator.pop();
}
}
protected void renderJoin(Entity entity) {
if (!entity.isUsed()) {
return;
}
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (entity.getAttribute() != null) {
jpqlBuilder
.append(' ')
.append(entity.getJoinType().name().toLowerCase())
.append(" join ");
if (entity.isFetch()) {
jpqlBuilder.append("fetch ");
}
jpqlBuilder
.append(entity.getParent().getRenderAlias())
.append('.')
.append(entity.getAttribute().getName())
.append(' ')
.append(entity.getRenderAlias());
Predicate on = entity.getOn();
if (on != null) {
jpqlBuilder.append(" on ");
this.visit(entity.getOn());
}
}
for (Entity childEntity : entity.getEntities()) {
this.renderJoin(childEntity);
}
}
protected void renderPathNode(PathNode pathNode) {
Class> treatAsType = pathNode.getTreatAsType();
if (treatAsType != null) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
jpqlBuilder.append("treat(");
this.renderNonTreatedPathNode(pathNode);
jpqlBuilder.append(" as ");
jpqlBuilder.append(treatAsType.getName());
jpqlBuilder.append(')');
} else {
this.renderNonTreatedPathNode(pathNode);
}
}
protected void renderNonTreatedPathNode(PathNode pathNode) {
if (pathNode.getEntity() != null) {
this.jpqlBuilder.append(pathNode.getEntity().getRenderAlias());
} else {
this.renderPathNode(pathNode.getParent());
this
.jpqlBuilder
.append('.')
.append(pathNode.getAttribute().getName());
}
}
protected void renderConstant(Object constant) {
StringBuilder jpqlBuilder = this.jpqlBuilder;
if (constant instanceof String) {
String str = (String)constant;
constant = str.replaceAll("'", "''");
jpqlBuilder.append('\'').append(constant).append('\'');
} else if (constant instanceof Character) {
char ch = (Character)constant;
if (ch == '\'') {
jpqlBuilder.append("''''");
} else {
jpqlBuilder.append('\'').append(constant).append('\'');
}
} else {
jpqlBuilder.append(constant);
if (constant instanceof Long) {
jpqlBuilder.append('L');
}
if (constant instanceof Float) {
jpqlBuilder.append('F');
}
if (constant instanceof Double) {
jpqlBuilder.append('D');
}
}
}
protected LiteralParameter getLiteralParameter(Object object) {
XOrderedMap literalParameters = this.literalParameters;
if (literalParameters == null) {
this.literalParameters = literalParameters = new LinkedHashMap<>();
}
LiteralParameter literalParameter = literalParameters.get(object);
if (literalParameter == null) {
if (object instanceof LiteralExpression>) {
literalParameter = new LiteralParameter(literalParameters.size(),
((LiteralExpression>)object).getValue());
} else if (object instanceof Partition>) {
Collection c = new ArrayList<>();
for (Expression> value : ((Partition>)object).getValues()) {
if (value instanceof LiteralExpression>) {
c.add(((LiteralExpression>)value).getValue());
} else {
c.add(((ConstantExpression>)value).getValue());
}
}
literalParameter = new LiteralParameter(literalParameters.size(), c);
} else {
Arguments.mustBeInstanceOfAnyOfValue("object", object, LiteralExpression.class, Partition.class);
}
literalParameters.put(object, literalParameter);
}
return literalParameter;
}
protected void visit(Object o) {
if (o != null) {
Object parent = this.pathIdAllocator.peek();
this.pathIdAllocator.push(o);
try {
if (o instanceof AbstractExpression>) {
AbstractExpression> abstractExpression = (AbstractExpression>)o;
int priority = abstractExpression.getPriority();
if (priority >= this.currentPriority) {
int oldPriority = this.currentPriority;
this.currentPriority = priority;
try {
abstractExpression.accept(this);
} finally {
this.currentPriority = oldPriority;
}
} else {
int oldPriority = this.currentPriority;
this.currentPriority = priority;
try {
this.jpqlBuilder.append('(');
abstractExpression.accept(this);
this.jpqlBuilder.append(')');
} finally {
this.currentPriority = oldPriority;
}
}
} else {
((AbstractNode)o).accept(this);
}
if (o instanceof TupleElement && !(o instanceof CompoundSelection)) {
String alias = ((TupleElement>)o).getAlias();
if (!Nulls.isNullOrEmpty(alias) && !(o instanceof From, ?>)) {
if (parent == PathId.Allocator.SELECTION || parent instanceof CompoundSelection>) {
this.jpqlBuilder.append(" as ").append(alias);
}
}
}
} finally {
this.pathIdAllocator.pop();
}
}
}
}
private static class TupleResultTransformer implements ResultTransformer {
private static final long serialVersionUID = -2248010661387946493L;
private CompoundSelection tupleSelection;
public TupleResultTransformer(CompoundSelection tupleSelection) {
this.tupleSelection = tupleSelection;
}
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
return new TupleImpl(this.tupleSelection, tuple);
}
@SuppressWarnings("rawtypes")
@Override
public List transformList(List collection) {
return collection;
}
}
private static class TupleImpl implements Tuple, Serializable {
private static final long serialVersionUID = 9017189381422990789L;
private static final LazyResource LAZY_RESOURCE = LazyResource.of(Resource.class);
private Object[] values;
private String[] aliases;
private String[] nonNullAliases;
private int size;
private transient TupleElement>[] tupleElements;
public TupleImpl(CompoundSelection tupleSelection, Object[] values) {
this.size = tupleSelection.getCompoundSelectionItems().size();
TupleElement>[] tupleElements = tupleSelection.getCompoundSelectionItems().toArray(new TupleElement[this.size]);
String[] aliases = new String[this.size];
Collection nonNullList = new ArrayList<>(this.size);
for (int i = this.size - 1; i >= 0; i--) {
String alias = tupleElements[i].getAlias();
aliases[i] = alias;
if (alias != null) {
nonNullList.add(alias);
}
}
this.aliases = aliases;
this.nonNullAliases = nonNullList.toArray(new String[nonNullList.size()]);
this.tupleElements = tupleElements;
this.values = values;
}
@SuppressWarnings("unchecked")
@Override
public X get(TupleElement tupleElement) {
int size = this.size;
TupleElement>[] tupleElements = this.tupleElements;
if (tupleElements != null) {
for (int i = 0; i < size; i++) {
if (tupleElements[i] == tupleElement) {
return (X)this.values[i];
}
}
}
String alias = tupleElement.getAlias();
if (alias != null) {
String[] aliases = this.aliases;
for (int i = 0; i < size; i++) {
if (alias.equals(aliases[i])) {
return (X)this.values[i];
}
}
}
throw new IllegalArgumentException(LAZY_RESOURCE.get().missTupleElementOfThisIsDeserialized());
}
@Override
public Object get(String alias) {
if (alias != null) {
int size = this.size;
String[] aliases = this.aliases;
for (int i = 0; i < size; i++) {
if (alias.equals(aliases[i])) {
return this.values[i];
}
}
Arguments.mustBeAnyOfValue("alias", alias, this.nonNullAliases);
}
Arguments.mustNotBeNull("alias", alias);
throw new AssertionError(/* impossible */);
}
@Override
public Object get(int index) {
try {
return this.values[index];
} catch (ArrayIndexOutOfBoundsException ex) {
Arguments.indexMustBetweenValue("index", index, 0, true, this.values.length, false);
throw new AssertionError(/* impossible */);
}
}
@SuppressWarnings("unchecked")
@Override
public X get(String alias, Class type) {
return (X)this.get(alias);
}
@SuppressWarnings("unchecked")
@Override
public X get(int index, Class type) {
return (X)this.get(index);
}
@Override
public List> getElements() {
if (this.tupleElements == null) {
throw new IllegalStateException(LAZY_RESOURCE.get().thisIsDeserialized());
}
return MACollections.wrap(this.tupleElements);
}
@Override
public Object[] toArray() {
return this.values;
}
private interface Resource {
String missTupleElementOfThisIsDeserialized();
String thisIsDeserialized();
}
}
}