DATAJPA-845 - Avoid unnecessary lookups of PersistenceProvider.

To prevent multiple attempts of class loading down stream for multiple lookup calls to fromEntityManager(…) or fromMetamodel(…). We now avoid the repeated lookups of a PersistenceProvider instance by reusing a canonical one created in CreateQueryLookupStrategy.

Instead of looking up the PersistenceProvider for every entity again, we now look it up once in the JpaMappingContext for reuse on all JpaPersistentEntity instances.

Original pull request: #161.
This commit is contained in:
Oliver Gierke
2016-01-07 18:22:02 +01:00
parent b04e0f03fc
commit e668d800d1
8 changed files with 102 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.Set;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -34,10 +35,11 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @since 1.3
*/
public class JpaMetamodelMappingContext extends
AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {
public class JpaMetamodelMappingContext
extends AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {
private final Set<Metamodel> models;
private final PersistenceProvider persistenceProvider;
/**
* Creates a new JPA {@link Metamodel} based {@link MappingContext}.
@@ -50,6 +52,7 @@ public class JpaMetamodelMappingContext extends
Assert.notEmpty(models, "At least one JPA metamodel must be present!");
this.models = models;
this.persistenceProvider = PersistenceProvider.fromMetamodel(models.iterator().next());
}
/*
@@ -58,7 +61,7 @@ public class JpaMetamodelMappingContext extends
*/
@Override
protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new JpaPersistentEntityImpl<T>(typeInformation, getMetamodelFor(typeInformation.getType()));
return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider);
}
/*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,7 @@ package org.springframework.data.jpa.mapping;
import java.util.Comparator;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.annotation.Version;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.provider.ProxyIdAccessor;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.model.BasicPersistentEntity;
@@ -48,14 +45,14 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
* Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}.
*
* @param information must not be {@literal null}.
* @param metamodel must not be {@literal null}.
* @param proxyIdAccessor must not be {@literal null}.
*/
public JpaPersistentEntityImpl(TypeInformation<T> information, Metamodel metamodel) {
public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor) {
super(information, null);
Assert.notNull(metamodel, "Metamodel must not be null!");
this.proxyIdAccessor = PersistenceProvider.fromMetamodel(metamodel);
Assert.notNull(proxyIdAccessor, "ProxyIdAccessor must not be null!");
this.proxyIdAccessor = proxyIdAccessor;
}
/*
@@ -116,7 +113,8 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
* @param bean must not be {@literal null}.
* @param proxyIdAccessor must not be {@literal null}.
*/
public JpaProxyAwareIdentifierAccessor(JpaPersistentEntity<?> entity, Object bean, ProxyIdAccessor proxyIdAccessor) {
public JpaProxyAwareIdentifierAccessor(JpaPersistentEntity<?> entity, Object bean,
ProxyIdAccessor proxyIdAccessor) {
super(entity, bean);
@@ -132,8 +130,8 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
*/
@Override
public Object getIdentifier() {
return proxyIdAccessor.shouldUseAccessorFor(bean) ? proxyIdAccessor.getIdentifierFrom(bean) : super
.getIdentifier();
return proxyIdAccessor.shouldUseAccessorFor(bean) ? proxyIdAccessor.getIdentifierFrom(bean)
: super.getIdentifier();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2015 the original author or authors.
* Copyright 2008-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ import org.hibernate.proxy.HibernateProxy;
import org.springframework.data.util.CloseableIterator;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Enumeration representing persistence providers to be used.
@@ -51,7 +52,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Thomas Darimont
*/
public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
public enum PersistenceProvider implements QueryExtractor,ProxyIdAccessor {
/**
* Hibernate persistence provider.
@@ -121,8 +122,8 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
/**
* EclipseLink persistence provider.
*/
ECLIPSELINK(Collections.singleton(ECLIPSELINK_ENTITY_MANAGER_INTERFACE), Collections
.singleton(ECLIPSELINK_JPA_METAMODEL_TYPE)) {
ECLIPSELINK(Collections.singleton(ECLIPSELINK_ENTITY_MANAGER_INTERFACE),
Collections.singleton(ECLIPSELINK_JPA_METAMODEL_TYPE)) {
public String extractQueryString(Query query) {
return ((JpaQuery<?>) query).getDatabaseQuery().getJPQLString();
@@ -263,6 +264,8 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
String OPENJPA_JPA_METAMODEL_TYPE = "org.apache.openjpa.persistence.meta.MetamodelImpl";
}
private static ConcurrentReferenceHashMap<Class<?>, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<Class<?>, PersistenceProvider>();
private final Iterable<String> entityManagerClassNames;
private final Iterable<String> metamodelClassNames;
@@ -287,32 +290,65 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
*/
public static PersistenceProvider fromEntityManager(EntityManager em) {
Assert.notNull(em);
Assert.notNull(em, "EntityManager must not be null!");
Class<?> entityManagerType = em.getDelegate().getClass();
PersistenceProvider cachedProvider = CACHE.get(entityManagerType);
if (cachedProvider != null) {
return cachedProvider;
}
for (PersistenceProvider provider : values()) {
for (String entityManagerClassName : provider.entityManagerClassNames) {
if (isEntityManagerOfType(em, entityManagerClassName)) {
return provider;
return cacheAndReturn(entityManagerType, provider);
}
}
}
return GENERIC_JPA;
return cacheAndReturn(entityManagerType, GENERIC_JPA);
}
/**
* Determines the {@link PersistenceProvider} from the given {@link Metamodel}. If no special one can be determined
* {@link #GENERIC_JPA} will be returned.
*
* @param metamodel must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static PersistenceProvider fromMetamodel(Metamodel metamodel) {
Assert.notNull(metamodel, "Metamodel must not be null!");
Class<? extends Metamodel> metamodelType = metamodel.getClass();
PersistenceProvider cachedProvider = CACHE.get(metamodelType);
if (cachedProvider != null) {
return cachedProvider;
}
for (PersistenceProvider provider : values()) {
for (String metamodelClassName : provider.metamodelClassNames) {
if (isMetamodelOfType(metamodel, metamodelClassName)) {
return provider;
return cacheAndReturn(metamodelType, provider);
}
}
}
return GENERIC_JPA;
return cacheAndReturn(metamodelType, GENERIC_JPA);
}
/**
* Caches the given {@link PersistenceProvider} for the given source type.
*
* @param type must not be {@literal null}.
* @param provider must not be {@literal null}.
* @return
*/
private static PersistenceProvider cacheAndReturn(Class<?> type, PersistenceProvider provider) {
CACHE.put(type, provider);
return provider;
}
/*
@@ -348,8 +384,8 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
}
public CloseableIterator<Object> executeQueryWithResultStream(Query jpaQuery) {
throw new UnsupportedOperationException("Streaming results is not implement for this PersistenceProvider: "
+ name());
throw new UnsupportedOperationException(
"Streaming results is not implement for this PersistenceProvider: " + name());
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2013 the original author or authors.
* Copyright 2008-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import java.lang.reflect.Method;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
@@ -87,15 +88,19 @@ public final class JpaQueryLookupStrategy {
*/
private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrategy {
private final PersistenceProvider persistenceProvider;
public CreateQueryLookupStrategy(EntityManager em, QueryExtractor extractor) {
super(em, extractor);
this.persistenceProvider = PersistenceProvider.fromEntityManager(em);
}
@Override
protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) {
try {
return new PartTreeJpaQuery(method, em);
return new PartTreeJpaQuery(method, em, persistenceProvider);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
String.format("Could not create query metamodel for method %s!", method.toString()), e);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,7 +73,6 @@ class ParameterMetadataProvider {
* @param provider must not be {@literal null}.
*/
public ParameterMetadataProvider(CriteriaBuilder builder, Parameters<?, ?> parameters, PersistenceProvider provider) {
this(builder, null, parameters, provider);
}
@@ -90,9 +89,9 @@ class ParameterMetadataProvider {
private ParameterMetadataProvider(CriteriaBuilder builder, Iterator<Object> bindableParameterValues,
Parameters<?, ?> parameters, PersistenceProvider provider) {
Assert.notNull(builder);
Assert.notNull(parameters);
Assert.notNull(provider);
Assert.notNull(builder, "CriteriaBuilder must not be null!");
Assert.notNull(parameters, "Parameters must not be null!");
Assert.notNull(provider, "PesistenceProvider must not be null!");
this.builder = builder;
this.parameters = parameters.getBindableParameters().iterator();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2014 the original author or authors.
* Copyright 2008-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,9 +54,10 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
* @param factory must not be {@literal null}.
* @param em must not be {@literal null}.
*/
public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) {
public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em, PersistenceProvider persistenceProvider) {
super(method, em);
this.em = em;
this.domainClass = method.getEntityInformation().getJavaType();
this.tree = new PartTree(method.getName(), domainClass);
@@ -64,8 +65,8 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
boolean recreationRequired = parameters.hasDynamicProjection() || parameters.potentiallySortsDynamically();
this.countQuery = new CountQueryPreparer(recreationRequired);
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(recreationRequired);
this.countQuery = new CountQueryPreparer(persistenceProvider, recreationRequired);
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(persistenceProvider, recreationRequired);
}
/*
@@ -106,10 +107,14 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
private final CriteriaQuery<?> cachedCriteriaQuery;
private final List<ParameterMetadata<?>> expressions;
private final PersistenceProvider persistenceProvider;
public QueryPreparer(boolean recreateQueries) {
public QueryPreparer(PersistenceProvider persistenceProvider, boolean recreateQueries) {
this.persistenceProvider = persistenceProvider;
JpaQueryCreator creator = createCreator(null, persistenceProvider);
JpaQueryCreator creator = createCreator(null);
this.cachedCriteriaQuery = recreateQueries ? null : creator.createQuery();
this.expressions = recreateQueries ? null : creator.getParameterExpressions();
}
@@ -127,7 +132,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {
JpaQueryCreator creator = createCreator(accessor);
JpaQueryCreator creator = createCreator(accessor, persistenceProvider);
criteriaQuery = creator.createQuery(getDynamicSort(values));
expressions = creator.getParameterExpressions();
}
@@ -186,11 +191,11 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
return getEntityManager().createQuery(criteriaQuery);
}
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor,
PersistenceProvider persistenceProvider) {
EntityManager entityManager = getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(entityManager);
ParameterMetadataProvider provider = accessor == null
? new ParameterMetadataProvider(builder, parameters, persistenceProvider)
@@ -232,20 +237,20 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
*/
private class CountQueryPreparer extends QueryPreparer {
public CountQueryPreparer(boolean recreateQueries) {
super(recreateQueries);
public CountQueryPreparer(PersistenceProvider persistenceProvider, boolean recreateQueries) {
super(persistenceProvider, recreateQueries);
}
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#createCreator(org.springframework.data.repository.query.ParametersParameterAccessor)
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#createCreator(org.springframework.data.repository.query.ParametersParameterAccessor, org.springframework.data.jpa.provider.PersistenceProvider)
*/
@Override
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor,
PersistenceProvider persistenceProvider) {
EntityManager entityManager = getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(entityManager);
ParameterMetadataProvider provider = accessor == null
? new ParameterMetadataProvider(builder, parameters, persistenceProvider)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,6 +70,7 @@ public class JpaQueryLookupStrategyUnitTests {
when(em.getEntityManagerFactory()).thenReturn(emf);
when(emf.createEntityManager()).thenReturn(em);
when(em.getDelegate()).thenReturn(em);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License
import org.springframework.aop.framework.Advised;
@@ -74,7 +74,8 @@ public class PartTreeJpaQueryIntegrationTests {
public void test() throws Exception {
JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager,
PersistenceProvider.fromEntityManager(entityManager));
jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
@@ -101,7 +102,8 @@ public class PartTreeJpaQueryIntegrationTests {
public void recreatesQueryIfNullValueIsGiven() throws Exception {
JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager,
PersistenceProvider.fromEntityManager(entityManager));
Query query = jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
@@ -122,8 +124,8 @@ public class PartTreeJpaQueryIntegrationTests {
}
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
JpaQueryMethod queryMethod = getQueryMethod(methodName, parameterTypes);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager,
PersistenceProvider.fromEntityManager(entityManager));
jpaQuery.createQuery(values);
}