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); }