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 c1af02c7e..cbafb058d 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.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); 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 ac8189ae2..501269d14 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. @@ -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> 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) 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 0d0eb618d..22ce45689 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. @@ -70,6 +70,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 7b7ebe5c0..697259712 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; @@ -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); }