Skip to content

Commit e9513b1

Browse files
beikovsebersole
authored andcommitted
HHH-18271 Optimizations related to caching and statement execution
* Acquire ResultSet eagerly in JdbcValuesResultSetImpl to allow better inlining of hot next() method * Precompute cache related metadata in JdbcValuesMapping * Don't copy data returned from cache, since it's never mutated * Improve QueryParameterBindings building and binding * Get rid of LinkedIdentityHashMap and reduce allocations * Get rid of some megamorphic call sites
1 parent 2214c50 commit e9513b1

33 files changed

+536
-469
lines changed

hibernate-core/src/main/java/org/hibernate/cache/internal/QueryResultsCacheImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public List<?> get(
116116
L2CACHE_LOGGER.debug( "Returning cached query results" );
117117
}
118118

119-
return deepCopy( cacheItem.results );
119+
// No need to copy results, since consumers will never mutate
120+
return cacheItem.results;
120121
}
121122

122123
@Override

hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
2525
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
2626

27+
import org.checkerframework.checker.nullness.qual.Nullable;
28+
2729
/**
2830
* Standard implementation of {@link StatementPreparer}.
2931
*
@@ -129,33 +131,28 @@ public PreparedStatement doPrepare() throws SQLException {
129131
@Override
130132
public PreparedStatement prepareQueryStatement(
131133
String sql,
132-
final boolean isCallable,
133-
final ScrollMode scrollMode) {
134+
boolean isCallable,
135+
@Nullable ScrollMode scrollMode) {
136+
final int resultSetType;
134137
if ( scrollMode != null && !scrollMode.equals( ScrollMode.FORWARD_ONLY ) ) {
135138
if ( ! settings().isScrollableResultSetsEnabled() ) {
136139
throw new AssertionFailure("scrollable result sets are not enabled");
137140
}
138-
final PreparedStatement ps = new QueryStatementPreparationTemplate( sql ) {
139-
public PreparedStatement doPrepare() throws SQLException {
140-
return isCallable
141-
? connection().prepareCall( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY )
142-
: connection().prepareStatement( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY );
143-
}
144-
}.prepareStatement();
145-
jdbcCoordinator.registerLastQuery( ps );
146-
return ps;
141+
resultSetType = scrollMode.toResultSetType();
147142
}
148143
else {
149-
final PreparedStatement ps = new QueryStatementPreparationTemplate( sql ) {
150-
public PreparedStatement doPrepare() throws SQLException {
151-
return isCallable
152-
? connection().prepareCall( sql )
153-
: connection().prepareStatement( sql );
154-
}
155-
}.prepareStatement();
156-
jdbcCoordinator.registerLastQuery( ps );
157-
return ps;
144+
resultSetType = ResultSet.TYPE_FORWARD_ONLY;
158145
}
146+
147+
final PreparedStatement ps = new QueryStatementPreparationTemplate( sql ) {
148+
public PreparedStatement doPrepare() throws SQLException {
149+
return isCallable
150+
? connection().prepareCall( sql, resultSetType, ResultSet.CONCUR_READ_ONLY )
151+
: connection().prepareStatement( sql, resultSetType, ResultSet.CONCUR_READ_ONLY );
152+
}
153+
}.prepareStatement();
154+
jdbcCoordinator.registerLastQuery( ps );
155+
return ps;
159156
}
160157

161158
private abstract class StatementPreparationTemplate {

hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import org.hibernate.ScrollMode;
1313

14+
import org.checkerframework.checker.nullness.qual.Nullable;
15+
1416
/**
1517
* Interface to the object that prepares JDBC {@link Statement}s and {@link PreparedStatement}s
1618
* on behalf of a {@link JdbcCoordinator}.
@@ -89,5 +91,5 @@ public interface StatementPreparer {
8991
*
9092
* @return the prepared statement
9193
*/
92-
PreparedStatement prepareQueryStatement(String sql, boolean isCallable, ScrollMode scrollMode);
94+
PreparedStatement prepareQueryStatement(String sql, boolean isCallable, @Nullable ScrollMode scrollMode);
9395
}

hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
import java.io.Serializable;
1010
import java.util.Collections;
11-
import java.util.HashMap;
1211
import java.util.HashSet;
1312
import java.util.Map;
1413
import java.util.Set;
14+
import java.util.TreeMap;
1515
import java.util.function.Supplier;
1616

1717
import org.hibernate.Filter;
@@ -52,7 +52,8 @@ public class LoadQueryInfluencers implements Serializable {
5252
private @Nullable HashSet<String> enabledFetchProfileNames;
5353

5454
//Lazily initialized!
55-
private @Nullable HashMap<String,Filter> enabledFilters;
55+
//Note that ordering is important for cache keys
56+
private @Nullable TreeMap<String,Filter> enabledFilters;
5657

5758
private boolean subselectFetchEnabled;
5859

@@ -77,7 +78,7 @@ public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, SessionCre
7778
for (FilterDefinition filterDefinition : sessionFactory.getAutoEnabledFilters()) {
7879
FilterImpl filter = new FilterImpl( filterDefinition );
7980
if ( enabledFilters == null ) {
80-
enabledFilters = new HashMap<>();
81+
enabledFilters = new TreeMap<>();
8182
}
8283
enabledFilters.put( filterDefinition.getFilterName(), filter );
8384
}
@@ -157,7 +158,7 @@ public boolean hasEnabledFilters() {
157158
}
158159

159160
public Map<String,Filter> getEnabledFilters() {
160-
final HashMap<String, Filter> enabledFilters = this.enabledFilters;
161+
final TreeMap<String, Filter> enabledFilters = this.enabledFilters;
161162
if ( enabledFilters == null ) {
162163
return Collections.emptyMap();
163164
}
@@ -196,7 +197,7 @@ public Set<String> getEnabledFilterNames() {
196197
public Filter enableFilter(String filterName) {
197198
FilterImpl filter = new FilterImpl( sessionFactory.getFilterDefinition( filterName ) );
198199
if ( enabledFilters == null ) {
199-
this.enabledFilters = new HashMap<>();
200+
this.enabledFilters = new TreeMap<>();
200201
}
201202
enabledFilters.put( filterName, filter );
202203
return filter;

hibernate-core/src/main/java/org/hibernate/internal/FilterImpl.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import java.io.Serializable;
1010
import java.util.Arrays;
1111
import java.util.Collection;
12-
import java.util.HashMap;
12+
import java.util.Collections;
1313
import java.util.Map;
14+
import java.util.TreeMap;
1415
import java.util.function.Supplier;
1516

1617
import org.hibernate.Filter;
@@ -19,6 +20,8 @@
1920
import org.hibernate.engine.spi.SessionFactoryImplementor;
2021
import org.hibernate.metamodel.mapping.JdbcMapping;
2122

23+
import org.checkerframework.checker.nullness.qual.Nullable;
24+
2225
/**
2326
* Implementation of FilterImpl. FilterImpl implements the user's
2427
* view into enabled dynamic filters, allowing them to set filter parameter values.
@@ -30,7 +33,9 @@ public class FilterImpl implements Filter, Serializable {
3033

3134
private transient FilterDefinition definition;
3235
private final String filterName;
33-
private final Map<String,Object> parameters = new HashMap<>();
36+
//Lazily initialized!
37+
//Note that ordering is important for cache keys
38+
private @Nullable TreeMap<String,Object> parameters;
3439
private final boolean autoEnabled;
3540
private final boolean applyToLoadByKey;
3641

@@ -85,7 +90,7 @@ public boolean isAppliedToLoadByKey() {
8590
}
8691

8792
public Map<String,?> getParameters() {
88-
return parameters;
93+
return parameters == null ? Collections.emptyMap() : Collections.unmodifiableMap( parameters );
8994
}
9095

9196
/**
@@ -108,6 +113,9 @@ public Filter setParameter(String name, Object value) throws IllegalArgumentExce
108113
if ( argument != null && !type.getJavaTypeDescriptor().isInstance( argument ) ) {
109114
throw new IllegalArgumentException( "Incorrect type for parameter [" + name + "]" );
110115
}
116+
if ( parameters == null ) {
117+
parameters = new TreeMap<>();
118+
}
111119
parameters.put( name, argument );
112120
return this;
113121
}
@@ -135,6 +143,9 @@ public Filter setParameterList(String name, Collection<?> values) throws Hiberna
135143
throw new HibernateException( "Incorrect type for parameter [" + name + "]" );
136144
}
137145
}
146+
if ( parameters == null ) {
147+
parameters = new TreeMap<>();
148+
}
138149
parameters.put( name, values );
139150
return this;
140151
}
@@ -158,7 +169,7 @@ public Filter setParameterList(String name, Object[] values) throws IllegalArgum
158169
* @return The value of the named parameter.
159170
*/
160171
public Object getParameter(String name) {
161-
return parameters.get( name );
172+
return parameters == null ? null : parameters.get( name );
162173
}
163174

164175
public Supplier<?> getParameterResolver(String name) {
@@ -189,6 +200,6 @@ private boolean hasResolver(String parameterName) {
189200
}
190201

191202
private boolean hasArgument(String parameterName) {
192-
return parameters.containsKey(parameterName);
203+
return parameters != null && parameters.containsKey(parameterName);
193204
}
194205
}

hibernate-core/src/main/java/org/hibernate/internal/util/collections/LinkedIdentityHashMap.java

Lines changed: 0 additions & 84 deletions
This file was deleted.

hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
import java.util.Set;
1414
import java.util.function.Consumer;
1515
import java.util.function.Predicate;
16-
import java.util.stream.Collectors;
1716
import jakarta.persistence.Parameter;
1817

18+
import org.hibernate.engine.spi.SessionFactoryImplementor;
1919
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2020
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
2121
import org.hibernate.procedure.spi.ParameterStrategy;
2222
import org.hibernate.query.QueryParameter;
23+
import org.hibernate.query.internal.QueryParameterBindingsImpl;
2324
import org.hibernate.query.procedure.ProcedureParameter;
2425
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
2526
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
27+
import org.hibernate.query.spi.QueryParameterBindings;
2628
import org.hibernate.query.spi.QueryParameterImplementor;
2729

2830
/**
@@ -68,6 +70,11 @@ else if ( parameter.getPosition() != null ) {
6870
parameters.add( parameter );
6971
}
7072

73+
@Override
74+
public QueryParameterBindings createBindings(SessionFactoryImplementor sessionFactory) {
75+
return QueryParameterBindingsImpl.from( this, sessionFactory );
76+
}
77+
7178
@Override
7279
public void visitParameters(Consumer<QueryParameterImplementor<?>> consumer) {
7380
if ( parameters != null ) {

0 commit comments

Comments
 (0)