HHH-7402 - Improve performance of named query registry

This commit is contained in:
Steve Ebersole 2013-03-26 11:14:14 -05:00
parent dc193c32c5
commit 7d99ca57f3
6 changed files with 266 additions and 112 deletions

View File

@ -251,4 +251,23 @@ public class NamedQueryDefinition implements Serializable {
public String toString() {
return getClass().getName() + '(' + name + " [" + query + "])";
}
public NamedQueryDefinition makeCopy(String name) {
return new NamedQueryDefinition(
name,
getQuery(),
isCacheable(),
getCacheRegion(),
getTimeout(),
getLockOptions(),
getFetchSize(),
getFlushMode(),
getCacheMode(),
isReadOnly(),
getComment(),
getParameterTypes(),
getFirstResult(),
getMaxResults()
);
}
}

View File

@ -216,4 +216,27 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
public String getResultSetRef() {
return resultSetRef;
}
@Override
public NamedSQLQueryDefinition makeCopy(String name) {
return new NamedSQLQueryDefinition(
name,
getQuery(),
isCacheable(),
getCacheRegion(),
getTimeout(),
getFetchSize(),
getFlushMode(),
getCacheMode(),
isReadOnly(),
getComment(),
getParameterTypes(),
getFirstResult(),
getMaxResults(),
getResultSetRef(),
getQuerySpaces(),
isCallable(),
getQueryReturns()
);
}
}

View File

@ -48,6 +48,7 @@ import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.NamedQueryRepository;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.EntityNotFoundDelegate;
@ -281,4 +282,11 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
/**
* Provides access to the named query repository
*
* @return
*/
public NamedQueryRepository getNamedQueryRepository();
}

View File

@ -0,0 +1,185 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.internal.util.collections.CollectionHelper;
/**
* @author Steve Ebersole
*/
public class NamedQueryRepository {
private static final Logger log = Logger.getLogger( NamedQueryRepository.class );
private volatile Map<String, NamedQueryDefinition> namedQueryDefinitionMap;
private volatile Map<String, NamedSQLQueryDefinition> namedSqlQueryDefinitionMap;
private final Map<String, ResultSetMappingDefinition> namedSqlResultSetMappingMap;
public NamedQueryRepository(
Iterable<NamedQueryDefinition> namedQueryDefinitions,
Iterable<NamedSQLQueryDefinition> namedSqlQueryDefinitions,
Iterable<ResultSetMappingDefinition> namedSqlResultSetMappings) {
final HashMap<String, NamedQueryDefinition> namedQueryDefinitionMap = new HashMap<String, NamedQueryDefinition>();
for ( NamedQueryDefinition namedQueryDefinition : namedQueryDefinitions ) {
namedQueryDefinitionMap.put( namedQueryDefinition.getName(), namedQueryDefinition );
}
this.namedQueryDefinitionMap = Collections.unmodifiableMap( namedQueryDefinitionMap );
final HashMap<String, NamedSQLQueryDefinition> namedSqlQueryDefinitionMap = new HashMap<String, NamedSQLQueryDefinition>();
for ( NamedSQLQueryDefinition namedSqlQueryDefinition : namedSqlQueryDefinitions ) {
namedSqlQueryDefinitionMap.put( namedSqlQueryDefinition.getName(), namedSqlQueryDefinition );
}
this.namedSqlQueryDefinitionMap = Collections.unmodifiableMap( namedSqlQueryDefinitionMap );
final HashMap<String, ResultSetMappingDefinition> namedSqlResultSetMappingMap = new HashMap<String, ResultSetMappingDefinition>();
for ( ResultSetMappingDefinition resultSetMappingDefinition : namedSqlResultSetMappings ) {
namedSqlResultSetMappingMap.put( resultSetMappingDefinition.getName(), resultSetMappingDefinition );
}
this.namedSqlResultSetMappingMap = Collections.unmodifiableMap( namedSqlResultSetMappingMap );
}
public NamedQueryDefinition getNamedQueryDefinition(String queryName) {
return namedQueryDefinitionMap.get( queryName );
}
public NamedSQLQueryDefinition getNamedSQLQueryDefinition(String queryName) {
return namedSqlQueryDefinitionMap.get( queryName );
}
public ResultSetMappingDefinition getResultSetMappingDefinition(String mappingName) {
return namedSqlResultSetMappingMap.get( mappingName );
}
public synchronized void registerNamedQueryDefinition(String name, NamedQueryDefinition definition) {
if ( NamedSQLQueryDefinition.class.isInstance( definition ) ) {
throw new IllegalArgumentException( "NamedSQLQueryDefinition instance incorrectly passed to registerNamedQueryDefinition" );
}
if ( ! name.equals( definition.getName() ) ) {
definition = definition.makeCopy( name );
}
final Map<String, NamedQueryDefinition> copy = CollectionHelper.makeCopy( namedQueryDefinitionMap );
final NamedQueryDefinition previous = copy.put( name, definition );
if ( previous != null ) {
log.debugf(
"registering named query definition [%s] overriding previously registered definition [%s]",
name,
previous
);
}
this.namedQueryDefinitionMap = Collections.unmodifiableMap( copy );
}
public synchronized void registerNamedSQLQueryDefinition(String name, NamedSQLQueryDefinition definition) {
if ( ! name.equals( definition.getName() ) ) {
definition = definition.makeCopy( name );
}
final Map<String, NamedSQLQueryDefinition> copy = CollectionHelper.makeCopy( namedSqlQueryDefinitionMap );
final NamedQueryDefinition previous = copy.put( name, definition );
if ( previous != null ) {
log.debugf(
"registering named SQL query definition [%s] overriding previously registered definition [%s]",
name,
previous
);
}
this.namedSqlQueryDefinitionMap = Collections.unmodifiableMap( copy );
}
public Map<String,HibernateException> checkNamedQueries(QueryPlanCache queryPlanCache) {
Map<String,HibernateException> errors = new HashMap<String,HibernateException>();
// Check named HQL queries
log.debugf( "Checking %s named HQL queries", namedQueryDefinitionMap.size() );
for ( NamedQueryDefinition namedQueryDefinition : namedQueryDefinitionMap.values() ) {
// this will throw an error if there's something wrong.
try {
log.debugf( "Checking named query: %s", namedQueryDefinition.getName() );
//TODO: BUG! this currently fails for named queries for non-POJO entities
queryPlanCache.getHQLQueryPlan( namedQueryDefinition.getQueryString(), false, Collections.EMPTY_MAP );
}
catch ( HibernateException e ) {
errors.put( namedQueryDefinition.getName(), e );
}
}
// Check native-sql queries
log.debugf( "Checking %s named SQL queries", namedSqlQueryDefinitionMap.size() );
for ( NamedSQLQueryDefinition namedSQLQueryDefinition : namedSqlQueryDefinitionMap.values() ) {
// this will throw an error if there's something wrong.
try {
log.debugf( "Checking named SQL query: %s", namedSQLQueryDefinition.getName() );
// TODO : would be really nice to cache the spec on the query-def so as to not have to re-calc the hash;
// currently not doable though because of the resultset-ref stuff...
NativeSQLQuerySpecification spec;
if ( namedSQLQueryDefinition.getResultSetRef() != null ) {
ResultSetMappingDefinition definition = getResultSetMappingDefinition( namedSQLQueryDefinition.getResultSetRef() );
if ( definition == null ) {
throw new MappingException( "Unable to find resultset-ref definition: " + namedSQLQueryDefinition.getResultSetRef() );
}
spec = new NativeSQLQuerySpecification(
namedSQLQueryDefinition.getQueryString(),
definition.getQueryReturns(),
namedSQLQueryDefinition.getQuerySpaces()
);
}
else {
spec = new NativeSQLQuerySpecification(
namedSQLQueryDefinition.getQueryString(),
namedSQLQueryDefinition.getQueryReturns(),
namedSQLQueryDefinition.getQuerySpaces()
);
}
queryPlanCache.getNativeSQLQueryPlan( spec );
}
catch ( HibernateException e ) {
errors.put( namedSQLQueryDefinition.getName(), e );
}
}
return errors;
}
}

View File

@ -191,9 +191,7 @@ public final class SessionFactoryImpl
private final transient Map<String,CollectionMetadata> collectionMetadata;
private final transient Map<String,Set<String>> collectionRolesByEntityParticipant;
private final transient Map<String,IdentifierGenerator> identifierGenerators;
private final transient Map<String, NamedQueryDefinition> namedQueries;
private final transient Map<String, NamedSQLQueryDefinition> namedSqlQueries;
private final transient Map<String, ResultSetMappingDefinition> sqlResultSetMappings;
private final transient NamedQueryRepository namedQueryRepository;
private final transient Map<String, FilterDefinition> filters;
private final transient Map<String, FetchProfile> fetchProfiles;
private final transient Map<String,String> imports;
@ -456,9 +454,11 @@ public final class SessionFactoryImpl
collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
//Named Queries:
namedQueries = new HashMap<String, NamedQueryDefinition>( cfg.getNamedQueries() );
namedSqlQueries = new HashMap<String, NamedSQLQueryDefinition>( cfg.getNamedSQLQueries() );
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>( cfg.getSqlResultSetMappings() );
this.namedQueryRepository = new NamedQueryRepository(
cfg.getNamedQueries().values(),
cfg.getNamedSQLQueries().values(),
cfg.getSqlResultSetMappings().values()
);
imports = new HashMap<String,String>( cfg.getImports() );
// after *all* persisters and named queries are registered
@ -845,19 +845,14 @@ public final class SessionFactoryImpl
}
collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
//Named Queries:
namedQueries = new HashMap<String,NamedQueryDefinition>();
for ( NamedQueryDefinition namedQueryDefinition : metadata.getNamedQueryDefinitions() ) {
namedQueries.put( namedQueryDefinition.getName(), namedQueryDefinition );
}
namedSqlQueries = new HashMap<String, NamedSQLQueryDefinition>();
for ( NamedSQLQueryDefinition namedNativeQueryDefinition: metadata.getNamedNativeQueryDefinitions() ) {
namedSqlQueries.put( namedNativeQueryDefinition.getName(), namedNativeQueryDefinition );
}
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>();
for( ResultSetMappingDefinition resultSetMappingDefinition : metadata.getResultSetMappingDefinitions() ) {
sqlResultSetMappings.put( resultSetMappingDefinition.getName(), resultSetMappingDefinition );
}
namedQueryRepository = new NamedQueryRepository(
metadata.getNamedQueryDefinitions(),
metadata.getNamedNativeQueryDefinitions(),
metadata.getResultSetMappingDefinitions()
);
imports = new HashMap<String,String>();
for ( Map.Entry<String,String> importEntry : metadata.getImports() ) {
imports.put( importEntry.getKey(), importEntry.getValue() );
@ -1058,78 +1053,8 @@ public final class SessionFactoryImpl
return queryPlanCache;
}
@SuppressWarnings( {"ThrowableResultOfMethodCallIgnored"})
private Map<String,HibernateException> checkNamedQueries() throws HibernateException {
Map<String,HibernateException> errors = new HashMap<String,HibernateException>();
// Check named HQL queries
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Checking %s named HQL queries", namedQueries.size() );
}
Iterator itr = namedQueries.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final String queryName = ( String ) entry.getKey();
final NamedQueryDefinition qd = ( NamedQueryDefinition ) entry.getValue();
// this will throw an error if there's something wrong.
try {
LOG.debugf( "Checking named query: %s", queryName );
//TODO: BUG! this currently fails for named queries for non-POJO entities
queryPlanCache.getHQLQueryPlan( qd.getQueryString(), false, Collections.EMPTY_MAP );
}
catch ( QueryException e ) {
errors.put( queryName, e );
}
catch ( MappingException e ) {
errors.put( queryName, e );
}
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Checking %s named SQL queries", namedSqlQueries.size() );
}
itr = namedSqlQueries.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final String queryName = ( String ) entry.getKey();
final NamedSQLQueryDefinition qd = ( NamedSQLQueryDefinition ) entry.getValue();
// this will throw an error if there's something wrong.
try {
LOG.debugf( "Checking named SQL query: %s", queryName );
// TODO : would be really nice to cache the spec on the query-def so as to not have to re-calc the hash;
// currently not doable though because of the resultset-ref stuff...
NativeSQLQuerySpecification spec;
if ( qd.getResultSetRef() != null ) {
ResultSetMappingDefinition definition = sqlResultSetMappings.get( qd.getResultSetRef() );
if ( definition == null ) {
throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
}
spec = new NativeSQLQuerySpecification(
qd.getQueryString(),
definition.getQueryReturns(),
qd.getQuerySpaces()
);
}
else {
spec = new NativeSQLQuerySpecification(
qd.getQueryString(),
qd.getQueryReturns(),
qd.getQuerySpaces()
);
}
queryPlanCache.getNativeSQLQueryPlan( spec );
}
catch ( QueryException e ) {
errors.put( queryName, e );
}
catch ( MappingException e ) {
errors.put( queryName, e );
}
}
return errors;
return namedQueryRepository.checkNamedQueries( queryPlanCache );
}
public EntityPersister getEntityPersister(String entityName) throws MappingException {
@ -1206,41 +1131,29 @@ public final class SessionFactoryImpl
);
}
@Override
public NamedQueryRepository getNamedQueryRepository() {
return namedQueryRepository;
}
public void registerNamedQueryDefinition(String name, NamedQueryDefinition definition) {
if ( NamedSQLQueryDefinition.class.isInstance( definition ) ) {
throw new IllegalArgumentException( "NamedSQLQueryDefinition instance incorrectly passed to registerNamedQueryDefinition" );
}
final NamedQueryDefinition previous = namedQueries.put( name, definition );
if ( previous != null ) {
LOG.debugf(
"registering named query definition [%s] overriding previously registered definition [%s]",
name,
previous
);
}
namedQueryRepository.registerNamedQueryDefinition( name, definition );
}
public NamedQueryDefinition getNamedQuery(String queryName) {
return namedQueries.get( queryName );
return namedQueryRepository.getNamedQueryDefinition( queryName );
}
public void registerNamedSQLQueryDefinition(String name, NamedSQLQueryDefinition definition) {
final NamedSQLQueryDefinition previous = namedSqlQueries.put( name, definition );
if ( previous != null ) {
LOG.debugf(
"registering named SQL query definition [%s] overriding previously registered definition [%s]",
name,
previous
);
}
namedQueryRepository.registerNamedSQLQueryDefinition( name, definition );
}
public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
return namedSqlQueries.get( queryName );
return namedQueryRepository.getNamedSQLQueryDefinition( queryName );
}
public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
return sqlResultSetMappings.get( resultSetName );
public ResultSetMappingDefinition getResultSetMapping(String mappingName) {
return namedQueryRepository.getResultSetMappingDefinition( mappingName );
}
public Type getIdentifierType(String className) throws MappingException {

View File

@ -146,4 +146,10 @@ public final class CollectionHelper {
public static boolean isNotEmpty(Map map) {
return !isEmpty( map );
}
public static <X,Y> Map<X, Y> makeCopy(Map<X, Y> map) {
final Map<X,Y> copy = mapOfSize( map.size() + 1 );
copy.putAll( map );
return copy;
}
}