From 91c7480af521449d998fcf4fe0c9bac9d7c67e15 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 7 Jan 2016 18:22:02 +0100 Subject: [PATCH] DATAJPA-845 - Avoid unnecessary lookups of PersistenceProvider. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../mapping/JpaMetamodelMappingContext.java | 11 ++-- .../jpa/mapping/JpaPersistentEntityImpl.java | 20 +++---- .../jpa/provider/PersistenceProvider.java | 58 +++++++++++++++---- .../query/JpaQueryLookupStrategy.java | 17 ++++-- .../query/ParameterMetadataProvider.java | 9 ++- .../repository/query/PartTreeJpaQuery.java | 44 ++++++++------ .../JpaQueryLookupStrategyUnitTests.java | 3 +- .../PartTreeJpaQueryIntegrationTests.java | 11 ++-- 8 files changed, 113 insertions(+), 60 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index a6198fc35..13172aafe 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -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, JpaPersistentProperty> { +public class JpaMetamodelMappingContext + extends AbstractMappingContext, JpaPersistentProperty> { private final Set 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 JpaPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { - return new JpaPersistentEntityImpl(typeInformation, getMetamodelFor(typeInformation.getType())); + return new JpaPersistentEntityImpl(typeInformation, persistenceProvider); } /* diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index 65dd245fc..4b1252723 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -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 extends BasicPersistentEntity information, Metamodel metamodel) { + public JpaPersistentEntityImpl(TypeInformation 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 extends BasicPersistentEntity entity, Object bean, ProxyIdAccessor proxyIdAccessor) { + public JpaProxyAwareIdentifierAccessor(JpaPersistentEntity entity, Object bean, + ProxyIdAccessor proxyIdAccessor) { super(entity, bean); @@ -132,8 +130,8 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity) 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, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap, PersistenceProvider>(); + private final Iterable entityManagerClassNames; private final Iterable 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 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 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()); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 7b1606394..1a9a8d1ef 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -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.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; @@ -84,18 +85,22 @@ 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); + throw new IllegalArgumentException( + String.format("Could not create query metamodel for method %s!", method.toString()), e); } } @@ -157,8 +162,8 @@ public final class JpaQueryLookupStrategy { return query; } - throw new IllegalStateException(String.format( - "Did neither find a NamedQuery nor an annotated query for method %s!", method)); + throw new IllegalStateException( + String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method)); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 1647bb749..9d101d26a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -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 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(); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 6156f6802..ee0c7c983 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -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. @@ -52,17 +52,19 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { * @param method 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); this.parameters = method.getParameters(); - this.countQuery = new CountQueryPreparer(parameters.potentiallySortsDynamically()); - this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(parameters.potentiallySortsDynamically()); + this.countQuery = new CountQueryPreparer(persistenceProvider, parameters.potentiallySortsDynamically()); + this.query = tree.isCountProjection() ? countQuery + : new QueryPreparer(persistenceProvider, parameters.potentiallySortsDynamically()); } /* @@ -103,10 +105,14 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { private final CriteriaQuery cachedCriteriaQuery; private final List> 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(); } @@ -124,7 +130,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(); } @@ -183,14 +189,15 @@ 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) : new ParameterMetadataProvider(builder, accessor, persistenceProvider); + ParameterMetadataProvider provider = accessor == null + ? new ParameterMetadataProvider(builder, parameters, persistenceProvider) + : new ParameterMetadataProvider(builder, accessor, persistenceProvider); return new JpaQueryCreator(tree, domainClass, builder, provider); } @@ -226,23 +233,24 @@ 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) : new ParameterMetadataProvider(builder, accessor, persistenceProvider); + ParameterMetadataProvider provider = accessor == null + ? new ParameterMetadataProvider(builder, parameters, persistenceProvider) + : new ParameterMetadataProvider(builder, accessor, persistenceProvider); return new JpaCountQueryCreator(tree, domainClass, builder, provider); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 778c48bb2..f12229292 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -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. @@ -68,6 +68,7 @@ public class JpaQueryLookupStrategyUnitTests { when(em.getEntityManagerFactory()).thenReturn(emf); when(emf.createEntityManager()).thenReturn(em); + when(em.getDelegate()).thenReturn(em); } /** diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index bf0815996..8ac508283 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -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; @@ -75,7 +75,8 @@ public class PartTreeJpaQueryIntegrationTests { Method method = UserRepository.class.getMethod("findByFirstname", String.class, Pageable.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class), PersistenceProvider.fromEntityManager(entityManager)); - 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) }); @@ -104,7 +105,8 @@ public class PartTreeJpaQueryIntegrationTests { Method method = UserRepository.class.getMethod("findByFirstname", String.class, Pageable.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class), PersistenceProvider.fromEntityManager(entityManager)); - 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) }); @@ -126,7 +128,8 @@ public class PartTreeJpaQueryIntegrationTests { Method method = UserRepository.class.getMethod(methodName, parameterTypes); JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class), PersistenceProvider.fromEntityManager(entityManager)); - PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); + PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, + PersistenceProvider.fromEntityManager(entityManager)); jpaQuery.createQuery(values); }