From b6845e7310fb5d994d719e984946b22c0f5709bb Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 20 Apr 2011 20:30:00 +0200 Subject: [PATCH] DATACMNS-32 - Refactored Querydsl support code from JPA and Mongo module into core. --- spring-data-commons-core/pom.xml | 42 ++++++ .../data/querydsl/EntityPathResolver.java | 28 ++++ .../data/querydsl/QueryDslUtils.java | 27 ++++ .../querydsl/SimpleEntityPathResolver.java | 135 ++++++++++++++++++ .../SimpleEntityPathResolverUnitTests.java | 72 ++++++++++ .../springframework/data/querydsl/User.java | 26 ++++ spring-data-commons-core/template.mf | 1 + spring-data-commons-parent/pom.xml | 8 ++ src/main/resources/changelog.txt | 1 + 9 files changed, 340 insertions(+) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/querydsl/EntityPathResolver.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/querydsl/SimpleEntityPathResolver.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/querydsl/SimpleEntityPathResolverUnitTests.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/querydsl/User.java diff --git a/spring-data-commons-core/pom.xml b/spring-data-commons-core/pom.xml index df116c007..9c2a32b67 100644 --- a/spring-data-commons-core/pom.xml +++ b/spring-data-commons-core/pom.xml @@ -10,6 +10,11 @@ spring-data-commons-core jar Spring Data Commons Core + + + 2.1.1 + + @@ -69,6 +74,26 @@ 1.6 true + + + com.mysema.querydsl + querydsl-mongodb + ${querydsl.version} + true + + + com.google.code.morphia + morphia + + + + + + com.mysema.querydsl + querydsl-apt + ${querydsl.version} + provided + @@ -77,6 +102,23 @@ com.springsource.bundlor com.springsource.bundlor.maven + + com.mysema.maven + maven-apt-plugin + 1.0 + + + generate-test-sources + + test-process + + + target/generated-sources/test-annotations + com.mysema.query.apt.QuerydslAnnotationProcessor + + + + diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/EntityPathResolver.java b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/EntityPathResolver.java new file mode 100644 index 000000000..389a9aefb --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/EntityPathResolver.java @@ -0,0 +1,28 @@ +/* + * 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.querydsl; + +import com.mysema.query.types.EntityPath; + +/** + * Strategy interface to abstract the ways to translate an plain domain class into a {@link EntityPath}. + * + * @author Oliver Gierke + */ +public interface EntityPathResolver { + + EntityPath createPath(Class domainClass); +} \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java new file mode 100644 index 000000000..d9a4ca37a --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java @@ -0,0 +1,27 @@ +/* + * 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.querydsl; + + +/** + * + * @author Oliver Gierke + */ +public class QueryDslUtils { + + public static final boolean QUERY_DSL_PRESENT = org.springframework.util.ClassUtils.isPresent( + "com.mysema.query.types.Predicate", QueryDslUtils.class.getClassLoader()); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/SimpleEntityPathResolver.java b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/SimpleEntityPathResolver.java new file mode 100644 index 000000000..6d360001e --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/querydsl/SimpleEntityPathResolver.java @@ -0,0 +1,135 @@ +/* + * 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.querydsl; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +import com.mysema.query.types.EntityPath; + +/** + * Simple implementation of {@link EntityPathResolver} to lookup a query + * class by reflection and using the static field of the same type. + * + * @author Oliver Gierke + */ +public enum SimpleEntityPathResolver implements EntityPathResolver { + + INSTANCE; + + private static final String NO_CLASS_FOUND_TEMPLATE = + "Did not find a query class %s for domain class %s!"; + private static final String NO_FIELD_FOUND_TEMPLATE = + "Did not find a static field of the same type in %s!"; + + + /** + * Creates an {@link EntityPath} instance for the given domain class. + * Tries to lookup a class matching the naming convention (prepend Q to + * the simple name of the class, same package) and find a static field + * of the same type in it. + * + * @param domainClass + * @return + */ + @SuppressWarnings("unchecked") + public EntityPath createPath(Class domainClass) { + + String pathClassName = getQueryClassName(domainClass); + + try { + Class pathClass = + ClassUtils.forName(pathClassName, + SimpleEntityPathResolver.class.getClassLoader()); + Field field = getStaticFieldOfType(pathClass); + + if (field == null) { + throw new IllegalStateException(String.format( + NO_FIELD_FOUND_TEMPLATE, pathClass)); + } else { + return (EntityPath) ReflectionUtils + .getField(field, null); + } + + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(String.format( + NO_CLASS_FOUND_TEMPLATE, pathClassName, + domainClass.getName()), e); + } + } + + + /** + * Returns the first static field of the given type inside the given + * type. + * + * @param type + * @return + */ + private Field getStaticFieldOfType(Class type) { + + for (Field field : type.getDeclaredFields()) { + + boolean isStatic = Modifier.isStatic(field.getModifiers()); + boolean hasSameType = type.equals(field.getType()); + + if (isStatic && hasSameType) { + return field; + } + } + + Class superclass = type.getSuperclass(); + return Object.class.equals(superclass) ? null + : getStaticFieldOfType(superclass); + } + + + /** + * Returns the name of the query class for the given domain class. + * + * @param domainClass + * @return + */ + private String getQueryClassName(Class domainClass) { + + String simpleClassName = ClassUtils.getShortName(domainClass); + return String.format("%s.Q%s%s", + domainClass.getPackage().getName(), + getClassBase(simpleClassName), domainClass.getSimpleName()); + } + + + /** + * Analyzes the short class name and potentially returns the outer + * class. + * + * @param shortName + * @return + */ + private String getClassBase(String shortName) { + + String[] parts = shortName.split("\\."); + + if (parts.length < 2) { + return ""; + } + + return parts[0] + "_"; + } +} \ No newline at end of file diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/SimpleEntityPathResolverUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/SimpleEntityPathResolverUnitTests.java new file mode 100644 index 000000000..146f25a8a --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/SimpleEntityPathResolverUnitTests.java @@ -0,0 +1,72 @@ +/* + * 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.querydsl; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.querydsl.EntityPathResolver; +import org.springframework.data.querydsl.SimpleEntityPathResolver; +import org.springframework.data.querydsl.QSimpleEntityPathResolverUnitTests_NamedUser; +import org.springframework.data.querydsl.QSimpleEntityPathResolverUnitTests_Sample; +import org.springframework.data.querydsl.QUser; + +import com.mysema.query.annotations.QueryEntity; + + +/** + * Unit test for {@link SimpleEntityPathResolver}. + * + * @author Oliver Gierke + */ +public class SimpleEntityPathResolverUnitTests { + + EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE; + + + @Test + public void createsRepositoryFromDomainClassCorrectly() throws Exception { + + assertThat(resolver.createPath(User.class), is(QUser.class)); + } + + + @Test + public void resolvesEntityPathForInnerClassCorrectly() throws Exception { + + assertThat(resolver.createPath(NamedUser.class), + is(QSimpleEntityPathResolverUnitTests_NamedUser.class)); + } + + + @Test(expected = IllegalArgumentException.class) + public void rejectsClassWithoutQueryClassConfrmingToTheNamingScheme() + throws Exception { + + resolver.createPath(QSimpleEntityPathResolverUnitTests_Sample.class); + } + + @QueryEntity + static class Sample { + + } + + @QueryEntity + static class NamedUser { + + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/User.java b/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/User.java new file mode 100644 index 000000000..1547360fe --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/querydsl/User.java @@ -0,0 +1,26 @@ +/* + * 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.querydsl; + +import com.mysema.query.annotations.QueryEntity; + +/** + * @author Oliver Gierke + */ +@QueryEntity +public class User { + +} diff --git a/spring-data-commons-core/template.mf b/spring-data-commons-core/template.mf index f55c1819d..dac7166fa 100644 --- a/spring-data-commons-core/template.mf +++ b/spring-data-commons-core/template.mf @@ -5,6 +5,7 @@ Bundle-ManifestVersion: 2 Import-Package: sun.reflect;version="0";resolution:=optional Import-Template: + com.mysema.query.*;version="${querydsl.version:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.aop.*;version="${org.springframework.version:[=.=.=,+1.0.0)}", org.springframework.beans.*;version="${org.springframework.version:[=.=.=,+1.0.0)}", org.springframework.core.*;version="${org.springframework.version:[=.=.=,+1.0.0)}", diff --git a/spring-data-commons-parent/pom.xml b/spring-data-commons-parent/pom.xml index cea7c716e..96145b9d2 100644 --- a/spring-data-commons-parent/pom.xml +++ b/spring-data-commons-parent/pom.xml @@ -361,6 +361,14 @@ Spring Framework Maven Snapshot Repository http://maven.springframework.org/snapshot + + querydsl + Mysema QueryDsl + http://source.mysema.com/maven2/releases + + false + + diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 5db709af9..1c35db42f 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -5,6 +5,7 @@ Changes in version 1.0.1 ---------------------------------------- General +* Extracted Querydsl support code from JPA and Mongo modules (DATACMNS-32) * Implementations of Page, Pageable and Sort are now serializable (DATACMNS-30) Changes in version 1.0.0 (2011-04-18)