DATAJPA-59, DATAJPA-60 - Improved handling of PersistenceProviders not capable of extracting queries for paging query methods.
Removed check for that capability (QueryExtractor.canExtractQuery()) from JpaQueryMethod and defer it into NamedQuery as we can handle derived queries and queries annotated with @Query regardless of that capability. Made exception message a bit more verbose to give hints what to do if the exception occurs.
This commit is contained in:
@@ -67,11 +67,6 @@ public class JpaQueryMethod extends QueryMethod {
|
||||
Assert.isTrue(!(isModifyingQuery() && getParameters()
|
||||
.hasSpecialParameter()), String.format(
|
||||
"Modifying method must not contain %s!", Parameters.TYPES));
|
||||
|
||||
Assert.isTrue(!(getParameters().hasPageableParameter() && !extractor
|
||||
.canExtractQuery()),
|
||||
"You cannot use Pageable as method parameter if your "
|
||||
+ "persistence provider cannot extract queries!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,12 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
*/
|
||||
final class NamedQuery extends AbstractStringBasedJpaQuery {
|
||||
|
||||
private static final String CANNOT_EXTRACT_QUERY =
|
||||
"Your persistence provider does not support extracting the JPQL query from a "
|
||||
+ "named query thus you can't use Pageable inside your query method. Make sure you "
|
||||
+ "have a JpaDialect configured at your EntityManagerFactoryBean as this affects "
|
||||
+ "discovering the concrete persistence provider.";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NamedQuery.class);
|
||||
|
||||
private final String queryName;
|
||||
@@ -49,6 +55,31 @@ final class NamedQuery extends AbstractStringBasedJpaQuery {
|
||||
|
||||
this.queryName = method.getNamedQueryName();
|
||||
this.extractor = method.getQueryExtractor();
|
||||
|
||||
Parameters parameters = method.getParameters();
|
||||
|
||||
if (parameters.hasSortParameter()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Finder method %s is backed " + "by a NamedQuery and must "
|
||||
+ "not contain a sort parameter as we "
|
||||
+ "cannot modify the query! Use @Query instead!",
|
||||
method));
|
||||
}
|
||||
|
||||
if (parameters.hasPageableParameter()) {
|
||||
LOG.info("Finder method {} is backed by a NamedQuery"
|
||||
+ " but contains a Pageble parameter! Sorting deliviered "
|
||||
+ "via this Pageable will not be applied!", method);
|
||||
}
|
||||
|
||||
boolean weNeedToCreateCountQuery =
|
||||
method.getParameters().hasPageableParameter();
|
||||
boolean cantExtractQuery = !this.extractor.canExtractQuery();
|
||||
|
||||
if (weNeedToCreateCountQuery && cantExtractQuery) {
|
||||
throw QueryCreationException.create(method, CANNOT_EXTRACT_QUERY);
|
||||
}
|
||||
|
||||
Query query = em.createNamedQuery(queryName);
|
||||
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=322579
|
||||
@@ -73,40 +104,7 @@ final class NamedQuery extends AbstractStringBasedJpaQuery {
|
||||
LOG.debug("Looking up named query {}", queryName);
|
||||
|
||||
try {
|
||||
|
||||
RepositoryQuery query = new NamedQuery(method, em);
|
||||
Parameters parameters = method.getParameters();
|
||||
|
||||
if (parameters.hasSortParameter()) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Finder method %s is backed "
|
||||
+ "by a NamedQuery and must "
|
||||
+ "not contain a sort parameter as we "
|
||||
+ "cannot modify the query! Use @Query instead!",
|
||||
method));
|
||||
}
|
||||
|
||||
boolean isPaging = parameters.hasPageableParameter();
|
||||
boolean cannotExtractQuery =
|
||||
!method.getQueryExtractor().canExtractQuery();
|
||||
|
||||
if (isPaging && cannotExtractQuery) {
|
||||
throw QueryCreationException
|
||||
.create(method,
|
||||
"Cannot use Pageable parameter in query methods with your persistence provider!");
|
||||
}
|
||||
|
||||
if (parameters.hasPageableParameter()) {
|
||||
LOG.info(
|
||||
"Finder method {} is backed by a NamedQuery"
|
||||
+ " but contains a Pageble parameter! Sorting deliviered "
|
||||
+ "via this Pageable will not be applied!",
|
||||
method);
|
||||
|
||||
}
|
||||
|
||||
return query;
|
||||
return new NamedQuery(method, em);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
@@ -168,20 +167,6 @@ public class JpaQueryMethodUnitTests {
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsPageablesOnPersistenceProvidersNotExtractingQueries()
|
||||
throws Exception {
|
||||
|
||||
Method method =
|
||||
UserRepository.class.getMethod("findByLastname",
|
||||
Pageable.class, String.class);
|
||||
|
||||
when(extractor.canExtractQuery()).thenReturn(false);
|
||||
|
||||
new JpaQueryMethod(method, metadata, extractor);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void recognizesModifyingMethod() {
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.query;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.support.RepositoryMetadata;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link NamedQuery}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class NamedQueryUnitTests {
|
||||
|
||||
@Mock
|
||||
RepositoryMetadata metadata;
|
||||
@Mock
|
||||
QueryExtractor extractor;
|
||||
@Mock
|
||||
EntityManager em;
|
||||
|
||||
Method method;
|
||||
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
method = SampleRepository.class.getMethod("foo", Pageable.class);
|
||||
when(metadata.getDomainClass()).thenReturn((Class) String.class);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = QueryCreationException.class)
|
||||
public void rejectsPersistenceProviderIfIncapableOfExtractingQueriesAndPagebleBeingUsed() {
|
||||
|
||||
when(extractor.canExtractQuery()).thenReturn(false);
|
||||
|
||||
JpaQueryMethod queryMethod =
|
||||
new JpaQueryMethod(method, metadata, extractor);
|
||||
NamedQuery.lookupFrom(queryMethod, em);
|
||||
}
|
||||
|
||||
interface SampleRepository {
|
||||
|
||||
Page<String> foo(Pageable pageable);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user