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 e0d825330..a6198fc35 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -17,7 +17,6 @@ package org.springframework.data.jpa.mapping; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; -import java.util.Collections; import java.util.Set; import javax.persistence.metamodel.ManagedType; @@ -49,11 +48,8 @@ public class JpaMetamodelMappingContext extends Assert.notNull(models, "JPA metamodel must not be null!"); Assert.notEmpty(models, "At least one JPA metamodel must be present!"); - this.models = models; - } - public JpaMetamodelMappingContext(Metamodel model) { - this(Collections.singleton(model)); + this.models = models; } /* @@ -62,7 +58,7 @@ public class JpaMetamodelMappingContext extends */ @Override protected JpaPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { - return new JpaPersistentEntityImpl(typeInformation, null); + return new JpaPersistentEntityImpl(typeInformation, getMetamodelFor(typeInformation.getType())); } /* 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 4e133b7c8..7f2b2489f 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -17,8 +17,15 @@ package org.springframework.data.jpa.mapping; import java.util.Comparator; +import javax.persistence.metamodel.Metamodel; + +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; +import org.springframework.data.mapping.model.IdPropertyIdentifierAccessor; import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; /** * Implementation of {@link JpaPersistentEntity}. @@ -29,14 +36,20 @@ import org.springframework.data.util.TypeInformation; class JpaPersistentEntityImpl extends BasicPersistentEntity implements JpaPersistentEntity { + private final ProxyIdAccessor proxyIdAccessor; + /** * Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}. * * @param information must not be {@literal null}. - * @param comparator must not be {@literal null}. + * @param metamodel must not be {@literal null}. */ - public JpaPersistentEntityImpl(TypeInformation information, Comparator comparator) { - super(information, comparator); + public JpaPersistentEntityImpl(TypeInformation information, Metamodel metamodel) { + + super(information, null); + + Assert.notNull(metamodel, "Metamodel must not be null!"); + this.proxyIdAccessor = PersistenceProvider.fromMetamodel(metamodel); } /* @@ -47,4 +60,54 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity entity, Object bean, ProxyIdAccessor proxyIdAccessor) { + + super(entity, bean); + + Assert.notNull(proxyIdAccessor, "Proxy identifier accessor must not be null!"); + + this.proxyIdAccessor = proxyIdAccessor; + this.bean = bean; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.IdentifierAccessor#getIdentifier() + */ + @Override + public Object getIdentifier() { + return proxyIdAccessor.shouldUseAccessorFor(bean) ? proxyIdAccessor.getIdentifierFrom(bean) : super + .getIdentifier(); + } + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java b/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java similarity index 67% rename from src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java rename to src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index fd91caf90..4f0eeaa64 100644 --- a/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java +++ b/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.utils; +package org.springframework.data.jpa.provider; import javax.persistence.EntityManager; +import javax.persistence.metamodel.Metamodel; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -25,7 +26,7 @@ import org.springframework.util.ClassUtils; * * @author Oliver Gierke */ -public abstract class JpaClassUtils { +abstract class JpaClassUtils { /** * Private constructor to prevent instantiation. @@ -42,16 +43,21 @@ public abstract class JpaClassUtils { * @return */ public static boolean isEntityManagerOfType(EntityManager em, String type) { + return isOfType(em, type, em.getDelegate().getClass().getClassLoader()); + } - Assert.notNull(em, "EntityManager must not be null!"); - Assert.hasText(type, "EntityManager type must not be null!"); + public static boolean isMetamodelOfType(Metamodel metamodel, String type) { + return isOfType(metamodel, type, metamodel.getClass().getClassLoader()); + } + + private static boolean isOfType(Object source, String typeName, ClassLoader classLoader) { + + Assert.notNull(source, "Source instance must not be null!"); + Assert.hasText(typeName, "Target type name must not be null or empty!"); try { - ClassLoader loader = em.getDelegate().getClass().getClassLoader(); - Class emType = ClassUtils.forName(type, loader); - - emType.cast(em); + ClassUtils.forName(typeName, classLoader).cast(source); return true; } catch (Exception e) { diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java new file mode 100644 index 000000000..dd1177047 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -0,0 +1,284 @@ +/* + * Copyright 2008-2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.provider; + +import static org.springframework.data.jpa.provider.JpaClassUtils.*; +import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*; + +import java.util.Arrays; +import java.util.Collections; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.metamodel.Metamodel; + +import org.apache.openjpa.enhance.PersistenceCapable; +import org.apache.openjpa.persistence.OpenJPAQuery; +import org.eclipse.persistence.jpa.JpaQuery; +import org.hibernate.ejb.HibernateQuery; +import org.hibernate.proxy.HibernateProxy; +import org.springframework.util.Assert; + +/** + * Enumeration representing persistence providers to be used. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { + + /** + * Hibernate persistence provider. + *

+ * Since Hibernate 4.3 the location of the HibernateEntityManager moved to the org.hibernate.jpa package. In order to + * support both locations we interpret both classnames as a Hibernate {@code PersistenceProvider}. + * + * @see DATAJPA-444 + */ + HIBERNATE(// + Arrays.asList(HIBERNATE43_ENTITY_MANAGER_INTERFACE, HIBERNATE_ENTITY_MANAGER_INTERFACE), // + Arrays.asList(HIBERNATE43_JPA_METAMODEL_TYPE, HIBERNATE_JPA_METAMODEL_TYPE)) { + + public String extractQueryString(Query query) { + return ((HibernateQuery) query).getHibernateQuery().getQueryString(); + } + + /** + * Return custom placeholder ({@code *}) as Hibernate does create invalid queries for count queries for objects with + * compound keys. + * + * @see HHH-4044 + * @see HHH-3096 + */ + @Override + public String getCountQueryPlaceholder() { + return "*"; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) + */ + @Override + public boolean shouldUseAccessorFor(Object entity) { + return entity instanceof HibernateProxy; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) + */ + @Override + public Object getIdentifierFrom(Object entity) { + return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier(); + } + }, + + /** + * EclipseLink persistence provider. + */ + ECLIPSELINK(Collections.singleton(ECLIPSELINK_ENTITY_MANAGER_INTERFACE), Collections + .singleton(ECLIPSELINK_JPA_METAMODEL_TYPE)) { + + public String extractQueryString(Query query) { + return ((JpaQuery) query).getDatabaseQuery().getJPQLString(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) + */ + @Override + public boolean shouldUseAccessorFor(Object entity) { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) + */ + @Override + public Object getIdentifierFrom(Object entity) { + return null; + } + }, + + /** + * OpenJpa persistence provider. + */ + OPEN_JPA(Collections.singleton(OPENJPA_ENTITY_MANAGER_INTERFACE), Collections.singleton(OPENJPA_JPA_METAMODEL_TYPE)) { + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(javax.persistence.Query) + */ + @Override + public String extractQueryString(Query query) { + return ((OpenJPAQuery) query).getQueryString(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) + */ + @Override + public boolean shouldUseAccessorFor(Object entity) { + return entity instanceof PersistenceCapable; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) + */ + @Override + public Object getIdentifierFrom(Object entity) { + return ((PersistenceCapable) entity).pcFetchObjectId(); + } + }, + + /** + * Unknown special provider. Use standard JPA. + */ + GENERIC_JPA(Collections.singleton(GENERIC_JPA_ENTITY_MANAGER_INTERFACE), Collections. emptySet()) { + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(javax.persistence.Query) + */ + @Override + public String extractQueryString(Query query) { + return null; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.PersistenceProvider#canExtractQuery() + */ + @Override + public boolean canExtractQuery() { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) + */ + @Override + public boolean shouldUseAccessorFor(Object entity) { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) + */ + @Override + public Object getIdentifierFrom(Object entity) { + return null; + } + }; + + /** + * Holds the PersistenceProvider specific interface names. + * + * @author Thomas Darimont + */ + static interface Constants { + + String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "javax.persistence.EntityManager"; + String OPENJPA_ENTITY_MANAGER_INTERFACE = "org.apache.openjpa.persistence.OpenJPAEntityManager"; + String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager"; + String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.ejb.HibernateEntityManager"; + String HIBERNATE43_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager"; + + String HIBERNATE_JPA_METAMODEL_TYPE = "org.hibernate.ejb.metamodel.MetamodelImpl"; + String HIBERNATE43_JPA_METAMODEL_TYPE = "org.hibernate.jpa.internal.metamodel.MetamodelImpl"; + String ECLIPSELINK_JPA_METAMODEL_TYPE = "org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl"; + String OPENJPA_JPA_METAMODEL_TYPE = "org.apache.openjpa.persistence.meta.MetamodelImpl"; + } + + private final Iterable entityManagerClassNames; + private final Iterable metamodelClassNames; + + /** + * Creates a new {@link PersistenceProvider}. + * + * @param entityManagerClassNames the names of the provider specific {@link EntityManager} implementations. Must not + * be {@literal null} or empty. + */ + private PersistenceProvider(Iterable entityManagerClassNames, Iterable metamodelClassNames) { + + this.entityManagerClassNames = entityManagerClassNames; + this.metamodelClassNames = metamodelClassNames; + } + + /** + * Determines the {@link PersistenceProvider} from the given {@link EntityManager}. If no special one can be + * determined {@link #GENERIC_JPA} will be returned. + * + * @param em must not be {@literal null}. + * @return will never be {@literal null}. + */ + public static PersistenceProvider fromEntityManager(EntityManager em) { + + Assert.notNull(em); + + for (PersistenceProvider provider : values()) { + for (String entityManagerClassName : provider.entityManagerClassNames) { + if (isEntityManagerOfType(em, entityManagerClassName)) { + return provider; + } + } + } + + return GENERIC_JPA; + } + + public static PersistenceProvider fromMetamodel(Metamodel metamodel) { + + Assert.notNull(metamodel, "Metamodel must not be null!"); + + for (PersistenceProvider provider : values()) { + for (String metamodelClassName : provider.metamodelClassNames) { + if (isMetamodelOfType(metamodel, metamodelClassName)) { + return provider; + } + } + } + + return GENERIC_JPA; + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery + * () + */ + public boolean canExtractQuery() { + return true; + } + + /** + * Returns the placeholder to be used for simple count queries. Default implementation returns {@code *}. + * + * @return + */ + public String getCountQueryPlaceholder() { + return "x"; + } +} diff --git a/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java b/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java new file mode 100644 index 000000000..4a319662d --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.provider; + +/** + * Interface for a persistence provider specific accessor of identifiers held in proxies. + * + * @author Oliver Gierke + */ +public interface ProxyIdAccessor { + + /** + * Returns whether the {@link ProxyIdAccessor} should be used for the given entity. Will inspect the entity to see + * whether it is a proxy so that lenient id lookup can be used. + * + * @param entity must not be {@literal null}. + * @return + */ + boolean shouldUseAccessorFor(Object entity); + + /** + * Returns the identifier of the given entity by leniently inspecting it for the identifier value. + * + * @param entity must not be {@literal null}. + * @return + */ + Object getIdentifierFrom(Object entity); +} diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryExtractor.java b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java similarity index 96% rename from src/main/java/org/springframework/data/jpa/repository/query/QueryExtractor.java rename to src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java index fa3e930ad..244b5531a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryExtractor.java +++ b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.query; +package org.springframework.data.jpa.provider; import javax.persistence.Query; 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 0203c646b..7b1606394 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 @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import javax.persistence.EntityManager; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.EvaluationContextProvider; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index da219fe6b..084c5e30b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -29,6 +29,7 @@ import javax.persistence.LockModeType; import javax.persistence.QueryHint; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Modifying; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 180c80c46..56b2eae80 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -21,6 +21,7 @@ import javax.persistence.TypedQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryCreationException; import org.springframework.data.repository.query.RepositoryQuery; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index d0cbe0e51..92d325f1f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -54,7 +54,7 @@ public abstract class JpaEntityInformationSupport ex * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) - public static JpaEntityInformation getMetadata(Class domainClass, EntityManager em) { + public static JpaEntityInformation getEntityInformation(Class domainClass, EntityManager em) { Assert.notNull(domainClass); Assert.notNull(em); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 24d609a3f..96cb0b531 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -21,9 +21,10 @@ import java.io.Serializable; import javax.persistence.EntityManager; +import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; -import org.springframework.data.jpa.repository.query.QueryExtractor; import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; @@ -142,6 +143,6 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { @SuppressWarnings("unchecked") public JpaEntityInformation getEntityInformation(Class domainClass) { - return (JpaEntityInformation) JpaEntityInformationSupport.getMetadata(domainClass, entityManager); + return (JpaEntityInformation) JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java deleted file mode 100644 index f806ad02c..000000000 --- a/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2008-2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import static org.springframework.data.jpa.repository.utils.JpaClassUtils.*; - -import java.util.Arrays; -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.Query; - -import org.apache.openjpa.persistence.OpenJPAQuery; -import org.eclipse.persistence.jpa.JpaQuery; -import org.hibernate.ejb.HibernateQuery; -import org.springframework.data.jpa.repository.query.QueryExtractor; -import org.springframework.util.Assert; - -/** - * Enumeration representing persistence providers to be used. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -public enum PersistenceProvider implements QueryExtractor { - - /** - * Hibernate persistence provider. - *

- * Since Hibernate 4.3 the location of the HibernateEntityManager moved to the org.hibernate.jpa package. In order to - * support both locations we interpret both classnames as a Hibernate {@code PersistenceProvider}. - * - * @see DATAJPA-444 - */ - HIBERNATE(Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE, Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE) { - - public String extractQueryString(Query query) { - - return ((HibernateQuery) query).getHibernateQuery().getQueryString(); - } - - /** - * Return custom placeholder ({@code *}) as Hibernate does create invalid queries for count queries for objects with - * compound keys. - * - * @see HHH-4044 - * @see HHH-3096 - */ - @Override - protected String getCountQueryPlaceholder() { - - return "*"; - } - }, - - /** - * EclipseLink persistence provider. - */ - ECLIPSELINK(Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE) { - - public String extractQueryString(Query query) { - - return ((JpaQuery) query).getDatabaseQuery().getJPQLString(); - } - - }, - - /** - * OpenJpa persistence provider. - */ - OPEN_JPA(Constants.OPENJPA_ENTITY_MANAGER_INTERFACE) { - - public String extractQueryString(Query query) { - - return ((OpenJPAQuery) query).getQueryString(); - } - }, - - /** - * Unknown special provider. Use standard JPA. - */ - GENERIC_JPA(Constants.GENERIC_JPA_ENTITY_MANAGER_INTERFACE) { - - public String extractQueryString(Query query) { - - return null; - } - - @Override - public boolean canExtractQuery() { - - return false; - } - }; - - /** - * Holds the PersistenceProvider specific interface names. - * - * @author Thomas Darimont - */ - static interface Constants { - - String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "javax.persistence.EntityManager"; - String OPENJPA_ENTITY_MANAGER_INTERFACE = "org.apache.openjpa.persistence.OpenJPAEntityManager"; - String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager"; - String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.ejb.HibernateEntityManager"; - String HIBERNATE43_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager"; - } - - private List entityManagerClassNames; - - /** - * Creates a new {@link PersistenceProvider}. - * - * @param entityManagerClassNames the names of the provider specific {@link EntityManager} implementations. Must not - * be {@literal null} or empty. - */ - private PersistenceProvider(String... entityManagerClassNames) { - - Assert.notEmpty(entityManagerClassNames, "EntityManagerClassNames must not be empty!"); - - this.entityManagerClassNames = Arrays.asList(entityManagerClassNames); - } - - /** - * Determines the {@link PersistenceProvider} from the given {@link EntityManager}. If no special one can be - * determined {@link #GENERIC_JPA} will be returned. - * - * @param em must not be {@literal null}. - * @return will never be {@literal null}. - */ - public static PersistenceProvider fromEntityManager(EntityManager em) { - - Assert.notNull(em); - - for (PersistenceProvider provider : values()) { - for (String entityManagerClassName : provider.entityManagerClassNames) { - - if (isEntityManagerOfType(em, entityManagerClassName)) { - return provider; - } - } - } - - return GENERIC_JPA; - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery - * () - */ - public boolean canExtractQuery() { - - return true; - } - - /** - * Returns the placeholder to be used for simple count queries. Default implementation returns {@code *}. - * - * @return - */ - protected String getCountQueryPlaceholder() { - - return "x"; - } -} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index c46f153ef..9051a2cc6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -22,6 +22,7 @@ import javax.persistence.EntityManager; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.querydsl.QSort; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index e90f4bcd4..e78daa36b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -42,6 +42,7 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.query.Jpa21Utils; @@ -96,7 +97,7 @@ public class SimpleJpaRepository implements JpaRepos * @param em must not be {@literal null}. */ public SimpleJpaRepository(Class domainClass, EntityManager em) { - this(JpaEntityInformationSupport.getMetadata(domainClass, em), em); + this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em); } /** diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Category.java b/src/test/java/org/springframework/data/jpa/domain/sample/Category.java new file mode 100644 index 000000000..96230ebbe --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Category.java @@ -0,0 +1,30 @@ +package org.springframework.data.jpa.domain.sample; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Category { + + @Id @GeneratedValue private Long id; + + @ManyToOne(fetch = FetchType.LAZY)// + private Product product; + + public Category(Product product) { + this.product = product; + } + + protected Category() {} + + public Long getId() { + return id; + } + + public Product getProduct() { + return product; + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java new file mode 100644 index 000000000..d63e78dfb --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java @@ -0,0 +1,15 @@ +package org.springframework.data.jpa.domain.sample; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Product { + + @Id @GeneratedValue private Long id; + + public Long getId() { + return id; + } +} diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 14feb79b9..13f3c006c 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2014 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. @@ -18,15 +18,32 @@ package org.springframework.data.jpa.mapping; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import java.util.Collections; +import javax.persistence.EntityManager; + +import org.hibernate.proxy.HibernateProxy; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.jpa.domain.sample.Category; +import org.springframework.data.jpa.domain.sample.Product; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.sample.CategoryRepository; +import org.springframework.data.jpa.repository.sample.ProductRepository; +import org.springframework.data.mapping.IdentifierAccessor; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; /** * Integration tests for {@link JpaMetamodelMappingContext}. @@ -35,16 +52,28 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @since 1.3 */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:infrastructure.xml") +@ContextConfiguration public class JpaMetamodelMappingContextIntegrationTests { + @Configuration + @ImportResource("classpath:infrastructure.xml") + @EnableJpaRepositories(basePackageClasses = CategoryRepository.class,// + includeFilters = @Filter(value = { CategoryRepository.class, ProductRepository.class }, + type = FilterType.ASSIGNABLE_TYPE)) + static class Config { + + } + JpaMetamodelMappingContext context; - @PersistenceContext EntityManager em; + @Autowired ProductRepository products; + @Autowired CategoryRepository categories; + @Autowired EntityManager em; + @Autowired PlatformTransactionManager transactionManager; @Before public void setUp() { - context = new JpaMetamodelMappingContext(em.getMetamodel()); + context = new JpaMetamodelMappingContext(Collections.singleton(em.getMetamodel())); } @Test @@ -95,4 +124,36 @@ public class JpaMetamodelMappingContextIntegrationTests { assertThat(entity.getPersistentProperty("colleagues").isEntity(), is(true)); } + + /** + * @see DATAJPA-630 + */ + @Test + public void lookingUpIdentifierOfProxyDoesNotInitializeProxy() { + + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { + + @Override + public Void doInTransaction(TransactionStatus status) { + + Product product = products.save(new Product()); + Category category = categories.save(new Category(product)); + em.clear(); + + Category loaded = categories.findOne(category.getId()); + Product loadedProduct = loaded.getProduct(); + + JpaPersistentEntity entity = context.getPersistentEntity(Product.class); + IdentifierAccessor accessor = entity.getIdentifierAccessor(loadedProduct); + + assertThat(accessor.getIdentifier(), is((Object) product.getId())); + assertThat(loadedProduct, is(instanceOf(HibernateProxy.class))); + assertThat(((HibernateProxy) loadedProduct).getHibernateLazyInitializer().isUninitialized(), is(true)); + + status.setRollbackOnly(); + + return null; + } + }); + } } diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 2c34a95f6..9b52d61f7 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -18,6 +18,8 @@ package org.springframework.data.jpa.mapping; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.util.Collections; + import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Embeddable; @@ -50,7 +52,7 @@ public class JpaPersistentPropertyImplUnitTests { @Before public void setUp() { - context = new JpaMetamodelMappingContext(model); + context = new JpaMetamodelMappingContext(Collections.singleton(model)); entity = context.getPersistentEntity(Sample.class); } diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java new file mode 100644 index 000000000..c72792c09 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.provider; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import javax.persistence.EntityManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.jpa.domain.sample.Category; +import org.springframework.data.jpa.domain.sample.Product; +import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.provider.ProxyIdAccessor; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.sample.CategoryRepository; +import org.springframework.data.jpa.repository.sample.ProductRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; + +/** + * Integration tests for {@link PersistenceProvider}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class PersistenceProviderIntegrationTests { + + @Configuration + @ImportResource("classpath:infrastructure.xml") + @EnableJpaRepositories(basePackageClasses = CategoryRepository.class,// + includeFilters = @Filter(value = { CategoryRepository.class, ProductRepository.class }, + type = FilterType.ASSIGNABLE_TYPE)) + static class Config { + + } + + @Autowired CategoryRepository categories; + @Autowired ProductRepository products; + @Autowired PlatformTransactionManager transactionManager; + @Autowired EntityManager em; + + Product product; + Category category; + + @Before + public void setUp() { + this.product = products.save(new Product()); + this.category = categories.save(new Category(product)); + } + + /** + * @see DATAJPA-630 + */ + @Test + public void testname() { + + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { + + @Override + public Void doInTransaction(TransactionStatus status) { + + Product product = categories.findOne(category.getId()).getProduct(); + ProxyIdAccessor accessor = PersistenceProvider.fromEntityManager(em); + + assertThat(accessor.shouldUseAccessorFor(product), is(true)); + assertThat(accessor.getIdentifierFrom(product).toString(), is((Object) product.getId().toString())); + + return null; + } + }); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java similarity index 82% rename from src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java rename to src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java index 594eb2c88..41f809bd6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java +++ b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.support; +package org.springframework.data.jpa.provider; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.springframework.data.jpa.provider.PersistenceProvider.*; +import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*; import java.util.ArrayList; import java.util.List; @@ -28,6 +30,7 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.asm.ClassWriter; import org.springframework.asm.Opcodes; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.instrument.classloading.ShadowingClassLoader; import org.springframework.util.ClassUtils; @@ -35,27 +38,28 @@ import org.springframework.util.ClassUtils; * Tests for PersistenceProvider detection logic in {@link PersistenceProvider}. * * @author Thomas Darimont + * @author Oliver Gierke */ -public class PersistenceProviderTests { +public class PersistenceProviderUnitTests { - private ShadowingClassLoader shadowingClassLoader; + ShadowingClassLoader shadowingClassLoader; @Before public void setup() { - shadowingClassLoader = new ShadowingClassLoader(getClass().getClassLoader()); + this.shadowingClassLoader = new ShadowingClassLoader(getClass().getClassLoader()); } /** * @see DATAJPA-444 */ @Test - public void detectsHibernatePersistenceProviderForHibernateVersionLessThan4dot3() throws Exception { + public void detectsHibernatePersistenceProviderForHibernateVersionLessThan4Dot3() throws Exception { shadowingClassLoader.excludePackage("org.hibernate"); - EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE); + EntityManager em = mockProviderSpecificEntityManagerInterface(HIBERNATE_ENTITY_MANAGER_INTERFACE); - assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.HIBERNATE)); + assertThat(fromEntityManager(em), is(HIBERNATE)); } /** @@ -66,9 +70,9 @@ public class PersistenceProviderTests { shadowingClassLoader.excludePackage("org.hibernate"); - EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE); + EntityManager em = mockProviderSpecificEntityManagerInterface(HIBERNATE43_ENTITY_MANAGER_INTERFACE); - assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.HIBERNATE)); + assertThat(fromEntityManager(em), is(HIBERNATE)); } @Test @@ -76,9 +80,9 @@ public class PersistenceProviderTests { shadowingClassLoader.excludePackage("org.apache.openjpa.persistence"); - EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.OPENJPA_ENTITY_MANAGER_INTERFACE); + EntityManager em = mockProviderSpecificEntityManagerInterface(OPENJPA_ENTITY_MANAGER_INTERFACE); - assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.OPEN_JPA)); + assertThat(fromEntityManager(em), is(OPEN_JPA)); } @Test @@ -86,9 +90,9 @@ public class PersistenceProviderTests { shadowingClassLoader.excludePackage("org.eclipse.persistence.jpa"); - EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE); + EntityManager em = mockProviderSpecificEntityManagerInterface(ECLIPSELINK_ENTITY_MANAGER_INTERFACE); - assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.ECLIPSELINK)); + assertThat(fromEntityManager(em), is(ECLIPSELINK)); } @Test @@ -96,7 +100,7 @@ public class PersistenceProviderTests { EntityManager em = mockProviderSpecificEntityManagerInterface("foo.bar.unknown.jpa.JpaEntityManager"); - assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.GENERIC_JPA)); + assertThat(fromEntityManager(em), is(GENERIC_JPA)); } private EntityManager mockProviderSpecificEntityManagerInterface(String interfaceName) throws ClassNotFoundException { diff --git a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java similarity index 97% rename from src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataIntegrationTests.java rename to src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java index 8fe76aa15..3f3a20553 100644 --- a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java @@ -48,7 +48,7 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; * @author Thomas Darimont */ @RunWith(MockitoJUnitRunner.class) -public class CrudMethodMetadataIntegrationTests { +public class CrudMethodMetadataUnitTests { @Mock EntityManager em; @Mock EntityManagerFactory emf; @@ -65,6 +65,7 @@ public class CrudMethodMetadataIntegrationTests { when(information.getJavaType()).thenReturn(Role.class); + when(em.getDelegate()).thenReturn(em); when(em.getEntityManagerFactory()).thenReturn(emf); when(emf.createEntityManager()).thenReturn(em); diff --git a/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java index c4ca76702..5f4ad62f1 100644 --- a/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2014 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,7 +22,6 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.junit.Test; -import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertiesFactoryBean; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 24d07fd51..5fd9fe17f 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -34,11 +34,12 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.QueryHints; -import org.springframework.data.jpa.repository.support.PersistenceProvider; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.test.context.ContextConfiguration; 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 f3c11bbda..aed61b6d9 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 @@ -35,6 +35,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.NamedQueries; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 6e0db24d6..f948142c2 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -36,6 +36,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 3b6b7a1db..7bb346028 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -30,6 +30,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryCreationException; 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 a6f16b997..bf6f25f7a 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 @@ -42,8 +42,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.Temporal; -import org.springframework.data.jpa.repository.support.PersistenceProvider; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.Param; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index d2d0fbfb8..1d611115c 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -40,6 +40,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.repository.core.RepositoryMetadata; diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java new file mode 100644 index 000000000..a0844d396 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +import org.springframework.data.jpa.domain.sample.Category; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Oliver Gierke + */ +public interface CategoryRepository extends CrudRepository {} diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java new file mode 100644 index 000000000..7ff2c7a6c --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +import org.springframework.data.jpa.domain.sample.Product; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Oliver Gierke + */ +public interface ProductRepository extends JpaRepository { + +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java index 85efa364a..3590c8600 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java @@ -38,7 +38,8 @@ public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends */ @Test public void reactivatedDetectsIdTypeForMappedSuperclass() { - JpaEntityInformation information = JpaEntityInformationSupport.getMetadata(AbstractPersistable.class, em); + JpaEntityInformation information = JpaEntityInformationSupport.getEntityInformation( + AbstractPersistable.class, em); assertEquals(String.class, information.getIdType()); } @@ -56,10 +57,6 @@ public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends @Ignore public void detectsNewStateForEntityWithPrimitiveId() {} - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests#considersEntityWithUnSetCompundIdNew() - */ @Override @Ignore public void considersEntityWithUnsetCompundIdNew() {} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java new file mode 100644 index 000000000..f9146c00e --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import org.junit.Ignore; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.jpa.provider.PersistenceProviderIntegrationTests; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Oliver Gierke + */ +@ContextConfiguration(classes = EclipseLinkProxyIdAccessorTests.EclipseLinkConfig.class) +public class EclipseLinkProxyIdAccessorTests extends PersistenceProviderIntegrationTests { + + @Configuration + @ImportResource("classpath:eclipselink.xml") + static class EclipseLinkConfig {} + + /** + * Do not execute the test as EclipseLink does not create a lazy-loading proxy as expected. + */ + @Override + @Ignore + public void testname() {} +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index c16866a92..6ff5b9ae0 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -61,7 +61,7 @@ public class JpaEntityInformationSupportUnitTests { public void rejectsClassNotBeingFoundInMetamodel() { when(em.getMetamodel()).thenReturn(metaModel); - JpaEntityInformationSupport.getMetadata(User.class, em); + JpaEntityInformationSupport.getEntityInformation(User.class, em); } static class User { diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index d3340749f..0c39a5ef8 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.support; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*; import java.io.Serializable; import java.sql.Timestamp; @@ -43,7 +44,6 @@ import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK; import org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SampleWithIdClass; -import org.springframework.data.jpa.domain.sample.SampleWithIdClass.SampleWithIdClassPK; import org.springframework.data.jpa.domain.sample.SampleWithPrimitiveId; import org.springframework.data.jpa.domain.sample.SampleWithTimestampVersion; import org.springframework.data.jpa.domain.sample.User; @@ -68,7 +68,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Test public void detectsIdTypeForEntity() { - JpaEntityInformation information = JpaEntityInformationSupport.getMetadata(User.class, em); + JpaEntityInformation information = getEntityInformation(User.class, em); assertThat(information.getIdType(), is(typeCompatibleWith(Integer.class))); } @@ -83,7 +83,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Ignore public void detectsIdTypeForMappedSuperclass() { - JpaEntityInformation information = JpaEntityInformationSupport.getMetadata(AbstractPersistable.class, em); + JpaEntityInformation information = getEntityInformation(AbstractPersistable.class, em); assertEquals(Serializable.class, information.getIdType()); } @@ -93,8 +93,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Test public void detectsIdClass() { - EntityInformation information = JpaEntityInformationSupport.getMetadata( - PersistableWithIdClass.class, em); + EntityInformation information = getEntityInformation(PersistableWithIdClass.class, em); assertThat(information.getIdType(), is(typeCompatibleWith(PersistableWithIdClassPK.class))); } @@ -106,8 +105,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { PersistableWithIdClass entity = new PersistableWithIdClass(2L, 4L); - JpaEntityInformation information = JpaEntityInformationSupport.getMetadata( - PersistableWithIdClass.class, em); + JpaEntityInformation information = getEntityInformation(PersistableWithIdClass.class, em); Object id = information.getId(entity); assertThat(id, is(instanceOf(PersistableWithIdClassPK.class))); @@ -227,8 +225,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Test public void considersEntityWithUnsetCompundIdNew() { - EntityInformation information = new JpaMetamodelEntityInformation( - SampleWithIdClass.class, em.getMetamodel()); + EntityInformation information = getEntityInformation(SampleWithIdClass.class, em); assertThat(information.isNew(new SampleWithIdClass()), is(true)); } @@ -239,7 +236,8 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Test public void considersEntityWithSetTimestampVersionNotNew() { - EntityInformation information = getEntityInformation(SampleWithTimestampVersion.class); + EntityInformation information = getEntityInformation( + SampleWithTimestampVersion.class, em); SampleWithTimestampVersion entity = new SampleWithTimestampVersion(); entity.version = new Timestamp(new Date().getTime()); @@ -253,7 +251,7 @@ public class JpaMetamodelEntityInformationIntegrationTests { @Test public void considersEntityWithNonPrimitiveNonNullIdTypeNotNew() { - EntityInformation information = getEntityInformation(User.class); + EntityInformation information = getEntityInformation(User.class, em); User user = new User(); assertThat(information.isNew(user), is(true)); @@ -262,10 +260,6 @@ public class JpaMetamodelEntityInformationIntegrationTests { assertThat(information.isNew(user), is(false)); } - private EntityInformation getEntityInformation(Class domainType) { - return new JpaMetamodelEntityInformation(domainType, em.getMetamodel()); - } - protected String getMetadadataPersitenceUnitName() { return "metadata"; } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index e70767c99..c470443d7 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -52,27 +52,27 @@ public class JpaRepositoryFactoryUnitTests { JpaRepositoryFactory factory; @Mock EntityManager entityManager; - @Mock @SuppressWarnings("rawtypes") JpaEntityInformation metadata; + @Mock @SuppressWarnings("rawtypes") JpaEntityInformation entityInformation; @Mock EntityManagerFactory emf; @Before public void setUp() { + when(entityManager.getEntityManagerFactory()).thenReturn(emf); + when(entityManager.getDelegate()).thenReturn(entityManager); + when(emf.createEntityManager()).thenReturn(entityManager); + // Setup standard factory configuration factory = new JpaRepositoryFactory(entityManager) { @Override @SuppressWarnings("unchecked") public JpaEntityInformation getEntityInformation(Class domainClass) { - - return metadata; + return entityInformation; }; }; factory.setQueryLookupStrategyKey(Key.CREATE_IF_NOT_FOUND); - - when(entityManager.getEntityManagerFactory()).thenReturn(emf); - when(emf.createEntityManager()).thenReturn(entityManager); } /** @@ -140,7 +140,7 @@ public class JpaRepositoryFactoryUnitTests { @Test public void usesQueryDslRepositoryIfInterfaceImplementsExecutor() { - when(metadata.getJavaType()).thenReturn(User.class); + when(entityInformation.getJavaType()).thenReturn(User.class); assertEquals(QueryDslJpaRepository.class, factory.getRepositoryBaseClass(new DefaultRepositoryMetadata(QueryDslSampleRepository.class))); diff --git a/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java b/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java new file mode 100644 index 000000000..3b4723f16 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.jpa.provider.PersistenceProviderIntegrationTests; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Oliver Gierke + */ +@ContextConfiguration +public class OpenJpaProxyIdAccessorTests extends PersistenceProviderIntegrationTests { + + @Configuration + @ImportResource("classpath:openjpa.xml") + static class Config {} +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index 2e6e505d7..ecf07b181 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -53,6 +53,8 @@ public class SimpleJpaRepositoryUnitTests { @Before public void setUp() { + when(em.getDelegate()).thenReturn(em); + when(information.getJavaType()).thenReturn(User.class); when(em.getCriteriaBuilder()).thenReturn(builder); diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index e58401db3..212f4120a 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -10,6 +10,7 @@ org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser org.springframework.data.jpa.domain.sample.AuditableRole org.springframework.data.jpa.domain.sample.AuditableUser + org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.Child org.springframework.data.jpa.domain.sample.ConcreteType1 org.springframework.data.jpa.domain.sample.ConcreteType2 @@ -26,6 +27,7 @@ org.springframework.data.jpa.domain.sample.Parent org.springframework.data.jpa.domain.sample.PersistableWithIdClass org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty + org.springframework.data.jpa.domain.sample.Product org.springframework.data.jpa.domain.sample.Role org.springframework.data.jpa.domain.sample.SampleEntity org.springframework.data.jpa.domain.sample.SampleEntityPK diff --git a/src/test/resources/META-INF/persistence2.xml b/src/test/resources/META-INF/persistence2.xml index 50cd7584f..4ecbc5e16 100644 --- a/src/test/resources/META-INF/persistence2.xml +++ b/src/test/resources/META-INF/persistence2.xml @@ -6,24 +6,28 @@ org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser org.springframework.data.jpa.domain.sample.AuditableRole org.springframework.data.jpa.domain.sample.AuditableUser - org.springframework.data.jpa.domain.sample.User - org.springframework.data.jpa.domain.sample.SpecialUser - org.springframework.data.jpa.domain.sample.Role + org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.MailMessage org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.domain.sample.MailUser + org.springframework.data.jpa.domain.sample.Product + org.springframework.data.jpa.domain.sample.Role + org.springframework.data.jpa.domain.sample.SpecialUser + org.springframework.data.jpa.domain.sample.User true - org.springframework.data.jpa.domain.sample.User - org.springframework.data.jpa.domain.sample.SpecialUser - org.springframework.data.jpa.domain.sample.Role org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser org.springframework.data.jpa.domain.sample.AuditableUser org.springframework.data.jpa.domain.sample.AuditableRole + org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.MailMessage org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.domain.sample.MailUser + org.springframework.data.jpa.domain.sample.Product + org.springframework.data.jpa.domain.sample.Role + org.springframework.data.jpa.domain.sample.SpecialUser + org.springframework.data.jpa.domain.sample.User true diff --git a/template.mf b/template.mf index 42028fd79..95ee501d7 100644 --- a/template.mf +++ b/template.mf @@ -12,7 +12,7 @@ Import-Template: javax.annotation.*;version="0.0.0", javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0,2.0.0)", - org.apache.openjpa.persistence.*;version="${openjpa:[=.=.=,+1.0.0)}";resolution:=optional, + org.apache.openjpa.*;version="${openjpa:[=.=.=,+1.0.0)}";resolution:=optional, org.aspectj.*;version="${aspectj:[=.=.=,+1.0.0)}";resolution:=optional, org.eclipse.persistence.*;version="${eclipselink:[=.=.=,+1.0.0)}";resolution:=optional, org.hibernate.*;version="[3.6.10,4.4.0)";resolution:=optional,