From 00ece2c25ec6220f2458f3e397fcbf2181d0afce Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 21 Jul 2011 12:18:37 +0200 Subject: [PATCH] Polished codebase and added architecture management. Added Sonargraph architecture description. Removed cyclic package dependency. Introduced JpaEntityInformationSupport to contain common getEntityName() method and serving as factory for JpaEntityInformation instances. --- Spring Data JPA.sonargraph | 216 ++++++++++++++++++ .../data/jpa/repository/JpaRepository.java | 4 +- .../jpa/repository/query/JpaQueryCreator.java | 1 - .../data/jpa/repository/query/QueryUtils.java | 24 +- .../support/JpaEntityInformation.java | 8 + .../support/JpaEntityInformationSupport.java | 92 ++++++++ .../JpaMetamodelEntityInformation.java | 4 +- .../support/JpaRepositoryFactory.java | 10 +- .../support/SimpleJpaRepository.java | 21 +- .../jpa/repository/utils/JpaClassUtils.java | 50 ---- .../CustomGenericJpaRepositoryFactory.java | 5 +- .../JpaEntityInformationSupportUnitTests.java | 86 +++++++ .../util/JpaClassUtilsUnitTests.java | 46 ---- 13 files changed, 430 insertions(+), 137 deletions(-) create mode 100644 Spring Data JPA.sonargraph create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java delete mode 100644 src/test/java/org/springframework/data/jpa/repository/util/JpaClassUtilsUnitTests.java diff --git a/Spring Data JPA.sonargraph b/Spring Data JPA.sonargraph new file mode 100644 index 000000000..9a9fd815a --- /dev/null +++ b/Spring Data JPA.sonargraph @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index 2d4a453e4..e0c769d87 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -22,11 +22,11 @@ import javax.persistence.EntityManager; import org.springframework.data.domain.Sort; import org.springframework.data.repository.PagingAndSortingRepository; -import org.springframework.data.repository.CrudRepository; /** - * JPA specific extension of {@link Repository}. + * JPA specific extension of + * {@link org.springframework.data.repository.Repository}. * * @author Oliver Gierke */ diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index c10ffffd0..04395e7ac 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -34,7 +34,6 @@ import javax.persistence.criteria.Root; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.Parameter; -import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 907b948e2..b1d385741 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -16,7 +16,6 @@ package org.springframework.data.jpa.repository.query; import static java.util.regex.Pattern.*; -import static org.springframework.data.jpa.repository.utils.JpaClassUtils.*; import java.util.ArrayList; import java.util.Iterator; @@ -91,33 +90,18 @@ public abstract class QueryUtils { } - /** - * Returns the query string for the given class. - * - * @return - */ - public static String getQueryString(String template, Class clazz) { - - if (null == clazz) { - throw new IllegalArgumentException("Class must not be null!"); - } - - return getQueryString(template, getEntityName(clazz)); - } - - /** * Returns the query string for the given class name. * * @param template - * @param clazzName + * @param entityName * @return */ - private static String getQueryString(String template, String clazzName) { + public static String getQueryString(String template, String entityName) { - Assert.hasText(clazzName, "Classname must not be null or empty!"); + Assert.hasText(entityName, "Entity name must not be null or empty!"); - return String.format(template, clazzName); + return String.format(template, entityName); } 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 index 35b381f34..674ef7c52 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -37,4 +37,12 @@ public interface JpaEntityInformation extends * @return */ SingularAttribute getIdAttribute(); + + + /** + * Returns the JPA entity name. + * + * @return + */ + String getEntityName(); } 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 new file mode 100644 index 000000000..e8d3696e3 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -0,0 +1,92 @@ +/* + * 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.Entity; +import javax.persistence.EntityManager; +import javax.persistence.metamodel.Metamodel; + +import org.springframework.data.domain.Persistable; +import org.springframework.data.repository.core.support.AbstractEntityInformation; +import org.springframework.util.StringUtils; + + +/** + * Base class for {@link JpaEntityInformation} implementations to share common + * method implementations. + * + * @author Oliver Gierke + */ +public abstract class JpaEntityInformationSupport + extends AbstractEntityInformation implements + JpaEntityInformation { + + /** + * Creates a new {@link JpaEntityInformationSupport} with the given domain + * class. + * + * @param domainClass + */ + public JpaEntityInformationSupport(Class domainClass) { + + super(domainClass); + } + + + /** + * 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; + } + } + } + + + /* + * (non-Javadoc) + * + * @see + * org.springframework.data.jpa.repository.support.JpaEntityInformation# + * getEntityName() + */ + public String getEntityName() { + + Class domainClass = getJavaType(); + Entity entity = domainClass.getAnnotation(Entity.class); + boolean hasName = null != entity && StringUtils.hasText(entity.name()); + + return hasName ? entity.name() : domainClass.getSimpleName(); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index b58ae3151..b0eeddfb9 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -25,7 +25,6 @@ import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.data.repository.core.support.AbstractEntityInformation; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -37,7 +36,8 @@ import org.springframework.util.ReflectionUtils; * @author Oliver Gierke */ public class JpaMetamodelEntityInformation extends - AbstractEntityInformation implements JpaEntityInformation { + JpaEntityInformationSupport implements + JpaEntityInformation { private final SingularAttribute attribute; 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 98aaddea8..d7617f183 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,9 @@ import java.io.Serializable; import javax.persistence.EntityManager; +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.jpa.repository.utils.JpaClassUtils; import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; @@ -71,7 +71,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { /** - * Callback to create a {@link RepositorySupport} instance with the given + * Callback to create a {@link JpaRepository} instance with the given * {@link EntityManager} * * @param @@ -81,7 +81,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) - protected Object getTargetRepository( + protected JpaRepository getTargetRepository( RepositoryMetadata metadata, EntityManager entityManager) { Class repositoryInterface = metadata.getRepositoryInterface(); @@ -156,7 +156,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { public JpaEntityInformation getEntityInformation( Class domainClass) { - return (JpaEntityInformation) JpaClassUtils.getMetadata( - domainClass, entityManager); + return (JpaEntityInformation) JpaEntityInformationSupport + .getMetadata(domainClass, 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 a88d6b8c2..5790d34c4 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 @@ -36,15 +36,15 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.jpa.repository.utils.JpaClassUtils; -import org.springframework.transaction.annotation.Transactional; import org.springframework.data.repository.CrudRepository; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; /** - * Default implementation of the {@link CrudRepository} interface. This will offer - * you a more sophisticated interface than the plain {@link EntityManager} . + * Default implementation of the {@link CrudRepository} interface. This will + * offer you a more sophisticated interface than the plain {@link EntityManager} + * . * * @author Oliver Gierke * @author Eberhard Wolff @@ -88,7 +88,7 @@ public class SimpleJpaRepository implements */ public SimpleJpaRepository(Class domainClass, EntityManager em) { - this(JpaClassUtils.getMetadata(domainClass, em), em); + this(JpaEntityInformationSupport.getMetadata(domainClass, em), em); } @@ -100,7 +100,8 @@ public class SimpleJpaRepository implements private String getDeleteAllQueryString() { - return getQueryString(DELETE_ALL_QUERY_STRING, getDomainClass()); + return getQueryString(DELETE_ALL_QUERY_STRING, + entityInformation.getEntityName()); } @@ -110,7 +111,7 @@ public class SimpleJpaRepository implements String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s"); - return getQueryString(countQuery, getDomainClass()); + return getQueryString(countQuery, entityInformation.getEntityName()); } @@ -174,8 +175,10 @@ public class SimpleJpaRepository implements return; } - applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, getDomainClass()), - entities, em).executeUpdate(); + applyAndBind( + getQueryString(DELETE_ALL_QUERY_STRING, + entityInformation.getEntityName()), entities, em) + .executeUpdate(); em.clear(); } 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 09ea8ccda..3fa063d99 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 @@ -15,15 +15,7 @@ */ 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; /** @@ -64,46 +56,4 @@ public abstract class JpaClassUtils { return false; } } - - - /** - * Returns the name ot the entity represented by this class. Used to build - * queries for that class. - * - * @param domainClass - * @return - */ - public static String getEntityName(Class domainClass) { - - Entity entity = domainClass.getAnnotation(Entity.class); - boolean hasName = null != entity && StringUtils.hasText(entity.name()); - - 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/CustomGenericJpaRepositoryFactory.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 6af50e92b..83fa73564 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,6 +21,7 @@ import java.io.Serializable; import javax.persistence.EntityManager; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.repository.core.RepositoryMetadata; @@ -52,8 +53,8 @@ public class CustomGenericJpaRepositoryFactory extends JpaRepositoryFactory { */ @Override @SuppressWarnings("unchecked") - protected Object getTargetRepository(RepositoryMetadata metadata, - EntityManager em) { + protected JpaRepository getTargetRepository( + RepositoryMetadata metadata, EntityManager em) { JpaEntityInformation entityMetadata = mock(JpaEntityInformation.class); 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 new file mode 100644 index 000000000..becaf314e --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -0,0 +1,86 @@ +/* + * 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 static org.junit.Assert.*; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.metamodel.SingularAttribute; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + + +/** + * Unit tests for {@link AbstractJpaEntityInformation}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class JpaEntityInformationSupportUnitTests { + + @Test + public void usesSimpleClassNameIfNoEntityNameGiven() throws Exception { + + JpaEntityInformation information = + new DummyJpaEntityInformation(User.class); + assertEquals("User", information.getEntityName()); + + JpaEntityInformation second = + new DummyJpaEntityInformation( + NamedUser.class); + assertEquals("AnotherNamedUser", second.getEntityName()); + } + + static class User { + + } + + @Entity(name = "AnotherNamedUser") + public class NamedUser { + + } + + static class DummyJpaEntityInformation extends + JpaEntityInformationSupport { + + public DummyJpaEntityInformation(Class domainClass) { + + super(domainClass); + } + + + public SingularAttribute getIdAttribute() { + + return null; + } + + + public ID getId(T entity) { + + return null; + } + + + public Class getIdType() { + + return null; + } + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/util/JpaClassUtilsUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/util/JpaClassUtilsUnitTests.java deleted file mode 100644 index 784f5d7ea..000000000 --- a/src/test/java/org/springframework/data/jpa/repository/util/JpaClassUtilsUnitTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2008-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.util; - -import static org.junit.Assert.*; -import static org.springframework.data.jpa.repository.utils.JpaClassUtils.*; - -import javax.persistence.Entity; - -import org.junit.Test; - - -/** - * @author Oliver Gierke - */ -public class JpaClassUtilsUnitTests { - - @Test - public void usesSimpleClassNameIfNoEntityNameGiven() throws Exception { - - assertEquals("User", getEntityName(User.class)); - assertEquals("AnotherNamedUser", getEntityName(NamedUser.class)); - } - - static class User { - - } - - @Entity(name = "AnotherNamedUser") - public class NamedUser { - - } -}