Repository: deltaspike Updated Branches: refs/heads/master b02acc77e -> f632269f1
DELTASPIKE-1152 Adding support for first and top in method name, including docs. Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/f632269f Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/f632269f Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/f632269f Branch: refs/heads/master Commit: f632269f12404626b0ae24b0fbf1c643b3aae8d9 Parents: b02acc7 Author: John D. Ament <[email protected]> Authored: Sun May 22 19:20:15 2016 -0400 Committer: John D. Ament <[email protected]> Committed: Sun May 22 20:28:00 2016 -0400 ---------------------------------------------------------------------- .../impl/handler/CdiQueryInvocationContext.java | 2 +- .../deltaspike/data/impl/meta/MethodPrefix.java | 81 +++++++++++--------- .../data/impl/meta/RepositoryMethod.java | 5 ++ .../deltaspike/data/impl/param/Parameters.java | 9 ++- .../data/impl/handler/QueryHandlerTest.java | 33 +++++++- .../data/impl/meta/MethodPrefixTest.java | 66 ++++++++++++++++ .../data/test/service/SimpleRepository.java | 4 + documentation/src/main/asciidoc/data.adoc | 19 +++++ 8 files changed, 175 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java index 4505f6a..9f032e5 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java @@ -60,7 +60,7 @@ public class CdiQueryInvocationContext implements QueryInvocationContext { this.entityManager = entityManager; this.args = args == null ? new Object[]{} : args; - this.params = Parameters.create(method, this.args); + this.params = Parameters.create(method, this.args, repoMethod); this.proxy = proxy; this.method = method; this.repoMethod = repoMethod; http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/MethodPrefix.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/MethodPrefix.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/MethodPrefix.java index e421087..37f73b5 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/MethodPrefix.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/MethodPrefix.java @@ -20,6 +20,9 @@ package org.apache.deltaspike.data.impl.meta; import org.apache.deltaspike.data.api.SingleResultType; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class MethodPrefix { public static final String DEFAULT_PREFIX = "findBy"; @@ -28,13 +31,22 @@ public class MethodPrefix public static final String DEFAULT_DELETE_PREFIX = "deleteBy"; public static final String DEFAULT_REMOVE_PREFIX = "removeBy"; + private static final String FIND_FIRST_PREFIX = "find(First|Top)(\\d+)By"; + private static final String FIND_FIRST_PREFIX_PATTERN = FIND_FIRST_PREFIX + "(.+)"; + private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d+"); + private final String customPrefix; private final String methodName; + private int definedMaxResults = 0; public MethodPrefix(String customPrefix, String methodName) { this.customPrefix = customPrefix; this.methodName = methodName; + if (this.methodName != null) + { + this.parseMaxResults(); + } } public String removePrefix(String queryPart) @@ -91,54 +103,46 @@ public class MethodPrefix this.getPrefix().equalsIgnoreCase(DEFAULT_REMOVE_PREFIX); } - private static enum KnownQueryPrefix + public int getDefinedMaxResults() { - DEFAULT(DEFAULT_PREFIX) - { - @Override - public SingleResultType getStyle() - { - return SingleResultType.JPA; - } - }, - OPTIONAL(DEFAULT_OPT_PREFIX) - { - @Override - public SingleResultType getStyle() - { - return SingleResultType.OPTIONAL; - } - }, - ANY(DEFAULT_ANY_PREFIX) + return definedMaxResults; + } + + private void parseMaxResults() + { + if (this.methodName.matches(FIND_FIRST_PREFIX_PATTERN)) { - @Override - public SingleResultType getStyle() + Matcher matcher = DIGIT_PATTERN.matcher(this.methodName); + if (matcher.find()) { - return SingleResultType.ANY; + this.definedMaxResults = Integer.parseInt(matcher.group()); } - }, - DELETE_DEFAULT(DEFAULT_DELETE_PREFIX) + } + } + + private enum KnownQueryPrefix + { + DEFAULT(DEFAULT_PREFIX, SingleResultType.JPA), + FIND_FIRST(FIND_FIRST_PREFIX, SingleResultType.JPA) { @Override - public SingleResultType getStyle() + public String removePrefix(String queryPart) { - return SingleResultType.ANY; + return queryPart.replaceFirst(FIND_FIRST_PREFIX,""); } }, - REMOVE_DEFAULT(DEFAULT_REMOVE_PREFIX) - { - @Override - public SingleResultType getStyle() - { - return SingleResultType.ANY; - } - }; + OPTIONAL(DEFAULT_OPT_PREFIX,SingleResultType.OPTIONAL), + ANY(DEFAULT_ANY_PREFIX, SingleResultType.ANY), + DELETE_DEFAULT(DEFAULT_DELETE_PREFIX, SingleResultType.ANY), + REMOVE_DEFAULT(DEFAULT_REMOVE_PREFIX, SingleResultType.ANY); private final String prefix; + private final SingleResultType singleResultType; - private KnownQueryPrefix(String prefix) + KnownQueryPrefix(String prefix, SingleResultType singleResultType) { this.prefix = prefix; + this.singleResultType = singleResultType; } public String removePrefix(String queryPart) @@ -151,10 +155,17 @@ public class MethodPrefix return prefix; } - public abstract SingleResultType getStyle(); + public SingleResultType getStyle() + { + return this.singleResultType; + } public static KnownQueryPrefix fromMethodName(String name) { + if (name.matches(FIND_FIRST_PREFIX_PATTERN)) + { + return FIND_FIRST; + } for (KnownQueryPrefix mapping : values()) { if (name.startsWith(mapping.getPrefix())) http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java index f56ffaf..08cedd8 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java @@ -223,6 +223,11 @@ public class RepositoryMethod return mapper != null; } + public int getDefinedMaxResults() + { + return this.methodPrefix.getDefinedMaxResults(); + } + public SingleResultType getSingleResultStyle() { if (method.isAnnotationPresent(Query.class)) http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java index 76b8dae..d5e74b8 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java @@ -32,6 +32,7 @@ import org.apache.deltaspike.data.api.FirstResult; import org.apache.deltaspike.data.api.MaxResults; import org.apache.deltaspike.data.api.QueryParam; import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; +import org.apache.deltaspike.data.impl.meta.RepositoryMethod; /** * Convenience class to manage method and query parameters. @@ -61,9 +62,9 @@ public final class Parameters return new Parameters(empty, DEFAULT_MAX, DEFAULT_FIRST); } - public static Parameters create(Method method, Object[] parameters) + public static Parameters create(Method method, Object[] parameters, RepositoryMethod repositoryMethod) { - int max = extractSizeRestriction(method); + int max = extractSizeRestriction(method, repositoryMethod); int first = DEFAULT_FIRST; List<Parameter> result = new ArrayList<Parameter>(parameters.length); int paramIndex = 1; @@ -142,13 +143,13 @@ public final class Parameters return firstResult; } - private static int extractSizeRestriction(Method method) + private static int extractSizeRestriction(Method method, RepositoryMethod repositoryMethod) { if (method.isAnnotationPresent(org.apache.deltaspike.data.api.Query.class)) { return method.getAnnotation(org.apache.deltaspike.data.api.Query.class).max(); } - return 0; + return repositoryMethod.getDefinedMaxResults(); } @SuppressWarnings("unchecked") http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/handler/QueryHandlerTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/handler/QueryHandlerTest.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/handler/QueryHandlerTest.java index 89b9397..f7b8ccd 100644 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/handler/QueryHandlerTest.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/handler/QueryHandlerTest.java @@ -27,11 +27,8 @@ import static org.junit.Assert.assertTrue; import java.util.List; -import javax.enterprise.inject.Produces; import javax.inject.Inject; -import javax.persistence.EntityManager; import javax.persistence.NonUniqueResultException; -import javax.persistence.PersistenceContext; import org.apache.deltaspike.data.test.TransactionalTestCase; import org.apache.deltaspike.data.test.domain.Simple; @@ -455,12 +452,40 @@ public class QueryHandlerTest extends TransactionalTestCase builder.createSimple(name); // when - Simple result = repo.findByNameIgnoreCase("should_create_case_insensitive_query_for_equals"); + Simple result = repo.findByNameIgnoreCase(name.toLowerCase()); // then assertEquals(name, result.getName()); } + @Test + public void should_find_first_2() + { + final String name = "Should_Create_Case_Insensitive_Query_for_Equals"; + builder.createSimple(name); + builder.createSimple(name); + builder.createSimple(name); + builder.createSimple("this is something else"); + + List<Simple> result = repo.findFirst2ByName(name); + + assertEquals(2, result.size()); + } + + @Test + public void should_find_top_2() + { + final String name = "Should_Create_Case_Insensitive_Query_for_Equals"; + builder.createSimple(name); + builder.createSimple(name); + builder.createSimple(name); + builder.createSimple("this is something else"); + + List<Simple> result = repo.findTop2ByName(name); + + assertEquals(2, result.size()); + } + @Before public void setup() { http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/meta/MethodPrefixTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/meta/MethodPrefixTest.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/meta/MethodPrefixTest.java new file mode 100644 index 0000000..04e1aa1 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/meta/MethodPrefixTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.deltaspike.data.impl.meta; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MethodPrefixTest +{ + @Test + public void shouldParseArbitraryMethodFindPrefix() + { + MethodPrefix methodPrefix = new MethodPrefix("","findTop20ByName"); + + String resultingQuery = methodPrefix.removePrefix("findTop20ByName"); + + assertEquals("Name", resultingQuery); + } + + @Test + public void shouldParseFirst20MethodFindPrefix() + { + MethodPrefix methodPrefix = new MethodPrefix("","findFirst20ByName"); + + String resultingQuery = methodPrefix.removePrefix("findFirst20ByName"); + + assertEquals("Name", resultingQuery); + } + + @Test + public void shouldParseDefinedMaxResults() + { + MethodPrefix methodPrefix = new MethodPrefix("","findFirst20ByName"); + + int maxResults = methodPrefix.getDefinedMaxResults(); + + assertEquals(20, maxResults); + } + + @Test + public void shouldNotParseNonMatchingMethodName() + { + MethodPrefix methodPrefix = new MethodPrefix("","findAnyByName"); + + int maxResults = methodPrefix.getDefinedMaxResults(); + + assertEquals(0, maxResults); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleRepository.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleRepository.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleRepository.java index ebadd20..62dcb24 100755 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleRepository.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleRepository.java @@ -85,6 +85,10 @@ public abstract class SimpleRepository extends AbstractEntityRepository<Simple, public abstract Simple findAnyByName(String name); + public abstract List<Simple> findFirst2ByName(String name); + + public abstract List<Simple> findTop2ByName(String name); + public abstract List<Simple> findByOrderByCounterAscIdDesc(); @Query(value = "SELECT * from SIMPLE_TABLE s WHERE s.name = ?1", isNative = true) http://git-wip-us.apache.org/repos/asf/deltaspike/blob/f632269f/documentation/src/main/asciidoc/data.adoc ---------------------------------------------------------------------- diff --git a/documentation/src/main/asciidoc/data.adoc b/documentation/src/main/asciidoc/data.adoc index 6f7f76c..904a468 100644 --- a/documentation/src/main/asciidoc/data.adoc +++ b/documentation/src/main/asciidoc/data.adoc @@ -521,6 +521,25 @@ public interface PersonRepository extends EntityRepository<Person, Long> } ------------------------------------------------------------------------------ +=== Query Limits + +Starting with Apache DeltaSpike 1.6.2, you can apply query limits using method +expressions. They can be applied using `First` or `Top` keywords, in a method +like this + +[source,java] +------------------------------------------------------------------------------ +@Repository +public interface PersonRepository extends EntityRepository<Person, Long> +{ + + List<Person> findFirst2ByLastNameOrderByAgeAscLastNameDesc(String lastName); + + List<Person> findTop2ByLastNameOrderByAgeAscLastNameDesc(String lastName); + +} +------------------------------------------------------------------------------ + === Nested Properties To create a comparison on a nested property, the traversal parts can be
