diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 624658ad9..aad1c66c8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -17,8 +17,14 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.EntityManager; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution; import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.data.repository.support.EntityMetadata; import org.springframework.util.Assert; @@ -29,8 +35,7 @@ import org.springframework.util.Assert; */ public abstract class AbstractJpaQuery implements RepositoryQuery { - private final Parameters parameters; - private final JpaQueryExecution execution; + private final JpaQueryMethod method; private final EntityManager em; @@ -46,18 +51,30 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { Assert.notNull(method); Assert.notNull(em); - this.parameters = method.getParameters(); - this.execution = method.getExecution(); + this.method = method; this.em = em; } + /* + * (non-Javadoc) + * + * @see + * org.springframework.data.repository.query.RepositoryQuery#getQueryMethod + * () + */ + public QueryMethod getQueryMethod() { + + return method; + } + + /** * @return the parameters */ public Parameters getParameters() { - return parameters; + return method.getParameters(); } @@ -79,7 +96,25 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { */ public Object execute(Object[] parameters) { - return doExecute(execution, parameters); + return doExecute(getExecution(), parameters); + } + + + protected JpaQueryExecution getExecution() { + + switch (method.getType()) { + + case COLLECTION: + return new CollectionExecution(); + case PAGING: + return new PagedExecution(getParameters()); + case MODIFYING: + EntityMetadata metadata = method.getEntityMetadata(); + return method.getClearAutomatically() ? new ModifyingExecution( + metadata, em) : new ModifyingExecution(metadata, null); + default: + return new SingleEntityExecution(); + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 154186ac4..01a181367 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import java.lang.reflect.Method; - import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; @@ -27,6 +25,7 @@ import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.support.EntityMetadata; import org.springframework.util.Assert; @@ -219,9 +218,9 @@ public abstract class JpaQueryExecution { * * @param em */ - public ModifyingExecution(Method method, EntityManager em) { + public ModifyingExecution(EntityMetadata metadata, EntityManager em) { - Class type = method.getReturnType(); + Class type = metadata.getJavaType(); boolean isVoid = void.class.equals(type) || Void.class.equals(type); boolean isInt = 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 2a88a428a..d6dd1519d 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 @@ -70,7 +70,7 @@ public final class JpaQueryLookupStrategy { public final RepositoryQuery resolveQuery(Method method, Class domainClass) { - return resolveQuery(new JpaQueryMethod(method, provider, em), em); + return resolveQuery(new JpaQueryMethod(method, provider), em); } 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 48f2df5a8..9055843e4 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 @@ -22,17 +22,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.persistence.EntityManager; import javax.persistence.QueryHint; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryHints; -import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution; -import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution; -import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution; -import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethod; import org.springframework.util.Assert; @@ -47,7 +42,6 @@ import org.springframework.util.StringUtils; public class JpaQueryMethod extends QueryMethod { private final QueryExtractor extractor; - private final EntityManager em; private final Method method; @@ -56,55 +50,26 @@ public class JpaQueryMethod extends QueryMethod { * * @param method must not be {@literal null} * @param extractor must not be {@literal null} - * @param em must not be {@literal null} + * @param metadata must not be {@literal null} */ - public JpaQueryMethod(Method method, QueryExtractor extractor, - EntityManager em) { + public JpaQueryMethod(Method method, QueryExtractor extractor) { super(method); Assert.notNull(method, "Method must not be null!"); Assert.notNull(extractor, "Query extractor must not be null!"); - Assert.notNull(em, "EntityManager must not be null!"); this.method = method; this.extractor = extractor; - this.em = em; Assert.isTrue(!(isModifyingQuery() && getParameters() .hasSpecialParameter()), String.format( "Modifying method must not contain %s!", Parameters.TYPES)); - if (getParameters().hasPageableParameter() - && !extractor.canExtractQuery()) { - throw new IllegalArgumentException( - "You cannot use Pageable as method parameter if your " - + "persistence provider cannot extract queries!"); - } - } - - - /** - * Returns the {@link JpaQueryExecution}. - * - * @return - */ - public JpaQueryExecution getExecution() { - - if (isCollectionQuery()) { - return new CollectionExecution(); - } - - if (isPageQuery()) { - return new PagedExecution(getParameters()); - } - - if (isModifyingQuery()) { - return getClearAutomatically() ? new ModifyingExecution(method, em) - : new ModifyingExecution(method, null); - } - - return new SingleEntityExecution(); + Assert.isTrue(!(getParameters().hasPageableParameter() && !extractor + .canExtractQuery()), + "You cannot use Pageable as method parameter if your " + + "persistence provider cannot extract queries!"); } @@ -113,7 +78,8 @@ public class JpaQueryMethod extends QueryMethod { * * @return */ - final boolean isModifyingQuery() { + @Override + protected boolean isModifyingQuery() { return null != AnnotationUtils.findAnnotation(method, Modifying.class); } @@ -157,7 +123,8 @@ public class JpaQueryMethod extends QueryMethod { */ String getNamedQueryName() { - return String.format("%s.%s", getDomainClass().getSimpleName(), + Class domainClass = getDomainClass(); + return String.format("%s.%s", domainClass.getSimpleName(), method.getName()); } @@ -192,6 +159,18 @@ public class JpaQueryMethod extends QueryMethod { } + /** + * Returns whether we should clear automatically for modifying queries. + * + * @return + */ + boolean getClearAutomatically() { + + return (Boolean) AnnotationUtils.getValue( + method.getAnnotation(Modifying.class), "clearAutomatically"); + } + + /** * Returns the {@link Query} annotation that is applied to the method or * {@code null} if none available. @@ -202,16 +181,4 @@ public class JpaQueryMethod extends QueryMethod { return method.getAnnotation(Query.class); } - - - /** - * Returns whether we should clear automatically for modifying queries. - * - * @return - */ - private boolean getClearAutomatically() { - - return (Boolean) AnnotationUtils.getValue( - method.getAnnotation(Modifying.class), "clearAutomatically"); - } } 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 a55edc949..40df3b45a 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 @@ -23,7 +23,9 @@ import javax.persistence.criteria.CriteriaQuery; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.data.repository.support.EntityMetadata; /** @@ -34,7 +36,7 @@ import org.springframework.data.repository.query.parser.PartTree; public class PartTreeJpaQuery extends AbstractJpaQuery { private final PartTree tree; - private final Class domainClass; + private final QueryMethod method; /** @@ -46,9 +48,10 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) { super(method, em); - - this.tree = new PartTree(method.getName(), method.getDomainClass()); - this.domainClass = method.getDomainClass(); + this.tree = + new PartTree(method.getName(), method.getEntityMetadata() + .getJavaType()); + this.method = method; } @@ -65,8 +68,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { ParameterAccessor accessor = new ParametersParameterAccessor(getParameters(), parameters); + EntityMetadata metadata = method.getEntityMetadata(); JpaQueryCreator jpaQueryCreator = - new JpaQueryCreator(tree, accessor, domainClass, + new JpaQueryCreator(tree, accessor, metadata.getJavaType(), getEntityManager()); TypedQuery query = @@ -92,8 +96,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { CriteriaQuery query = new JpaCountQueryCreator(tree, new ParametersParameterAccessor( - getParameters(), parameters), domainClass, - getEntityManager()).createQuery(); + getParameters(), parameters), method + .getEntityMetadata().getJavaType(), getEntityManager()) + .createQuery(); return getEntityManager().createQuery(query); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java new file mode 100644 index 000000000..343ae229e --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -0,0 +1,37 @@ +/* + * Copyright 2011 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 javax.persistence.metamodel.SingularAttribute; + +import org.springframework.data.repository.support.EntityInformation; + + +/** + * Extension of {@link EntityInformation} to capture aditional JPA specific + * information about entities. + * + * @author Oliver Gierke + */ +public interface JpaEntityInformation extends EntityInformation { + + /** + * Returns the id attribute of the entity. + * + * @return + */ + SingularAttribute getIdAttribute(); +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityMetadata.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java similarity index 68% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityMetadata.java rename to src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index 3b9dc6385..40b16178d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -23,7 +23,8 @@ import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute; -import org.springframework.data.repository.support.AbstractEntityMetadata; +import org.springframework.data.repository.support.AbstractEntityInformation; +import org.springframework.data.repository.support.EntityInformation; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -34,27 +35,32 @@ import org.springframework.util.ReflectionUtils; * * @author Oliver Gierke */ -public class JpaMetamodelEntityMetadata extends AbstractEntityMetadata { +public class JpaMetamodelEntityInformation extends AbstractEntityInformation + implements JpaEntityInformation { - private final Member member; + private final SingularAttribute attribute; /** - * Creates a new {@link JpaMetamodelEntityMetadata} for the given domain + * Creates a new {@link JpaMetamodelEntityInformation} for the given domain * class and {@link Metamodel}. * * @param domainClass * @param metamodel */ - public JpaMetamodelEntityMetadata(Class domainClass, Metamodel metamodel) { + public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) { super(domainClass); Assert.notNull(metamodel); - EntityType type = metamodel.entity(domainClass); - SingularAttribute idAttribute = - type.getId(type.getIdType().getJavaType()); - this.member = idAttribute.getJavaMember(); + EntityType type = metamodel.entity(domainClass); + + if (type == null) { + throw new IllegalArgumentException( + "The given domain class can not be found in the given Metamodel!"); + } + + this.attribute = type.getId(type.getIdType().getJavaType()); } @@ -65,9 +71,9 @@ public class JpaMetamodelEntityMetadata extends AbstractEntityMetadata { * org.springframework.data.repository.support.IdAware#getId(java.lang.Object * ) */ - public Object getId(Object entity) { + public Object getId(T entity) { - return getMemberValue(member, entity); + return getMemberValue(attribute.getJavaMember(), entity); } @@ -94,4 +100,16 @@ public class JpaMetamodelEntityMetadata extends AbstractEntityMetadata { throw new IllegalArgumentException( "Given member is neither Field nor Method!"); } + + + /* + * (non-Javadoc) + * + * @see org.springframework.data.jpa.repository.support.JpaEntityMetadata# + * getIdAttribute() + */ + public SingularAttribute getIdAttribute() { + + return attribute; + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java new file mode 100644 index 000000000..7bc9ccbe3 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -0,0 +1,60 @@ +/* + * Copyright 2011 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 java.io.Serializable; + +import javax.persistence.metamodel.Metamodel; + +import org.springframework.data.domain.Persistable; + + +/** + * Extension of {@link JpaMetamodelEntityInformation} that consideres methods of + * {@link Persistable} to lookup the id. + * + * @author Oliver Gierke + */ +public class JpaPersistableEntityInformation extends + JpaMetamodelEntityInformation { + + /** + * Creates a new {@link JpaPersistableEntityInformation} for the given + * domain class and {@link Metamodel}. + * + * @param domainClass + * @param metamodel + */ + public JpaPersistableEntityInformation(Class domainClass, + Metamodel metamodel) { + + super(domainClass, metamodel); + } + + + /* + * (non-Javadoc) + * + * @see + * org.springframework.data.jpa.repository.support.JpaMetamodelEntityMetadata + * #getId(java.lang.Object) + */ + @Override + public Serializable getId(T entity) { + + return entity.getId(); + } +} 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 0d160f10c..7d7297602 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 @@ -17,13 +17,11 @@ package org.springframework.data.jpa.repository.support; import javax.persistence.EntityManager; -import org.springframework.data.domain.Persistable; import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; import org.springframework.data.jpa.repository.query.QueryExtractor; +import org.springframework.data.jpa.repository.utils.JpaClassUtils; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; -import org.springframework.data.repository.support.EntityMetadata; -import org.springframework.data.repository.support.PersistableEntityMetadata; import org.springframework.data.repository.support.RepositoryFactorySupport; import org.springframework.data.repository.support.RepositoryMetadata; import org.springframework.util.Assert; @@ -81,32 +79,15 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { protected Object getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) { - return new SimpleJpaRepository(createEntityInformation( - metadata.getDomainClass(), entityManager), entityManager); + JpaEntityInformation entityMetadata = + getEntityMetadata(metadata.getDomainClass()); + return new SimpleJpaRepository(entityMetadata, entityManager); } - /** - * Creates a new {@link EntityMetadata} instance for the given domain class - * and {@link EntityManager}. Default implementation will use a - * {@link PersistableMetadata} for domain classes implementing - * {@link Persistable} and fall back to the JPA meta model though - * {@link JpaMetamodelEntityInformation} otherwise. - * - * @param domainClass - * @param em - * @return - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected EntityMetadata createEntityInformation(Class domainClass, - EntityManager em) { + protected JpaEntityInformation getEntityMetadata(Class domainClass) { - if (Persistable.class.isAssignableFrom(domainClass)) { - return new PersistableEntityMetadata(domainClass); - } else { - return new JpaMetamodelEntityMetadata(domainClass, - em.getMetamodel()); - } + return JpaClassUtils.getMetadata(domainClass, entityManager); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index 311adc47e..9a05a8d83 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -39,17 +39,6 @@ public class JpaRepositoryFactoryBean> extends private EntityManager entityManager; - public static > JpaRepositoryFactoryBean create( - Class repositoryInterface, EntityManager em) { - - JpaRepositoryFactoryBean factory = new JpaRepositoryFactoryBean(); - factory.setRepositoryInterface(repositoryInterface); - factory.setEntityManager(em); - - return factory; - } - - /** * The {@link EntityManager} to be used. * @@ -65,12 +54,11 @@ public class JpaRepositoryFactoryBean> extends /* * (non-Javadoc) * - * @see - * org.springframework.data.repository.support.RepositoryFactoryBeanSupport - * #createRepositoryFactory() + * @see org.springframework.data.repository.support. + * TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory() */ @Override - protected RepositoryFactorySupport createRepositoryFactory() { + protected RepositoryFactorySupport doCreateRepositoryFactory() { return createRepositoryFactory(entityManager); } 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 6119a67c1..4e956f43a 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 @@ -19,6 +19,7 @@ import static org.springframework.data.jpa.repository.query.QueryUtils.*; import java.io.Serializable; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import javax.persistence.EntityManager; @@ -26,6 +27,7 @@ import javax.persistence.NoResultException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -36,7 +38,6 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.Repository; -import org.springframework.data.repository.support.EntityMetadata; import org.springframework.util.Assert; @@ -53,7 +54,7 @@ import org.springframework.util.Assert; public class SimpleJpaRepository implements JpaRepository { - private final EntityMetadata entityInformation; + private final JpaEntityInformation entityInformation; private final EntityManager em; private final PersistenceProvider provider; @@ -62,15 +63,15 @@ public class SimpleJpaRepository implements * Creates a new {@link SimpleJpaRepository} to manage objects of the given * domain type. * - * @param entityInformation + * @param entityMetadata * @param entityManager */ - public SimpleJpaRepository(EntityMetadata entityInformation, + public SimpleJpaRepository(JpaEntityInformation entityMetadata, EntityManager entityManager) { - Assert.notNull(entityInformation); + Assert.notNull(entityMetadata); Assert.notNull(entityManager); - this.entityInformation = entityInformation; + this.entityInformation = entityMetadata; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); } diff --git a/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java b/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java index de4561639..ce6f30ab4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/utils/JpaClassUtils.java @@ -17,7 +17,12 @@ package org.springframework.data.jpa.repository.utils; import javax.persistence.Entity; import javax.persistence.EntityManager; +import javax.persistence.metamodel.Metamodel; +import org.springframework.data.domain.Persistable; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation; +import org.springframework.data.jpa.repository.support.JpaPersistableEntityInformation; import org.springframework.util.StringUtils; @@ -75,4 +80,30 @@ public abstract class JpaClassUtils { return hasName ? entity.name() : domainClass.getSimpleName(); } + + + /** + * Creates a {@link JpaEntityInformation} for the given domain class and + * {@link EntityManager}. + * + * @param domainClass + * @param em + * @return + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static JpaEntityInformation getMetadata(Class domainClass, + EntityManager em) { + + Metamodel metamodel = em.getMetamodel(); + + if (Persistable.class.isAssignableFrom(domainClass)) { + return new JpaPersistableEntityInformation(domainClass, metamodel); + } else { + try { + return new JpaMetamodelEntityInformation(domainClass, metamodel); + } catch (IllegalArgumentException e) { + return null; + } + } + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java index ccf822904..01803d4e6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java @@ -19,8 +19,8 @@ import java.io.Serializable; import javax.persistence.EntityManager; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; -import org.springframework.data.repository.support.EntityMetadata; /** @@ -36,7 +36,7 @@ public class CustomGenericJpaRepository extends * @param domainClass * @param entityManager */ - public CustomGenericJpaRepository(EntityMetadata metadata, + public CustomGenericJpaRepository(JpaEntityInformation metadata, EntityManager entityManager) { super(metadata, entityManager); diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 93d41f310..13c24e834 100644 --- a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -21,8 +21,8 @@ import java.io.Serializable; import javax.persistence.EntityManager; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; -import org.springframework.data.repository.support.EntityMetadata; import org.springframework.data.repository.support.RepositoryMetadata; @@ -55,7 +55,8 @@ public class CustomGenericJpaRepositoryFactory extends JpaRepositoryFactory { protected Object getTargetRepository(RepositoryMetadata metadata, EntityManager em) { - EntityMetadata entityMetadata = mock(EntityMetadata.class); + JpaEntityInformation entityMetadata = + mock(JpaEntityInformation.class); when(entityMetadata.getJavaType()).thenReturn( (Class) metadata.getDomainClass()); return new CustomGenericJpaRepository( diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 28ba38515..35f13cffd 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -20,18 +20,17 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; - import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.OngoingStubbing; import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution; +import org.springframework.data.repository.support.EntityMetadata; /** @@ -50,15 +49,8 @@ public class JpaQueryExecutionUnitTests { ParameterBinder binder; @Mock Query query; - - Method method; - - - @Before - public void setUp() throws Exception { - - method = Dummy.class.getMethod("voidMethod"); - } + @Mock + EntityMetadata metadata; @Test(expected = IllegalArgumentException.class) @@ -113,20 +105,32 @@ public class JpaQueryExecutionUnitTests { Query param = any(); when(binder.bind(param)).thenReturn(query); when(query.executeUpdate()).thenReturn(0); + mock(metadata, void.class); - ModifyingExecution execution = new ModifyingExecution(method, em); + ModifyingExecution execution = new ModifyingExecution(metadata, em); execution.execute(jpaQuery, binder); verify(em, times(1)).clear(); } + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void mock(EntityMetadata method, Class type, + Class... others) { + + OngoingStubbing stubbing = when(method.getJavaType()); + stubbing.thenReturn(type); + } + + @Test public void allowsMethodReturnTypesForModifyingQuery() throws Exception { - new ModifyingExecution(Dummy.class.getMethod("voidMethod"), em); - new ModifyingExecution(Dummy.class.getMethod("intMethod"), em); - new ModifyingExecution(Dummy.class.getMethod("integerMethod"), em); + mock(metadata, void.class, int.class, Integer.class); + + new ModifyingExecution(metadata, em); + new ModifyingExecution(metadata, em); + new ModifyingExecution(metadata, em); } @@ -134,7 +138,8 @@ public class JpaQueryExecutionUnitTests { public void modifyingExecutionRejectsNonIntegerOrVoidReturnType() throws Exception { - new ModifyingExecution(Dummy.class.getMethod("longMethod"), em); + mock(metadata, Long.class); + new ModifyingExecution(metadata, em); } static class StubQueryExecution extends JpaQueryExecution { @@ -153,18 +158,4 @@ public class JpaQueryExecutionUnitTests { return null; } } - - static interface Dummy { - - void voidMethod(); - - - int intMethod(); - - - Integer integerMethod(); - - - Long longMethod(); - } } 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 0106e8d1a..728b4ddd1 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 @@ -22,7 +22,6 @@ import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.List; -import javax.persistence.EntityManager; import javax.persistence.QueryHint; import org.junit.Before; @@ -35,9 +34,9 @@ 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.repository.Modifying; -import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.QueryMethod.Type; /** @@ -53,8 +52,6 @@ public class JpaQueryMethodUnitTests { @Mock QueryExtractor extractor; - @Mock - EntityManager em; Method repositoryMethod, invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, modifyingMethod; @@ -91,40 +88,31 @@ public class JpaQueryMethodUnitTests { @Test public void testname() { - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); + JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, extractor); assertEquals("User.findByLastname", method.getNamedQueryName()); - assertThat(method.getExecution(), is(CollectionExecution.class)); + assertThat(method.getType(), is(Type.COLLECTION)); } @Test(expected = IllegalArgumentException.class) public void preventsNullRepositoryMethod() { - new JpaQueryMethod(null, extractor, em); - } - - - @Test(expected = IllegalArgumentException.class) - public void preventsNullEntityManager() { - - new JpaQueryMethod(repositoryMethod, extractor, null); + new JpaQueryMethod(null, extractor); } @Test(expected = IllegalArgumentException.class) public void preventsNullQueryExtractor() { - new JpaQueryMethod(repositoryMethod, null, em); + new JpaQueryMethod(repositoryMethod, null); } @Test public void returnsCorrectName() { - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); + JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, extractor); assertEquals(repositoryMethod.getName(), method.getName()); } @@ -132,8 +120,7 @@ public class JpaQueryMethodUnitTests { @Test public void returnsQueryIfAvailable() throws Exception { - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); + JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, extractor); assertNull(method.getAnnotatedQuery()); @@ -141,55 +128,36 @@ public class JpaQueryMethodUnitTests { UserRepository.class.getMethod("findByAnnotatedQuery", String.class); - assertNotNull(new JpaQueryMethod(repositoryMethod, extractor, em) + assertNotNull(new JpaQueryMethod(repositoryMethod, extractor) .getAnnotatedQuery()); } - @Test - public void returnsCorrectDomainClassName() { - - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); - assertEquals(DOMAIN_CLASS, method.getDomainClass()); - } - - - @Test - public void returnsCorrectNumberOfParameters() { - - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); - assertTrue(method.isCorrectNumberOfParameters(repositoryMethod - .getParameterTypes().length)); - } - - @Test(expected = IllegalStateException.class) public void rejectsInvalidReturntypeOnPagebleFinder() { - new JpaQueryMethod(invalidReturnType, extractor, em); + new JpaQueryMethod(invalidReturnType, extractor); } @Test(expected = IllegalStateException.class) public void rejectsPageableAndSortInFinderMethod() { - new JpaQueryMethod(pageableAndSort, extractor, em); + new JpaQueryMethod(pageableAndSort, extractor); } @Test(expected = IllegalStateException.class) public void rejectsTwoPageableParameters() { - new JpaQueryMethod(pageableTwice, extractor, em); + new JpaQueryMethod(pageableTwice, extractor); } @Test(expected = IllegalStateException.class) public void rejectsTwoSortableParameters() { - new JpaQueryMethod(sortableTwice, extractor, em); + new JpaQueryMethod(sortableTwice, extractor); } @@ -203,15 +171,14 @@ public class JpaQueryMethodUnitTests { when(extractor.canExtractQuery()).thenReturn(false); - new JpaQueryMethod(method, extractor, em); + new JpaQueryMethod(method, extractor); } @Test public void recognizesModifyingMethod() { - JpaQueryMethod method = - new JpaQueryMethod(modifyingMethod, extractor, em); + JpaQueryMethod method = new JpaQueryMethod(modifyingMethod, extractor); assertTrue(method.isModifyingQuery()); } @@ -223,7 +190,7 @@ public class JpaQueryMethodUnitTests { InvalidRepository.class.getMethod("updateMethod", String.class, Pageable.class); - new JpaQueryMethod(method, extractor, em); + new JpaQueryMethod(method, extractor); } @@ -234,15 +201,14 @@ public class JpaQueryMethodUnitTests { InvalidRepository.class.getMethod("updateMethod", String.class, Sort.class); - new JpaQueryMethod(method, extractor, em); + new JpaQueryMethod(method, extractor); } @Test public void discoversHintsCorrectly() { - JpaQueryMethod method = - new JpaQueryMethod(repositoryMethod, extractor, em); + JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, extractor); List hints = method.getHints(); assertNotNull(hints); 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 27fe2d69e..26e34ee56 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 @@ -43,14 +43,14 @@ import org.springframework.data.jpa.repository.sample.UserRepository; @RunWith(MockitoJUnitRunner.class) public class SimpleJpaQueryUnitTests { - private JpaQueryMethod method; + JpaQueryMethod method; @Mock - private EntityManager em; + EntityManager em; @Mock - private QueryExtractor extractor; + QueryExtractor extractor; @Mock - private Query query; + Query query; @Before @@ -61,7 +61,7 @@ public class SimpleJpaQueryUnitTests { Method setUp = UserRepository.class.getMethod("findByLastname", String.class); - method = new JpaQueryMethod(setUp, extractor, em); + method = new JpaQueryMethod(setUp, extractor); } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index 68e3422ce..d07bd60f3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -34,6 +34,8 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.domain.Persistable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.support.RepositoryFactorySupport; /** @@ -47,18 +49,22 @@ import org.springframework.data.jpa.repository.JpaRepository; @RunWith(MockitoJUnitRunner.class) public class JpaRepositoryFactoryBeanUnitTests { - JpaRepositoryFactoryBean factory; + JpaRepositoryFactoryBean factoryBean; @Mock EntityManager entityManager; - + @Mock + RepositoryFactorySupport factory; @Mock ListableBeanFactory beanFactory; @Mock PersistenceExceptionTranslator translator; + @Mock + Repository repository; @Before + @SuppressWarnings("unchecked") public void setUp() { Map beans = @@ -68,12 +74,14 @@ public class JpaRepositoryFactoryBeanUnitTests { beanFactory.getBeansOfType( eq(PersistenceExceptionTranslator.class), anyBoolean(), anyBoolean())).thenReturn(beans); + when(factory.getRepository(any(Class.class), any(Object.class))) + .thenReturn(repository); // Setup standard factory configuration - factory = - JpaRepositoryFactoryBean.create(SimpleSampleRepository.class, - entityManager); - factory.setEntityManager(entityManager); + factoryBean = + new DummyJpaRepositoryFactoryBean(); + factoryBean.setRepositoryInterface(SimpleSampleRepository.class); + factoryBean.setEntityManager(entityManager); } @@ -86,17 +94,17 @@ public class JpaRepositoryFactoryBeanUnitTests { @Test public void setsUpBasicInstanceCorrectly() throws Exception { - factory.setBeanFactory(beanFactory); - factory.afterPropertiesSet(); + factoryBean.setBeanFactory(beanFactory); + factoryBean.afterPropertiesSet(); - assertNotNull(factory.getObject()); + assertNotNull(factoryBean.getObject()); } @Test(expected = IllegalArgumentException.class) public void requiresListableBeanFactory() throws Exception { - factory.setBeanFactory(mock(BeanFactory.class)); + factoryBean.setBeanFactory(mock(BeanFactory.class)); } @@ -109,7 +117,7 @@ public class JpaRepositoryFactoryBeanUnitTests { @Test(expected = IllegalArgumentException.class) public void preventsNullRepositoryInterface() { - factory.setRepositoryInterface(null); + factoryBean.setRepositoryInterface(null); } @@ -120,8 +128,25 @@ public class JpaRepositoryFactoryBeanUnitTests { @Test(expected = IllegalArgumentException.class) public void preventsUnsetRepositoryInterface() throws Exception { - factory = new JpaRepositoryFactoryBean(); - factory.afterPropertiesSet(); + factoryBean = new JpaRepositoryFactoryBean(); + factoryBean.afterPropertiesSet(); + } + + private class DummyJpaRepositoryFactoryBean> + extends JpaRepositoryFactoryBean { + + /* + * (non-Javadoc) + * + * @see + * org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean + * #createRepositoryFactory() + */ + @Override + protected RepositoryFactorySupport doCreateRepositoryFactory() { + + return factory; + } } private interface SimpleSampleRepository extends 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 3c881733e..512a5cd72 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 @@ -30,7 +30,6 @@ import org.springframework.data.domain.Persistable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.custom.CustomGenericJpaRepositoryFactory; import org.springframework.data.jpa.repository.custom.UserCustomExtendedRepository; -import org.springframework.transaction.annotation.Transactional; /** @@ -45,13 +44,23 @@ public class JpaRepositoryFactoryUnitTests { @Mock EntityManager entityManager; + @Mock + JpaEntityInformation metadata; @Before public void setUp() { // Setup standard factory configuration - factory = new JpaRepositoryFactory(entityManager); + factory = new JpaRepositoryFactory(entityManager) { + + @Override + protected JpaEntityInformation getEntityMetadata( + java.lang.Class domainClass) { + + return metadata; + } + }; } @@ -136,8 +145,6 @@ public class JpaRepositoryFactoryUnitTests { private interface SimpleSampleRepository extends JpaRepository { - @Transactional - User readByPrimaryKey(Integer primaryKey); } /**