org.babyfish.hibernate.hql.XQueryTranslatorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of babyfish-hibernate Show documentation
Show all versions of babyfish-hibernate Show documentation
The hibernate enhancement project of BabyFish.
The newest version!
/*
* 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.hql;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.Tuple;
import org.babyfish.collection.ArrayList;
import org.babyfish.collection.HashMap;
import org.babyfish.collection.HashSet;
import org.babyfish.collection.LinkedHashMap;
import org.babyfish.collection.LinkedHashSet;
import org.babyfish.collection.ReferenceEqualityComparator;
import org.babyfish.collection.XOrderedMap;
import org.babyfish.collection.XOrderedSet;
import org.babyfish.hibernate.cfg.SettingsFactory;
import org.babyfish.hibernate.dialect.DistinctLimitDialect;
import org.babyfish.hibernate.dialect.Oracle10gDialect;
import org.babyfish.hibernate.hql.HqlASTHelper.AliasGenerator;
import org.babyfish.hibernate.internal.AbstractHibernatePathPlanFactory;
import org.babyfish.hibernate.loader.DistinctLimitQueryLoader;
import org.babyfish.hibernate.loader.UnlimitedCountLoader;
import org.babyfish.hibernate.model.loader.HibernateObjectModelScalarLoader;
import org.babyfish.lang.IllegalProgramException;
import org.babyfish.lang.UncheckedException;
import org.babyfish.lang.reflect.asm.ASM;
import org.babyfish.lang.reflect.asm.ClassEnhancer;
import org.babyfish.model.ObjectModel;
import org.babyfish.model.ScalarBatchLoader;
import org.babyfish.org.objectweb.asm.Label;
import org.babyfish.org.objectweb.asm.Opcodes;
import org.babyfish.org.objectweb.asm.tree.AbstractInsnNode;
import org.babyfish.org.objectweb.asm.tree.FieldInsnNode;
import org.babyfish.org.objectweb.asm.tree.InsnList;
import org.babyfish.org.objectweb.asm.tree.InsnNode;
import org.babyfish.org.objectweb.asm.tree.JumpInsnNode;
import org.babyfish.org.objectweb.asm.tree.LabelNode;
import org.babyfish.org.objectweb.asm.tree.LdcInsnNode;
import org.babyfish.org.objectweb.asm.tree.MethodInsnNode;
import org.babyfish.org.objectweb.asm.tree.MethodNode;
import org.babyfish.org.objectweb.asm.tree.TypeInsnNode;
import org.babyfish.org.objectweb.asm.tree.VarInsnNode;
import org.babyfish.persistence.Constants;
import org.babyfish.persistence.QueryType;
import org.babyfish.persistence.model.metadata.JPAObjectModelMetadata;
import org.babyfish.persistence.path.spi.JoinNode;
import org.babyfish.persistence.path.spi.OrderNode;
import org.babyfish.persistence.path.spi.PathPlan;
import org.babyfish.persistence.path.spi.PathPlanKey;
import org.babyfish.persistence.path.spi.SubPlan;
import org.babyfish.util.LazyResource;
import org.hibernate.Filter;
import org.hibernate.QueryException;
import org.hibernate.bytecode.instrumentation.internal.FieldInterceptionHelper;
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.query.spi.EntityGraphQueryHint;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.hql.internal.antlr.HqlTokenTypes;
import org.hibernate.hql.internal.ast.HqlParser;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.internal.ast.QueryTranslatorImpl;
import org.hibernate.hql.internal.ast.SqlGenerator;
import org.hibernate.hql.internal.ast.tree.FromElement;
import org.hibernate.hql.internal.ast.tree.QueryNode;
import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.loader.hql.QueryLoader;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.AssociationType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import antlr.collections.AST;
/**
* @author Tao Chen
*/
public abstract class XQueryTranslatorImpl
extends QueryTranslatorImpl
implements XQueryTranslator, XFilterTranslator {
private static final Logger LOGGER = LoggerFactory.getLogger(XQueryTranslatorImpl.class);
private static final LazyResource LAZY_RESOURCE = LazyResource.of(Resource.class);
private static final Constructor CONSTRUCTOR;
private static final Method PARSE_METHOD;
/*
* These fields is not private because they may be access by the dynamic derived class.
*/
SessionFactoryImplementor factory;
String hql;
PathPlanKey pathPlanKey;
PathPlan pathPlan;
int queryType; //Hibernate's queryType: SELECT, INSERT, UPDATE, DELETE; not babyfish's queryType: DISTINCT, RESULT
Map startFromElementASTMap;
Map returnedEntityColumns;
String countSql;
AST countSqlAst;
String distinctCountSql;
AST distinctCountSqlAst;
QueryLoader queryLoader;
DistinctLimitQueryLoader distinctLimitQueryLoader;
UnlimitedCountLoader countLoader;
UnlimitedCountLoader distinctCountLoader;
ExceptionCreator unlimitedCountExceptionCreator;
boolean usingQueryPaths;
protected XQueryTranslatorImpl(
String queryIdentifier,
String query,
PathPlanKey pathPlanKey,
Map enabledFilters,
SessionFactoryImplementor factory,
EntityGraphQueryHint entityGraphQueryHint) {
super(queryIdentifier, query, enabledFilters, factory);
this.factory = factory;
this.hql = query;
this.pathPlanKey = pathPlanKey;
this.usingQueryPaths = pathPlanKey != null;
}
public static XQueryTranslatorImpl newInstance(
String queryIdentifier,
String queryString,
PathPlanKey pathPlanKey,
Map filters,
SessionFactoryImplementor factory,
EntityGraphQueryHint entityGraphQueryHint) {
try {
return CONSTRUCTOR.newInstance(
new Object[] {
queryIdentifier,
queryString,
pathPlanKey,
filters,
factory,
entityGraphQueryHint
}
);
} catch (InstantiationException ex) {
throw new AssertionError();
} catch (IllegalAccessException ex) {
throw UncheckedException.rethrow(ex);
} catch (InvocationTargetException ex) {
throw UncheckedException.rethrow(ex.getTargetException());
}
}
@Override
public String getCountSQLString() {
return this.countSql;
}
@Override
public String getDistinctCountSQLString() {
return this.distinctCountSql;
}
@Override
public final PathPlan getPathPlan() {
return this.pathPlan;
}
@Override
public long unlimitedCount(
SessionImplementor session,
QueryParameters queryParameters,
QueryType queryType) {
if (this.unlimitedCountExceptionCreator != null) {
throw this.unlimitedCountExceptionCreator.create();
}
UnlimitedCountLoader loader;
if (queryType == QueryType.DISTINCT && this.distinctCountLoader != null) {
loader = this.distinctCountLoader;
} else {
loader = this.countLoader;
}
if (loader == null) {
throw new QueryException(LAZY_RESOURCE.get().operationRequiresQuery("unlimitedCount"));
}
List> list = loader.list(session, queryParameters);
Object o = list.iterator().next();
if (o.getClass().isArray()) {
o = ((Object[])o)[0];
}
if (o instanceof Long) {
return (Long)o;
}
if (o instanceof Integer) {
return (Integer)o;
}
return Long.parseLong(o.toString());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List list(SessionImplementor session,
QueryParameters queryParameters,
QueryType queryType) {
if (queryType == QueryType.DISTINCT) {
List tmp;
if (this.distinctLimitQueryLoader != null) {
tmp = (List)this.distinctLimitQueryLoader.list(session, queryParameters);
} else {
boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits();
if (hasLimit && this.containsCollectionFetches()) {
if (!SettingsFactory.isLimitInMemoryEnabled(session.getFactory().getProperties())) {
throw new QueryException(
LAZY_RESOURCE.get().hibernateLimitInMemoryForCollectionFetchIsNotEnabled(
DistinctLimitDialect.class,
Oracle10gDialect.class,
SettingsFactory.ENABLE_LIMIT_IN_MEMORY
)
);
}
}
tmp = this.list(session, queryParameters);
}
Set distinction = new LinkedHashSet(ReferenceEqualityComparator.getInstance());
distinction.addAll(tmp);
List results = new ArrayList(distinction.size());
results.addAll(distinction);
this.applyScalarEagerness(results, session);
return results;
} else {
return this.queryLoader.list(session, queryParameters);
}
}
protected final SessionFactoryImplementor getFactory() {
return this.factory;
}
protected final boolean isUsingQueryPaths() {
return this.usingQueryPaths;
}
//It is invoked by the byte code generated runtime.
//It is always called before
//applyRootJoinNodeForCount and applyRootJoinNodeForDistinctCount
final void applyRootJoinNode(AST ast) {
this.queryType = ast.getType();
this.initialize(ast);
if (ast.getType() == HqlTokenTypes.QUERY) {
logAST(ast, "Before apply query paths for generic query, the HQL-AST is");
this.onApplyRootJoinNode(ast);
this.preApplyScalarEagerness(ast);
logAST(ast, "After apply query paths for generic query, the HQL-AST is");
}
}
//It is invoked by the byte code generated runtime.
final void applyRootJoinNodeForCount(AST ast) {
this.initialize(ast);
if (ast.getType() == HqlTokenTypes.QUERY && this.unlimitedCountExceptionCreator == null) {
this.unlimitedCountExceptionCreator = this.getUnlimitedCountExceptionCreator(ast);
if (this.unlimitedCountExceptionCreator == null) {
logAST(ast, "Before apply query paths for count query, the HQL-AST is");
this.onApplyRootJoinNodeForCount(ast, false);
logAST(ast, "After apply query paths for count query, the HQL-AST is");
}
}
}
//It is invoked by the byte code generated runtime.
final void applyRootJoinNodeForDistinctCount(AST ast) {
this.initialize(ast);
if (ast.getType() == HqlTokenTypes.QUERY && this.unlimitedCountExceptionCreator == null) {
this.unlimitedCountExceptionCreator = this.getUnlimitedCountExceptionCreator(ast);
if (this.unlimitedCountExceptionCreator == null) {
logAST(ast, "Before apply query paths for distinct count query, the HQL-AST is");
this.onApplyRootJoinNodeForCount(ast, true);
logAST(ast, "After apply query paths for distinct count query, the HQL-AST is");
}
}
}
//It is invoked by the byte code generated runtime.
final void processCountSqlAst(AST countSqlAST) {
logAST(countSqlAST, "Before SQL-AST processing for count query, the SQL-AST is");
this.onProcessCountSqlAST(countSqlAST, false);
logAST(countSqlAST, "After SQL-AST processing for count query, the SQL-AST is");
}
//It is invoked by the byte code generated runtime.
final void processDistinctCountSqlAst(AST distinctCountSqlAST) {
logAST(distinctCountSqlAST, "Before SQL-AST processing for distinct count query, the SQL-AST is");
this.onProcessCountSqlAST(distinctCountSqlAST, true);
logAST(distinctCountSqlAST, "Before SQL-AST processing for distinct count query, the SQL-AST is");
}
@SuppressWarnings("unchecked")
protected boolean shouldUseDistinctQuery() {
if (this.pathPlan.containsCollectionJoins()) {
return true;
}
QueryNode queryNode = (QueryNode)this.getSqlAST();
boolean foundFrom = false;
for (FromElement fromElement : (List)queryNode.getFromClause().getFromElements()) {
if (fromElement.getClass() == FromElement.class ) {
if (!fromElement.getText().contains(" join ")) {
if (foundFrom) {
return true;
}
foundFrom = true;
} else if (fromElement.getText().startsWith("cross ")) {
return true;
} else if (fromElement.getQueryableCollection() != null) {
return true;
}
}
}
return false;
}
@SuppressWarnings("unchecked")
protected boolean shouldUseDistinctCount() {
if (this.pathPlan.containsCollectionInnerJoins()) {
return true;
}
QueryNode queryNode = (QueryNode)this.getSqlAST();
Set collectionLeftJoins = new HashSet<>(ReferenceEqualityComparator.getInstance());
boolean foundFrom = false;
for (FromElement fromElement : (List)queryNode.getFromClause().getFromElements()) {
if (fromElement.getClass() == FromElement.class ) {
if (!fromElement.getText().contains(" join ")) {
if (foundFrom) {
return true;
}
foundFrom = true;
} else if (fromElement.getText().startsWith("cross ")) {
return true;
} else if (fromElement.getQueryableCollection() != null) {
if (!fromElement.getText().startsWith("left ")) {
return true;
}
if (fromElement.getWithClauseFragment() != null) {
return true;
}
collectionLeftJoins.add(fromElement);
}
}
}
return SqlASTHelper.findJoinReferenceInWhereCaluse(queryNode, collectionLeftJoins);
}
protected void onApplyRootJoinNode(AST ast) {
AliasGenerator aliasGenerator = new AliasGenerator();
AST selectFromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT_FROM);
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.FROM);
for (SubPlan subPlan : this.pathPlan.getSubPlans().values()) {
Map fromElementASTMap = new HashMap<>(
ReferenceEqualityComparator.getInstance(),
ReferenceEqualityComparator.getInstance());
AST startFromElementAst = this.startFromElementASTMap.get(subPlan.getAlias());
fromElementASTMap.put(subPlan.getJoinNode(), startFromElementAst);
if (!subPlan.getJoinNode().getChildNodes().isEmpty()) {
for (JoinNode joinNode : subPlan.getJoinNode().getChildNodes().values()) {
HqlASTHelper.addJoinAST(
fromAst,
startFromElementAst,
joinNode,
aliasGenerator,
true,
false,
false,
fromElementASTMap);
}
}
List preOrderNodes = subPlan.getPreOrderNodes();
List postOrderNodes = subPlan.getPostOrderNodes();
if (!preOrderNodes.isEmpty() || !postOrderNodes.isEmpty()) {
AST orderAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.ORDER);
if (orderAst == null) {
orderAst = HqlASTHelper.createAST(HqlTokenTypes.ORDER, "order");
ast.addChild(orderAst);
} else {
HqlASTHelper.removeChildOrdersInToppestQuery(orderAst, preOrderNodes, fromElementASTMap);
HqlASTHelper.removeChildOrdersInToppestQuery(orderAst, postOrderNodes, fromElementASTMap);
}
for (int i = preOrderNodes.size() - 1; i >= 0; i--) {
OrderNode preOrderNode = preOrderNodes.get(i);
JoinNode joinNode = preOrderNode.getParentNode();
AST fromElementAST = fromElementASTMap.get(joinNode);
String alias = HqlASTHelper.getAlias(fromElementAST);
if (alias == null) {
alias = aliasGenerator.generateAlias();
HqlASTHelper.setAlias(fromElementAST, alias);
}
AST orderFieldAST = HqlASTHelper.createOrderFieldAST(alias, preOrderNode);
AST orderTypeAST = preOrderNode.isDesc() ?
HqlASTHelper.createAST(HqlTokenTypes.DESCENDING, "desc") :
HqlASTHelper.createAST(HqlTokenTypes.ASCENDING, "asc");
orderTypeAST.setNextSibling(orderAst.getFirstChild());
orderFieldAST.setNextSibling(orderTypeAST);
orderAst.setFirstChild(orderFieldAST);
}
for (OrderNode postOrderNode : postOrderNodes) {
JoinNode joinNode = postOrderNode.getParentNode();
AST fromElementAST = fromElementASTMap.get(joinNode);
String alias = HqlASTHelper.getAlias(fromElementAST);
if (alias == null) {
alias = aliasGenerator.generateAlias();
HqlASTHelper.setAlias(fromElementAST, alias);
}
AST orderFieldAST = HqlASTHelper.createOrderFieldAST(alias, postOrderNode);
AST orderTypeAST = postOrderNode.isDesc() ?
HqlASTHelper.createAST(HqlTokenTypes.DESCENDING, "desc") :
HqlASTHelper.createAST(HqlTokenTypes.ASCENDING, "asc");
orderAst.addChild(orderFieldAST);
orderAst.addChild(orderTypeAST);
}
}
}
}
protected void onApplyRootJoinNodeForCount(AST ast, boolean distinct) {
AST orderAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.ORDER);
if (orderAst != null) {
HqlASTHelper.removeOrderByInToppestQuery(ast);
orderAst = null;
}
AST selectFromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT_FROM);
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.FROM);
AST selectAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.SELECT);
AST defaultRangeAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.RANGE);
AST defaultRangeAliasAst = HqlASTHelper.findFirstChildInToppestQuery(defaultRangeAst, HqlTokenTypes.ALIAS);
AliasGenerator aliasGenerator = new AliasGenerator();
if (defaultRangeAliasAst == null) {
defaultRangeAliasAst = HqlASTHelper.createAST(HqlTokenTypes.ALIAS, aliasGenerator.generateAlias());
defaultRangeAst.addChild(defaultRangeAliasAst);
}
AST countAst =
HqlASTHelper.createAST(
HqlTokenTypes.COUNT,
"count",
distinct && this.shouldUseDistinctCount() ? HqlASTHelper.createAST(HqlTokenTypes.DISTINCT, "distinct") : null,
HqlASTHelper.createAST(HqlTokenTypes.IDENT, defaultRangeAliasAst.getText()));
if (selectAst == null) {
selectAst = HqlASTHelper.createAST(HqlTokenTypes.SELECT, "select", countAst);
selectFromAst.addChild(selectAst);
} else {
selectAst.setFirstChild(countAst);
}
for (SubPlan subPlan : this.pathPlan.getSubPlans().values()) {
AST startFromElementAst = this.startFromElementASTMap.get(subPlan.getAlias());
//Must add join ASTs before unset fetch, because whether the original join ASTs
//is with fetches is very important to find out how to add new join ASTs.
for (JoinNode joinNode : subPlan.getJoinNode().getChildNodes().values()) {
HqlASTHelper.addJoinAST(
fromAst,
startFromElementAst,
joinNode,
aliasGenerator,
false,
true,
distinct,
null);
}
}
//After join ASTs, unset fetch of all the joinAST
for (AST fromElementAst = fromAst.getFirstChild();
fromElementAst != null;
fromElementAst = fromElementAst.getNextSibling()) {
if (fromElementAst.getType() == HqlTokenTypes.JOIN) {
HqlASTHelper.unsetFetch(fromElementAst);
}
}
}
@SuppressWarnings("unchecked")
protected void onProcessCountSqlAST(AST sqlAst, boolean distinct) {
QueryNode queryNode = (QueryNode)sqlAst;
Set usedFromElements = new HashSet<>(ReferenceEqualityComparator.getInstance());
for (FromElement fromElement : (List)queryNode.getFromClause().getFromElements()) {
if (fromElement.getClass() == FromElement.class && fromElement.getWithClauseFragment() == null) {
if (distinct || fromElement.getQueryableCollection() == null) {
if (fromElement.getText().startsWith("left ")) {
continue;
} else if (fromElement.getRole() != null &&
fromElement.getQueryableCollection() == null &&
SettingsFactory.isDbSchemaStrict(this.factory.getProperties())) {
String role = fromElement.getRole();
int lastDotIndex = role.lastIndexOf('.');
String entityName = role.substring(0, lastDotIndex);
String propertyName = role.substring(lastDotIndex + 1);
EntityMetamodel entityMetamodel = this.factory.getEntityPersister(entityName).getEntityMetamodel();
int propertyIndex = entityMetamodel.getPropertyIndex(propertyName);
if (entityMetamodel.getPropertyTypes()[propertyIndex] instanceof ManyToOneType) {
boolean nullable = entityMetamodel.getPropertyNullability()[propertyIndex];
if (!nullable) {
continue;
}
}
}
}
}
SqlASTHelper.addFromElementAndancestors(usedFromElements, fromElement);
}
SqlASTHelper.removeFromElementsExcept(queryNode, usedFromElements);
}
private void initialize(AST ast) {
if (this.pathPlan == null) {
if (ast.getType() != HqlTokenTypes.QUERY) {
if (this.pathPlanKey != null) {
throw new QueryException(LAZY_RESOURCE.get().queryPathsCanNotBeApplyToNonQuery(this.hql));
}
} else {
this.pathPlan = new PathPlanFactoryImpl(this.factory, ast).create(this.pathPlanKey);
if (HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT) == null &&
this.pathPlan.containsNoFetchJoins()) {
throw new QueryException(
LAZY_RESOURCE.get().hqlMustContainsSelectCaluseWhenThereIsNoFetchJoinInQueryPaths(this.hql)
);
}
this.startFromElementASTMap = this.createStartFromElementASTMap(ast);
}
}
}
private ExceptionCreator getUnlimitedCountExceptionCreator(AST ast) {
final String hql = this.hql;
AST groupAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.GROUP);
if (groupAst != null) {
return new ExceptionCreator() {
@Override
public RuntimeException create() {
return new QueryException(
LAZY_RESOURCE.get().unlimitedCountIsUnsupportedBecauseOfGroupBy(hql)
);
}
};
}
AST selectFromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT_FROM);
AST selectAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.SELECT);
if (selectAst != null) {
AST selectionAst = selectAst.getFirstChild();
if (selectionAst.getNextSibling() != null) {
return new ExceptionCreator() {
@Override
public RuntimeException create() {
return new QueryException(
LAZY_RESOURCE.get().unlimitedCountIsUnsupportedBecauseOfTooManySelections(hql)
);
}
};
}
boolean selectRootEntity = false;
if (selectionAst.getType() == HqlTokenTypes.IDENT) {
AST firstRangeAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.RANGE);
String alias = HqlASTHelper.getAlias(firstRangeAst);
selectRootEntity = selectionAst.getText().equals(alias);
}
if (!selectRootEntity) {
return new ExceptionCreator() {
@Override
public RuntimeException create() {
return new QueryException(
LAZY_RESOURCE.get().unlimitedCountIsUnsupportedBecauseSelectionIsNotRootEntity(hql)
);
}
};
}
} else {
boolean metRange = false;
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.FROM);
for (AST fromElementAst = fromAst.getFirstChild();
fromElementAst != null;
fromElementAst = fromElementAst.getNextSibling()) {
if (fromElementAst.getType() == HqlTokenTypes.RANGE) {
if (metRange) {
return new ExceptionCreator() {
@Override
public RuntimeException create() {
return new QueryException(
LAZY_RESOURCE.get().unlimitedCountIsUnsupportedBecauseOfTooManyRangeAndNoSelection(hql)
);
}
};
}
metRange = true;
} else {
AST fetchAst = HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.FETCH);
if (fetchAst == null) {
return new ExceptionCreator() {
@Override
public RuntimeException create() {
return new QueryException(
LAZY_RESOURCE.get().unlimitedCountIsUnsupportedBecauseOfNonFetchJoinsAndNoSelection(hql)
);
}
};
}
}
}
}
return null;
}
private void preApplyScalarEagerness(AST ast) {
if (!this.pathPlan.containsScalarEagerness()) {
return;
}
this.returnedEntityColumns = new LinkedHashMap<>();
AST selectAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT);
if (selectAst != null) {
for (SubPlan subPlan : this.pathPlan.getSubPlans().values()) {
if (subPlan.getJoinNode().containsScalarEagerness()) {
String subPlanAlias = subPlan.getAlias();
int column = -1;
int index = 0;
for (AST childAst = selectAst.getFirstChild(); childAst != null; childAst = childAst.getNextSibling()) {
if (subPlanAlias == null) {
column = index;
break;
}
if (childAst.getType() == HqlTokenTypes.IDENT &&
(subPlanAlias == null || childAst.getText().equals(subPlanAlias))) {
column = index;
break;
}
index++;
}
if (column == -1) {
throw new IllegalArgumentException();
}
this.returnedEntityColumns.put(subPlanAlias, column);
}
}
} else {
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.FROM);
XOrderedMap allEntityMap = new LinkedHashMap<>();
int index = 0;
for (AST fromElementAst = fromAst.getFirstChild(); fromElementAst != null; fromElementAst = fromElementAst.getNextSibling()) {
int type = fromElementAst.getType();
if (type == HqlTokenTypes.JOIN) {
if (HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.FETCH) != null) {
index++;
continue;
}
}
String fromElementAlias = HqlASTHelper.getAlias(fromElementAst);
if (!allEntityMap.containsKey(fromElementAlias)) {
allEntityMap.put(fromElementAlias, index);
}
index++;
}
for (SubPlan subPlan : this.pathPlan.getSubPlans().values()) {
if (subPlan.getJoinNode().containsScalarEagerness()) {
String subPlanAlias = subPlan.getAlias();
Integer column;
if (subPlanAlias != null) {
column = allEntityMap.get(subPlanAlias);
if (column == null) {
throw new IllegalArgumentException();
}
} else {
column = allEntityMap.firstEntry().getValue();
}
this.returnedEntityColumns.put(subPlanAlias, column);
}
}
}
}
private void applyScalarEagerness(List> results, SessionImplementor session) {
if (this.returnedEntityColumns == null) {
return;
}
ScalarEagerness scalarEagerness = new ScalarEagerness(session);
for (Entry entry : this.returnedEntityColumns.entrySet()) {
for (Object result : results) {
if (result == null) {
continue;
}
Object entity;
if (result.getClass().isArray()) {
Object[] arr = (Object[])result;
entity = arr[entry.getValue()];
} else if (result instanceof Tuple) {
Tuple tuple = (Tuple)result;
entity = tuple.get(entry.getValue());
} else {
if (entry.getValue() != 0) {
throw new AssertionError();
}
entity = result;
}
JoinNode joinNode = this.pathPlan.getSubPlans().get(entry.getKey()).getJoinNode();
scalarEagerness.prepareApply(entity, joinNode);
}
}
scalarEagerness.apply();
}
private Map createStartFromElementASTMap(AST ast) {
AST selectFromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT_FROM);
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.FROM);
AST defaultRangeAst = HqlASTHelper.findFirstChildInToppestQuery(fromAst, HqlTokenTypes.RANGE);
Map map = new HashMap<>();
map.put(null, defaultRangeAst);
for (SubPlan subPlan : this.pathPlan.getSubPlans().values()) {
String alias = subPlan.getAlias();
if (alias != null) {
AST matchedFromElementAst = null;
for (AST fromElementAst = fromAst.getFirstChild(); fromElementAst != null; fromElementAst = fromElementAst.getNextSibling()) {
AST feaAst = HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.ALIAS);
String feaAlias = feaAst != null ? feaAst.getText() : null;
if (feaAlias.startsWith(Constants.NOT_SHARED_JOIN_ALIAS_PREFIX)) {
feaAlias = feaAlias.substring(Constants.NOT_SHARED_JOIN_ALIAS_PREFIX.length());
}
if (alias.equals(feaAlias)) {
matchedFromElementAst = fromElementAst;
break;
}
}
if (matchedFromElementAst == null) {
throw new IllegalArgumentException(
LAZY_RESOURCE.get().illegalSubPathAlias(
alias,
Constants.NOT_SHARED_JOIN_ALIAS_PREFIX + alias,
hql
)
);
}
map.put(alias, matchedFromElementAst);
}
}
return map;
}
private static void logAST(AST ast, String message) {
if (LOGGER.isDebugEnabled()) {
ASTPrinter printer = new ASTPrinter(HqlTokenTypes.class);
String astText = printer.showAsString(ast, message);
LOGGER.debug(astText);
}
}
private static class Enhancer extends ClassEnhancer {
private static final Enhancer INSTANCE = getInstance(Enhancer.class);
private int walkerVarInDoCompile;
private Enhancer() {
super(XQueryTranslatorImpl.class);
}
static Class getEhancedClass() {
return INSTANCE.getResultClass();
}
@Override
protected void doMethodFilter(MethodSource methodSource) {
Method method = methodSource.getMethod();
if (method.getDeclaringClass() == QueryTranslatorImpl.class &&
method.getName().equals("parse") &&
method.getReturnType() == HqlParser.class) {
InsnList instructions = methodSource.getInstructions();
for (AbstractInsnNode abstractInsnNode = instructions.getFirst();
abstractInsnNode != null;
abstractInsnNode = abstractInsnNode.getNext()) {
if (abstractInsnNode.getOpcode() == Opcodes.ARETURN) {
InsnList tmpInstructions = new InsnList();
tmpInstructions.add(new InsnNode(Opcodes.DUP));
tmpInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
tmpInstructions.add(new InsnNode(Opcodes.SWAP));
tmpInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(HqlParser.class),
"getAST",
"()" + ASM.getDescriptor(AST.class),
false));
tmpInstructions.add(
new MethodInsnNode(
Opcodes.INVOKESPECIAL,
this.getResultInternalName(),
"applyRootJoinNode",
"(" +
ASM.getDescriptor(AST.class) +
")V",
false));
instructions.insertBefore(abstractInsnNode, tmpInstructions);
}
}
} else if (method.getDeclaringClass() == QueryTranslatorImpl.class &&
method.getName().equals("doCompile") &&
Arrays.equals(method.getParameterTypes(), new Class[] { Map.class, boolean.class, String.class })) {
InsnList instructions = methodSource.getInstructions();
for (AbstractInsnNode abstractInsnNode = instructions.getFirst();
abstractInsnNode != null;
abstractInsnNode = abstractInsnNode.getNext()) {
if (abstractInsnNode.getOpcode() == Opcodes.PUTFIELD) {
FieldInsnNode fieldInsnNode = (FieldInsnNode)abstractInsnNode;
if (fieldInsnNode.name.equals("queryLoader") &&
fieldInsnNode.owner.equals(ASM.getInternalName(QueryTranslatorImpl.class))) {
InsnList beforeInstructions = new InsnList();
beforeInstructions.add(new InsnNode(Opcodes.DUP));
beforeInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
beforeInstructions.add(new InsnNode(Opcodes.SWAP));
beforeInstructions.add(
new FieldInsnNode(
Opcodes.PUTFIELD,
ASM.getInternalName(XQueryTranslatorImpl.class),
"queryLoader",
ASM.getDescriptor(QueryLoader.class)));
instructions.insertBefore(fieldInsnNode, beforeInstructions);
InsnList afterInstructions = new InsnList();
/*
* if (this.queryType == QUERY) {
*/
LabelNode isNotQueryLabelNode = new LabelNode();
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(new FieldInsnNode(
Opcodes.GETFIELD,
ASM.getInternalName(XQueryTranslatorImpl.class),
"queryType",
"I"));
afterInstructions.add(new LdcInsnNode(HqlTokenTypes.QUERY));
afterInstructions.add(new JumpInsnNode(Opcodes.IF_ICMPNE, isNotQueryLabelNode));
/*
* this.compileForCount();
*/
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 3));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKESPECIAL,
this.getResultInternalName(),
"compileForCount",
"(Ljava/lang/String;)V",
false));
/*
* this.compileForDistinctCount();
*/
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 3));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKESPECIAL,
this.getResultInternalName(),
"compileForDistinctCount",
"(Ljava/lang/String;)V",
false));
/*
* if (this.shouldUseDistinctQuery() && this.factory.getDialect() instanceof DistinctLimitDialect) {
* this.distinctQueryLoader = new DistinctLimitQueryLoader(this, this.factory, walker.getSelectClause());
* }
*/
LabelNode endIfNode = new LabelNode(new Label());
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
this.getResultInternalName(),
"shouldUseDistinctQuery",
"()Z",
false));
afterInstructions.add(new JumpInsnNode(Opcodes.IFEQ, endIfNode));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(
new FieldInsnNode(
Opcodes.GETFIELD,
ASM.getInternalName(XQueryTranslatorImpl.class),
"factory",
ASM.getDescriptor(SessionFactoryImplementor.class)));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEINTERFACE,
ASM.getInternalName(SessionFactoryImplementor.class),
"getDialect",
"()" + ASM.getDescriptor(Dialect.class),
true));
afterInstructions.add(new TypeInsnNode(Opcodes.INSTANCEOF, ASM.getInternalName(DistinctLimitDialect.class)));
afterInstructions.add(new JumpInsnNode(Opcodes.IFEQ, endIfNode));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(
new TypeInsnNode(
Opcodes.NEW,
ASM.getInternalName(DistinctLimitQueryLoader.class)));
afterInstructions.add(new InsnNode(Opcodes.DUP));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
afterInstructions.add(
new FieldInsnNode(
Opcodes.GETFIELD,
this.getResultInternalName(),
"factory",
ASM.getDescriptor(SessionFactoryImplementor.class)));
if (this.walkerVarInDoCompile == 0) {
throw new AssertionError();
}
afterInstructions.add(new VarInsnNode(Opcodes.ALOAD, this.walkerVarInDoCompile));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(HqlSqlWalker.class),
"getSelectClause",
"()" + ASM.getDescriptor(SelectClause.class),
false));
afterInstructions.add(
new MethodInsnNode(
Opcodes.INVOKESPECIAL,
ASM.getInternalName(DistinctLimitQueryLoader.class),
"",
"(" +
ASM.getDescriptor(XQueryTranslatorImpl.class) +
ASM.getDescriptor(SessionFactoryImplementor.class) +
ASM.getDescriptor(SelectClause.class) +
")V",
false));
afterInstructions.add(
new FieldInsnNode(
Opcodes.PUTFIELD,
this.getResultInternalName(),
"distinctLimitQueryLoader",
ASM.getDescriptor(DistinctLimitQueryLoader.class)));
afterInstructions.add(endIfNode);
/*
* } //end if (this.queryType == QUERY)
*/
afterInstructions.add(isNotQueryLabelNode);
instructions.insert(abstractInsnNode, afterInstructions);
}
} else if (abstractInsnNode.getOpcode() == Opcodes.ASTORE &&
abstractInsnNode.getPrevious() instanceof MethodInsnNode) {
MethodInsnNode prevMethodInsnNode = (MethodInsnNode)abstractInsnNode.getPrevious();
if (prevMethodInsnNode.name.equals("analyze") &&
prevMethodInsnNode.owner.equals(ASM.getInternalName(QueryTranslatorImpl.class))) {
this.walkerVarInDoCompile = ((VarInsnNode)abstractInsnNode).var;
}
}
}
}
}
@Override
protected Collection getMoreMethodNodes(
MethodSourceFactory methodSourceFactory) {
List methodNodes = new ArrayList();
methodNodes.add(this.createCompileForCount(false));
methodNodes.add(this.createCompileForCount(true));
methodNodes.add(this.createParseForCount(false, methodSourceFactory));
methodNodes.add(this.createParseForCount(true, methodSourceFactory));
return methodNodes;
}
private MethodNode createCompileForCount(boolean distinct) {
/*
* private void compileForCount(String collectionRole) {
* HqlSqlWalker walker = super.analyze(this.parseForCount(true), collectionRole);
* this.countSqlAst = walker.getAST();
* SqlGenerator sqlGenerator = new SqlGenerator(this.factory);
* sqlGenerator.statement(this.countSqlAst);
* this.countSql = sqlGenerator.getSQL();
* this.countLoader = new UnlimitedCountLoader(
* this,
* this.factory,
* waler.getSelectClause(),
* false
* );
* }
*
* private void compileForDistinctCount(String role) {
* if (!this.shouldUseDistinctQuery()) {
* return;
* }
* HqlSqlWalker walker = super.analyze(this.parseForDistinctCount(true), role);
* this.distinctCountSqlAst = walker.getAST();
* SqlGenerator sqlGenerator = new SqlGenerator(this.factory);
* sqlGenerator.statement(this.distinctCountSqlAst);
* this.distinctCountSql = sqlGenerator.getSQL();
* this.distinctCountLoader = new UnlimitedCountLoader(
* this,
* this.factory,
* walker.getSelectClause(),
* true
* );
* }
*/
MethodNode methodNode = createMethodNode(
Opcodes.ACC_PRIVATE,
distinct ? "compileForDistinctCount" : "compileForCount",
"(Ljava/lang/String;)V",
null);
InsnList instructions = new InsnList();
final int walkerIndex = 2;
final int sqlAstIndex = walkerIndex + 1;
final int sqlGeneratorIndex = sqlAstIndex + 1;
if (distinct) {
LabelNode useDistinctLabelNode = new LabelNode();
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
this.getResultInternalName(),
"shouldUseDistinctQuery",
"()Z",
false
)
);
instructions.add(new JumpInsnNode(Opcodes.IFNE, useDistinctLabelNode));
instructions.add(new InsnNode(Opcodes.RETURN));
instructions.add(useDistinctLabelNode);
}
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new LdcInsnNode(true));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
this.getResultInternalName(),
distinct ? "parseForDistinctCount" : "parseForCount",
"(Z)" +
ASM.getDescriptor(HqlParser.class),
false));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(QueryTranslatorImpl.class),
"analyze",
"(" +
ASM.getDescriptor(HqlParser.class) +
"Ljava/lang/String;)" +
ASM.getDescriptor(HqlSqlWalker.class),
false));
instructions.add(new VarInsnNode(Opcodes.ASTORE, walkerIndex));
instructions.add(new VarInsnNode(Opcodes.ALOAD, walkerIndex));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(HqlSqlWalker.class),
"getAST",
"()" + ASM.getDescriptor(AST.class),
false));
instructions.add(new VarInsnNode(Opcodes.ASTORE, sqlAstIndex));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new VarInsnNode(Opcodes.ALOAD, sqlAstIndex));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
this.getResultInternalName(),
distinct ? "processDistinctCountSqlAst" : "processCountSqlAst",
"(" + ASM.getDescriptor(AST.class) + ")V",
false));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new VarInsnNode(Opcodes.ALOAD, sqlAstIndex));
instructions.add(
new FieldInsnNode(
Opcodes.PUTFIELD,
this.getResultInternalName(),
distinct ? "distinctCountSqlAst" : "countSqlAst",
ASM.getDescriptor(AST.class)));
instructions.add(new TypeInsnNode(Opcodes.NEW, ASM.getInternalName(SqlGenerator.class)));
instructions.add(new InsnNode(Opcodes.DUP));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(
new FieldInsnNode(
Opcodes.GETFIELD,
this.getResultInternalName(),
"factory",
ASM.getDescriptor(SessionFactoryImplementor.class)));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESPECIAL,
ASM.getInternalName(SqlGenerator.class),
"",
"(" +
ASM.getDescriptor(SessionFactoryImplementor.class) +
")V",
false));
instructions.add(new VarInsnNode(Opcodes.ASTORE, sqlGeneratorIndex));
instructions.add(new VarInsnNode(Opcodes.ALOAD, sqlGeneratorIndex));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(
new FieldInsnNode(
Opcodes.GETFIELD,
this.getResultInternalName(),
distinct ? "distinctCountSqlAst" : "countSqlAst",
ASM.getDescriptor(AST.class)));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(SqlGenerator.class),
"statement",
"(" +
ASM.getDescriptor(AST.class) +
")V",
false));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new VarInsnNode(Opcodes.ALOAD, sqlGeneratorIndex));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(SqlGenerator.class),
"getSQL",
"()Ljava/lang/String;",
false));
instructions.add(
new FieldInsnNode(
Opcodes.PUTFIELD,
this.getResultInternalName(),
distinct ? "distinctCountSql" : "countSql",
"Ljava/lang/String;"));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new TypeInsnNode(Opcodes.NEW, ASM.getInternalName(UnlimitedCountLoader.class)));
instructions.add(new InsnNode(Opcodes.DUP));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(
new FieldInsnNode(
Opcodes.GETFIELD,
this.getResultInternalName(),
"factory",
ASM.getDescriptor(SessionFactoryImplementor.class)));
instructions.add(new VarInsnNode(Opcodes.ALOAD, walkerIndex));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(HqlSqlWalker.class),
"getSelectClause",
"()" + ASM.getDescriptor(SelectClause.class),
false));
instructions.add(new InsnNode(distinct ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
instructions.add(new MethodInsnNode(
Opcodes.INVOKESPECIAL,
ASM.getInternalName(UnlimitedCountLoader.class),
"",
"(" +
ASM.getDescriptor(XQueryTranslatorImpl.class) +
ASM.getDescriptor(SessionFactoryImplementor.class) +
ASM.getDescriptor(SelectClause.class) +
"Z)V",
false));
instructions.add(
new FieldInsnNode(
Opcodes.PUTFIELD,
this.getResultInternalName(),
distinct ? "distinctCountLoader" : "countLoader",
ASM.getDescriptor(UnlimitedCountLoader.class)));
instructions.add(new InsnNode(Opcodes.RETURN));
methodNode.instructions = instructions;
return methodNode;
}
private MethodNode createParseForCount(boolean distinct, MethodSourceFactory methodSourceFactory) {
MethodNode methodNode = createMethodNode(
Opcodes.ACC_PRIVATE,
distinct ? "parseForDistinctCount" : "parseForCount",
"(Z)" + ASM.getDescriptor(HqlParser.class),
null);
MethodSource parseSource = methodSourceFactory.getMethodSource(PARSE_METHOD);
methodNode.tryCatchBlocks = cloneTryCatchBlocks(parseSource.getOldTryCatchBlocks());
InsnList instructions = cloneInsnList(parseSource.getOldInstructions());
for (AbstractInsnNode abstractInsnNode = instructions.getFirst();
abstractInsnNode != null;
abstractInsnNode = abstractInsnNode.getNext()) {
if (abstractInsnNode.getOpcode() == Opcodes.ARETURN) {
InsnList tmpInstructions = new InsnList();
tmpInstructions.add(new InsnNode(Opcodes.DUP));
tmpInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
tmpInstructions.add(new InsnNode(Opcodes.SWAP));
tmpInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
ASM.getInternalName(HqlParser.class),
"getAST",
"()" + ASM.getDescriptor(AST.class),
false));
tmpInstructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
this.getResultInternalName(),
distinct ? "applyRootJoinNodeForDistinctCount" : "applyRootJoinNodeForCount",
"(" +
ASM.getDescriptor(AST.class) +
")V",
false));
instructions.insertBefore(abstractInsnNode, tmpInstructions);
}
}
methodNode.instructions = instructions;
return methodNode;
}
}
private static class PathPlanFactoryImpl extends AbstractHibernatePathPlanFactory {
private Map entityPersisters;
@SuppressWarnings({ "unchecked", "rawtypes" })
public PathPlanFactoryImpl(SessionFactoryImplementor sfi, AST ast) {
Map map = new HashMap<>();
AST selectFromAst = HqlASTHelper.findFirstChildInToppestQuery(ast, HqlTokenTypes.SELECT_FROM);
AST fromAst = HqlASTHelper.findFirstChildInToppestQuery(selectFromAst, HqlTokenTypes.FROM);
boolean metFirstRange = false;
for (AST fromElementAst = fromAst.getFirstChild();
fromElementAst != null;
fromElementAst = fromElementAst.getNextSibling()) {
if (fromElementAst.getType() == HqlTokenTypes.RANGE) {
AST aliasAst = HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.ALIAS);
if (!metFirstRange || aliasAst != null) {
String alias = aliasAst != null ? aliasAst.getText() : null;
String entityName = HqlASTHelper.getComplexIdentifier(fromElementAst.getFirstChild());
EntityPersister entityPersister = sfi.getEntityPersister(entityName);
if (alias != null) {
map.put(alias, entityPersister);
}
if (!metFirstRange) {
metFirstRange = true;
map.put(null, entityPersister);
}
}
}
if (fromElementAst.getType() == HqlTokenTypes.JOIN) {
AST aliasAst = HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.ALIAS);
if (aliasAst != null) {
AST dotAST = HqlASTHelper.findFirstChildInToppestQuery(fromElementAst, HqlTokenTypes.DOT);
if (dotAST == null) {
throw new AssertionError();
}
AST parentAliasAst = dotAST.getFirstChild();
AST associationNameAst = parentAliasAst.getNextSibling();
JoinSource joinSource = new JoinSource(parentAliasAst.getText(), associationNameAst.getText());
map.put(aliasAst.getText(), joinSource);
}
}
}
while (true) {
boolean continue_ = false;
for (Entry e1 : map.entrySet()) {
if (e1.getValue() instanceof EntityPersister) {
EntityPersister parentEntityPersister = (EntityPersister)e1.getValue();
for (Entry e2 : map.entrySet()) {
if (e2.getValue() instanceof JoinSource) {
JoinSource joinSource = (JoinSource)e2.getValue();
if (joinSource.parentAlias.equals(e1.getKey())) {
String entityName = ((AssociationType)parentEntityPersister.getPropertyType(joinSource.assocationName)).getAssociatedEntityName(sfi);
EntityPersister entityPersister = sfi.getEntityPersister(entityName);
e2.setValue(entityPersister);
continue_ = true;
break;
}
}
}
}
}
if (!continue_) {
break;
}
}
this.entityPersisters = (Map)map;
}
@Override
protected EntityPersister getEntityPersister(String alias) {
return this.entityPersisters.get(alias);
}
private static class JoinSource {
String parentAlias;
String assocationName;
public JoinSource(String parentAlias, String assocationName) {
this.parentAlias = parentAlias;
this.assocationName = assocationName;
}
}
}
private static class ScalarEagerness {
private SessionImplementor session;
private Map, ClassMetadata> classMetadatas = new HashMap<>();
private ScalarBatchLoader scalarBatchLoader = new ScalarBatchLoader();
ScalarEagerness(SessionImplementor session) {
this.session = session;
}
@SuppressWarnings("unchecked")
void prepareApply(Object entity, JoinNode joinNode) {
if (entity == null || !joinNode.containsScalarEagerness()) {
return;
}
ClassMetadata classMetadata = this.getClassMetadata(entity.getClass());
EntityPersister entityPersister =
this
.session
.getEntityPersister(
classMetadata.getEntityName(),
entity
);
XOrderedSet loadedScalarNames = joinNode.getLoadedScalarNames();
String[] names = entityPersister.getPropertyNames();
Type[] types = entityPersister.getPropertyTypes();
boolean[] laziness = entityPersister.getPropertyLaziness();
XOrderedSet requiredPropertyNames = new LinkedHashSet<>((loadedScalarNames.size() * 4 + 2) / 3);
for (int i = names.length - 1; i >= 0; i--) {
if (!types[i].isAssociationType()) {
if (laziness[i] && loadedScalarNames.contains(names[i])) {
requiredPropertyNames.add(names[i]);
}
}
}
if (!requiredPropertyNames.isEmpty()) {
this.parepareLoadScalars(entity, entityPersister, requiredPropertyNames);
}
for (int i = names.length - 1; i >= 0; i--) {
if (types[i].isAssociationType()) {
JoinNode childJoinNode = joinNode.getChildNodes().get(names[i]);
if (childJoinNode != null && childJoinNode.isFetch()) {
Object value = entityPersister.getPropertyValue(entity, i);
if (value != null) {
if (value.getClass().isArray()) {
Object[] arr = (Object[])value;
for (Object childEntity : arr) {
this.prepareApply(childEntity, childJoinNode);
}
} else if (value instanceof Collection>) {
Collection