Skip to content

Commit 0bd7b8e

Browse files
committed
HHH-4959 - Concurrent HQL parsing blocks on ReflectHelper.classForName()
Exclude JPQL and Criteria API aliases when searching for a Java constant value
1 parent 159bc99 commit 0bd7b8e

File tree

13 files changed

+191
-13
lines changed

13 files changed

+191
-13
lines changed

documentation/src/main/asciidoc/userguide/appendices/Configurations.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,15 @@ implementations that sacrifices performance optimizations to allow legacy 4.x li
349349

350350
Legacy 4.x behavior favored performing pagination in-memory by avoiding the use of the offset value, which is overall poor performance.
351351
In 5.x, the limit handler behavior favors performance, thus, if the dialect doesn't support offsets, an exception is thrown instead.
352+
|`hibernate.query.conventional_java_constants` | `true` (default value) or `false` |
353+
Setting which indicates whether or not Java constant follow the https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html[Java Naming conventions].
354+
355+
Default is `true`.
356+
Existing applications may want to disable this (set it `false`) if non-conventional Java constants are used.
357+
However, there is a significant performance overhead for using non-conventional Java constants
358+
since Hibernate cannot determine if aliases should be treated as Java constants or not.
359+
360+
Check out https://hibernate.atlassian.net/browse/HHH-4959[HHH-4959] for more details.
352361
|===================================================================================================================================================================================================================================
353362

354363
[[configurations-batch]]

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ public static class SessionFactoryOptionsStateStandardImpl implements SessionFac
528528
private Map querySubstitutions;
529529
private boolean strictJpaQueryLanguageCompliance;
530530
private boolean namedQueryStartupCheckingEnabled;
531+
private boolean conventionalJavaConstants;
531532
private final boolean procedureParameterNullPassingEnabled;
532533
private final boolean collectionJoinSubqueryRewriteEnabled;
533534

@@ -654,6 +655,8 @@ public SessionFactoryOptionsStateStandardImpl(StandardServiceRegistry serviceReg
654655
this.querySubstitutions = ConfigurationHelper.toMap( QUERY_SUBSTITUTIONS, " ,=;:\n\t\r\f", configurationSettings );
655656
this.strictJpaQueryLanguageCompliance = cfgService.getSetting( JPAQL_STRICT_COMPLIANCE, BOOLEAN, false );
656657
this.namedQueryStartupCheckingEnabled = cfgService.getSetting( QUERY_STARTUP_CHECKING, BOOLEAN, true );
658+
this.conventionalJavaConstants = cfgService.getSetting(
659+
CONVENTIONAL_JAVA_CONSTANTS, BOOLEAN, true );
657660
this.procedureParameterNullPassingEnabled = cfgService.getSetting( PROCEDURE_NULL_PARAM_PASSING, BOOLEAN, false );
658661
this.collectionJoinSubqueryRewriteEnabled = cfgService.getSetting( COLLECTION_JOIN_SUBQUERY, BOOLEAN, true );
659662

@@ -1051,6 +1054,10 @@ public boolean isNamedQueryStartupCheckingEnabled() {
10511054
return namedQueryStartupCheckingEnabled;
10521055
}
10531056

1057+
public boolean isConventionalJavaConstants() {
1058+
return conventionalJavaConstants;
1059+
}
1060+
10541061
@Override
10551062
public boolean isProcedureParameterNullPassingEnabled() {
10561063
return procedureParameterNullPassingEnabled;
@@ -1365,6 +1372,10 @@ public boolean isNamedQueryStartupCheckingEnabled() {
13651372
return options.isNamedQueryStartupCheckingEnabled();
13661373
}
13671374

1375+
public boolean isConventionalJavaConstants() {
1376+
return options.isConventionalJavaConstants();
1377+
}
1378+
13681379
@Override
13691380
public boolean isProcedureParameterNullPassingEnabled() {
13701381
return options.isProcedureParameterNullPassingEnabled();

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
9696
private final Map querySubstitutions;
9797
private final boolean strictJpaQueryLanguageCompliance;
9898
private final boolean namedQueryStartupCheckingEnabled;
99+
private final boolean conventionalJavaConstants;
99100
private final boolean procedureParameterNullPassingEnabled;
100101
private final boolean collectionJoinSubqueryRewriteEnabled;
101102

@@ -175,6 +176,7 @@ public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
175176
this.querySubstitutions = state.getQuerySubstitutions();
176177
this.strictJpaQueryLanguageCompliance = state.isStrictJpaQueryLanguageCompliance();
177178
this.namedQueryStartupCheckingEnabled = state.isNamedQueryStartupCheckingEnabled();
179+
this.conventionalJavaConstants = state.isConventionalJavaConstants();
178180
this.procedureParameterNullPassingEnabled = state.isProcedureParameterNullPassingEnabled();
179181
this.collectionJoinSubqueryRewriteEnabled = state.isCollectionJoinSubqueryRewriteEnabled();
180182

@@ -376,6 +378,11 @@ public boolean isNamedQueryStartupCheckingEnabled() {
376378
return namedQueryStartupCheckingEnabled;
377379
}
378380

381+
@Override
382+
public boolean isConventionalJavaConstants() {
383+
return conventionalJavaConstants;
384+
}
385+
379386
@Override
380387
public boolean isProcedureParameterNullPassingEnabled() {
381388
return procedureParameterNullPassingEnabled;

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsState.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ public interface SessionFactoryOptionsState {
117117

118118
boolean isNamedQueryStartupCheckingEnabled();
119119

120+
boolean isConventionalJavaConstants();
121+
120122
boolean isProcedureParameterNullPassingEnabled();
121123

122124
boolean isCollectionJoinSubqueryRewriteEnabled();

hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ public boolean isNamedQueryStartupCheckingEnabled() {
214214
return delegate.isNamedQueryStartupCheckingEnabled();
215215
}
216216

217+
@Override
218+
public boolean isConventionalJavaConstants() {
219+
return delegate.isConventionalJavaConstants();
220+
}
221+
217222
@Override
218223
public boolean isProcedureParameterNullPassingEnabled() {
219224
return delegate.isProcedureParameterNullPassingEnabled();

hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ default boolean isAllowRefreshDetachedEntity() {
147147

148148
boolean isNamedQueryStartupCheckingEnabled();
149149

150+
boolean isConventionalJavaConstants();
151+
150152
boolean isSecondLevelCacheEnabled();
151153

152154
boolean isQueryCacheEnabled();

hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,17 @@ public interface AvailableSettings {
801801
*/
802802
String QUERY_STARTUP_CHECKING = "hibernate.query.startup_check";
803803

804+
/**
805+
* Setting which indicates whether or not Java constant follow the Java Naming conventions.
806+
* <p/>
807+
* Default is {@code true}. Existing applications may want to disable this (set it {@code false}) if non-conventional Java constants are used.
808+
* However, there is a significant performance overhead for using non-conventional Java constants since Hibernate cannot determine if aliases
809+
* should be treated as Java constants or not.
810+
*
811+
* @since 5.2
812+
*/
813+
String CONVENTIONAL_JAVA_CONSTANTS = "hibernate.query.conventional_java_constants";
814+
804815
/**
805816
* The {@link org.hibernate.exception.spi.SQLExceptionConverter} to use for converting SQLExceptions
806817
* to Hibernate's JDBCException hierarchy. The default is to use the configured

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import org.hibernate.HibernateException;
1919
import org.hibernate.MappingException;
2020
import org.hibernate.QueryException;
21-
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
2221
import org.hibernate.engine.query.spi.EntityGraphQueryHint;
2322
import org.hibernate.engine.spi.QueryParameters;
2423
import org.hibernate.engine.spi.RowSelection;
@@ -590,7 +589,6 @@ public static class JavaConstantConverter implements NodeTraverser.VisitationStr
590589
private AST dotRoot;
591590

592591
public JavaConstantConverter(SessionFactoryImplementor factory) {
593-
594592
this.factory = factory;
595593
}
596594

@@ -612,7 +610,7 @@ public void visit(AST node) {
612610
}
613611
private void handleDotStructure(AST dotStructureRoot) {
614612
final String expression = ASTUtil.getPathText( dotStructureRoot );
615-
final Object constant = ReflectHelper.getConstantValue( expression, factory.getServiceRegistry().getService( ClassLoaderService.class ) );
613+
final Object constant = ReflectHelper.getConstantValue( expression, factory );
616614
if ( constant != null ) {
617615
dotStructureRoot.setFirstChild( null );
618616
dotStructureRoot.setType( HqlTokenTypes.JAVA_CONSTANT );

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/JavaConstantNode.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.util.Locale;
1010

1111
import org.hibernate.QueryException;
12-
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1312
import org.hibernate.dialect.Dialect;
1413
import org.hibernate.engine.spi.SessionFactoryImplementor;
1514
import org.hibernate.hql.spi.QueryTranslator;
@@ -39,7 +38,7 @@ public void setText(String s) {
3938
// this method to get called twice. The first time with an empty string
4039
if ( StringHelper.isNotEmpty( s ) ) {
4140
constantExpression = s;
42-
constantValue = ReflectHelper.getConstantValue( s, factory.getServiceRegistry().getService( ClassLoaderService.class ) );
41+
constantValue = ReflectHelper.getConstantValue( s, factory );
4342
heuristicType = factory.getTypeResolver().heuristicType( constantValue.getClass().getName() );
4443
super.setText( s );
4544
}

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/LiteralProcessor.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313
import org.hibernate.HibernateException;
1414
import org.hibernate.MappingException;
1515
import org.hibernate.QueryException;
16-
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1716
import org.hibernate.dialect.Dialect;
1817
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
1918
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
2019
import org.hibernate.hql.internal.ast.HqlSqlWalker;
2120
import org.hibernate.hql.internal.ast.InvalidPathException;
22-
import org.hibernate.hql.internal.ast.tree.BooleanLiteralNode;
2321
import org.hibernate.hql.internal.ast.tree.DotNode;
2422
import org.hibernate.hql.internal.ast.tree.FromClause;
2523
import org.hibernate.hql.internal.ast.tree.IdentNode;
@@ -35,7 +33,6 @@
3533

3634
import antlr.SemanticException;
3735
import antlr.collections.AST;
38-
import java.util.Locale;
3936

4037
/**
4138
* A delegate that handles literals and constants for HqlSqlWalker, performing the token replacement functions and
@@ -109,7 +106,7 @@ public void lookupConstant(DotNode node) throws SemanticException {
109106
setSQLValue( node, text, discrim );
110107
}
111108
else {
112-
Object value = ReflectHelper.getConstantValue( text, walker.getSessionFactoryHelper().getFactory().getServiceRegistry().getService( ClassLoaderService.class ) );
109+
Object value = ReflectHelper.getConstantValue( text, walker.getSessionFactoryHelper().getFactory() );
113110
if ( value == null ) {
114111
throw new InvalidPathException( "Invalid path: '" + text + "'" );
115112
}

0 commit comments

Comments
 (0)