diff --git a/src/docbkx/reference/jpa.xml b/src/docbkx/reference/jpa.xml
index bec4b8585..28fcb53dd 100644
--- a/src/docbkx/reference/jpa.xml
+++ b/src/docbkx/reference/jpa.xml
@@ -763,6 +763,42 @@ public interface UserRepository extends JpaRepository<User, Long> {
+
+ Locking
+
+ To specify the lock mode to be used the
+ @Lock annotation can be used on query
+ methods:
+
+
+ Defining lock metadata on query methods
+
+ interface UserRepository extends Repository<User, Long> {
+
+ // Plain query method
+ @Lock(LockModeType.READ)
+ List<User> findByLastname(String lastname);
+}
+
+
+ This method declaration will cause the query being triggered to be
+ equipped with the LockModeType
+ READ. You can also define locking for CRUD methods by
+ redeclaring them in your repository interface and adding the
+ @Lock annotation:
+
+
+ Defining lock metadata on CRUD methods
+
+ interface UserRepository extends Repository<User, Long> {
+
+ // Redeclaration of a CRUD method
+ @Lock(LockModeType.READ);
+ List<User> findAll();
+}
+
+
+
Auditing
diff --git a/src/main/java/org/springframework/data/jpa/repository/Lock.java b/src/main/java/org/springframework/data/jpa/repository/Lock.java
new file mode 100644
index 000000000..45f6ea48b
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/Lock.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.persistence.LockModeType;
+
+/**
+ * Annotation used to specify the {@link LockModeType} to be used when executing the query. It will be evaluated when
+ * using {@link Query} on a query method or if you derive the query from the method name.
+ *
+ * @author Aleksander Blomskøld
+ * @author Oliver Gierke
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Lock {
+
+ /**
+ * The {@link LockModeType} to be used when executing the annotated query or CRUD method.
+ *
+ * @return
+ */
+ LockModeType value();
+}
\ No newline at end of file
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 99bea4a8a..6ee857102 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
@@ -16,6 +16,7 @@
package org.springframework.data.jpa.repository.query;
import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.QueryHint;
import javax.persistence.TypedQuery;
@@ -122,12 +123,25 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
return query;
}
+ /**
+ * Applies the {@link LockModeType} provided by the {@link JpaQueryMethod} to the given {@link Query}.
+ *
+ * @param query must not be {@literal null}.
+ * @param method must not be {@literal null}.
+ * @return
+ */
+ private Query applyLockMode(Query query, JpaQueryMethod method) {
+
+ LockModeType lockModeType = method.getLockModeType();
+ return lockModeType == null ? query : query.setLockMode(lockModeType);
+ }
+
protected ParameterBinder createBinder(Object[] values) {
return new ParameterBinder(getQueryMethod().getParameters(), values);
}
protected Query createQuery(Object[] values) {
- return applyHints(doCreateQuery(values), method);
+ return applyLockMode(applyHints(doCreateQuery(values), method), method);
}
protected TypedQuery createCountQuery(Object[] values) {
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 7ccad43b8..73cf1454a 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,9 +22,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import javax.persistence.LockModeType;
import javax.persistence.QueryHint;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.QueryHints;
@@ -93,6 +95,17 @@ public class JpaQueryMethod extends QueryMethod {
return result;
}
+ /**
+ * Returns the {@link LockModeType} to be used for the query.
+ *
+ * @return
+ */
+ LockModeType getLockModeType() {
+
+ Lock annotation = method.getAnnotation(Lock.class);
+ return (LockModeType) AnnotationUtils.getValue(annotation);
+ }
+
/**
* Returns whether the potentially configured {@link QueryHint}s shall be applied when triggering the count query for
* pagination.
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 bfcfa5799..9a30ac689 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
@@ -40,6 +40,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
private final EntityManager entityManager;
private final QueryExtractor extractor;
+ private final LockModeRepositoryPostProcessor lockModePostProcessor;
/**
* Creates a new {@link JpaRepositoryFactory}.
@@ -51,6 +52,9 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
Assert.notNull(entityManager);
this.entityManager = entityManager;
this.extractor = PersistenceProvider.fromEntityManager(entityManager);
+ this.lockModePostProcessor = LockModeRepositoryPostProcessor.INSTANCE;
+
+ addRepositoryProxyPostProcessor(lockModePostProcessor);
}
/*
@@ -82,11 +86,11 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
Class> repositoryInterface = metadata.getRepositoryInterface();
JpaEntityInformation, Serializable> entityInformation = getEntityInformation(metadata.getDomainClass());
- if (isQueryDslExecutor(repositoryInterface)) {
- return new QueryDslJpaRepository(entityInformation, entityManager);
- } else {
- return new SimpleJpaRepository(entityInformation, entityManager);
- }
+ SimpleJpaRepository, ?> repo = isQueryDslExecutor(repositoryInterface) ? new QueryDslJpaRepository(
+ entityInformation, entityManager) : new SimpleJpaRepository(entityInformation, entityManager);
+ repo.setLockMetadataProvider(lockModePostProcessor.getLockMetadataProvider());
+
+ return repo;
}
/*
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 469cf9936..24eb98ff9 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
@@ -21,7 +21,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.beans.factory.FactoryBean;
-import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
import org.springframework.util.Assert;
@@ -34,7 +34,7 @@ import org.springframework.util.Assert;
* @author Eberhard Wolff
* @param the type of the repository
*/
-public class JpaRepositoryFactoryBean, S, ID extends Serializable> extends
+public class JpaRepositoryFactoryBean, S, ID extends Serializable> extends
TransactionalRepositoryFactoryBeanSupport {
private EntityManager entityManager;
@@ -58,7 +58,6 @@ public class JpaRepositoryFactoryBean, S, ID exte
*/
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
-
return createRepositoryFactory(entityManager);
}
@@ -69,7 +68,6 @@ public class JpaRepositoryFactoryBean, S, ID exte
* @return
*/
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
-
return new JpaRepositoryFactory(entityManager);
}
diff --git a/src/main/java/org/springframework/data/jpa/repository/support/LockMetadataProvider.java b/src/main/java/org/springframework/data/jpa/repository/support/LockMetadataProvider.java
new file mode 100644
index 000000000..4de971291
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/support/LockMetadataProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.LockModeType;
+
+/**
+ * Interface to abstract {@link LockMetadataProvider} that provide the {@link LockModeType} to be used for query
+ * execution.
+ *
+ * @author Oliver Gierke
+ */
+public interface LockMetadataProvider {
+
+ /**
+ * Returns the {@link LockModeType} to be used.
+ *
+ * @return
+ */
+ LockModeType getLockModeType();
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/data/jpa/repository/support/LockModeRepositoryPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/LockModeRepositoryPostProcessor.java
new file mode 100644
index 000000000..5350b7ff8
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/support/LockModeRepositoryPostProcessor.java
@@ -0,0 +1,113 @@
+/*
+ * 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.lang.reflect.Method;
+
+import javax.persistence.LockModeType;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+
+/**
+ * {@link RepositoryProxyPostProcessor} that sets up interceptors to read {@link LockModeType} information from the
+ * invoked method. This is necessary to allow redeclaration of CRUD methods in repository interfaces and configure
+ * locking information on them.
+ *
+ * @author Oliver Gierke
+ */
+public enum LockModeRepositoryPostProcessor implements RepositoryProxyPostProcessor {
+
+ INSTANCE;
+
+ private static final Object NULL = new Object();
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.RepositoryProxyPostProcessor#postProcess(org.springframework.aop.framework.ProxyFactory)
+ */
+ public void postProcess(ProxyFactory factory) {
+
+ factory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
+ factory.addAdvice(LockModePopulatingMethodIntercceptor.INSTANCE);
+ }
+
+ /**
+ * Returns the {@link LockMetadataProvider} to lookup the lock information captured by the interceptors.
+ *
+ * @return
+ */
+ public LockMetadataProvider getLockMetadataProvider() {
+ return ThreadBoundLockMetadata.INSTANCE;
+ }
+
+ /**
+ * {@link MethodInterceptor} to inspect the currently invoked {@link Method} for a {@link Lock} annotation. Will bind
+ * the found information to a {@link TransactionSynchronizationManager} for later lookup.
+ *
+ * @see ThreadBoundLockMetadata
+ * @author Oliver Gierke
+ */
+ private static enum LockModePopulatingMethodIntercceptor implements MethodInterceptor {
+
+ INSTANCE;
+
+ /*
+ * (non-Javadoc)
+ * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
+ */
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+
+ Method method = invocation.getMethod();
+ Object lockInfo = TransactionSynchronizationManager.getResource(method);
+
+ if (lockInfo != null) {
+ return invocation.proceed();
+ }
+
+ Lock annotation = method.getAnnotation(Lock.class);
+ LockModeType lockMode = (LockModeType) AnnotationUtils.getValue(annotation);
+ TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);
+
+ return invocation.proceed();
+ }
+ }
+
+ /**
+ * {@link LockMetadataProvider} that looks up locking metadata from the {@link TransactionSynchronizationManager}
+ * using the current method invocation as key.
+ *
+ * @author Oliver Gierke
+ */
+ private static enum ThreadBoundLockMetadata implements LockMetadataProvider {
+
+ INSTANCE;
+
+ public LockModeType getLockModeType() {
+
+ MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation();
+ Object lockModeType = TransactionSynchronizationManager.getResource(invocation.getMethod());
+
+ return lockModeType == NULL ? null : (LockModeType) lockModeType;
+ }
+ }
+}
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 65d019e19..16f9af5e8 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
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
@@ -59,6 +60,8 @@ public class SimpleJpaRepository implements JpaRepos
private final EntityManager em;
private final PersistenceProvider provider;
+ private LockMetadataProvider lockMetadataProvider;
+
/**
* Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}.
*
@@ -85,6 +88,16 @@ public class SimpleJpaRepository implements JpaRepos
this(JpaEntityInformationSupport.getMetadata(domainClass, em), em);
}
+ /**
+ * Configures a custom {@link LockMetadataProvider} to be used to detect {@link LockModeType}s to be applied to
+ * queries.
+ *
+ * @param lockMetadataProvider
+ */
+ public void setLockMetadataProvider(LockMetadataProvider lockMetadataProvider) {
+ this.lockMetadataProvider = lockMetadataProvider;
+ }
+
private Class getDomainClass() {
return entityInformation.getJavaType();
}
@@ -367,17 +380,8 @@ public class SimpleJpaRepository implements JpaRepos
*/
private TypedQuery getQuery(Specification spec, Pageable pageable) {
- CriteriaBuilder builder = em.getCriteriaBuilder();
- CriteriaQuery query = builder.createQuery(getDomainClass());
-
- Root root = applySpecificationToCriteria(spec, query);
- query.select(root);
-
- if (pageable != null) {
- query.orderBy(toOrders(pageable.getSort(), root, builder));
- }
-
- return em.createQuery(query);
+ Sort sort = pageable == null ? null : pageable.getSort();
+ return getQuery(spec, sort);
}
/**
@@ -399,7 +403,7 @@ public class SimpleJpaRepository implements JpaRepos
query.orderBy(toOrders(sort, root, builder));
}
- return em.createQuery(query);
+ return applyLockMode(em.createQuery(query));
}
/**
@@ -444,4 +448,10 @@ public class SimpleJpaRepository implements JpaRepos
return root;
}
+
+ private TypedQuery applyLockMode(TypedQuery query) {
+
+ LockModeType type = lockMetadataProvider == null ? null : lockMetadataProvider.getLockModeType();
+ return type == null ? query : query.setLockMode(type);
+ }
}
diff --git a/src/test/java/org/springframework/data/jpa/repository/LockIntegrationTest.java b/src/test/java/org/springframework/data/jpa/repository/LockIntegrationTest.java
new file mode 100644
index 000000000..1fca7e6db
--- /dev/null
+++ b/src/test/java/org/springframework/data/jpa/repository/LockIntegrationTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.Serializable;
+
+import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.data.jpa.domain.sample.Role;
+import org.springframework.data.jpa.repository.sample.RoleRepository;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
+
+/**
+ * Integratio test for lock support.
+ *
+ * @author Oliver Gierke
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LockIntegrationTest {
+
+ @Mock
+ EntityManager em;
+ @Mock
+ CriteriaBuilder builder;
+ @Mock
+ CriteriaQuery criteriaQuery;
+ @Mock
+ JpaEntityInformation information;
+ @Mock
+ TypedQuery query;
+
+ /**
+ * @see DATAJPA-73
+ */
+ @Test
+ public void usesLockInformationAnnotatedAtRedeclaredMethod() {
+
+ when(information.getJavaType()).thenReturn(Role.class);
+ when(em.getCriteriaBuilder()).thenReturn(builder);
+ when(builder.createQuery(Role.class)).thenReturn(criteriaQuery);
+ when(em.createQuery(criteriaQuery)).thenReturn(query);
+ when(query.setLockMode(any(LockModeType.class))).thenReturn(query);
+
+ JpaRepositoryFactory factory = new JpaRepositoryFactory(em) {
+ @Override
+ @SuppressWarnings("unchecked")
+ public JpaEntityInformation getEntityInformation(Class domainClass) {
+ return (JpaEntityInformation) information;
+ }
+ };
+
+ RoleRepository repository = factory.getRepository(RoleRepository.class);
+
+ repository.findAll();
+
+ verify(query).setLockMode(LockModeType.READ);
+ }
+}
diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java
index e1f61397c..7228c3a09 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java
@@ -15,12 +15,14 @@
*/
package org.springframework.data.jpa.repository.query;
+import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import java.util.List;
import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.QueryHint;
@@ -30,6 +32,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
+import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.jpa.repository.support.PersistenceProvider;
import org.springframework.data.repository.Repository;
@@ -101,6 +104,24 @@ public class AbstractJpaQueryTests {
verify(result, never()).setHint("bar", "foo");
}
+ /**
+ * @see DATAJPA-73
+ */
+ @Test
+ public void addsLockingModeToQueryObject() throws Exception {
+
+ when(query.setLockMode(any(LockModeType.class))).thenReturn(query);
+
+ Method method = SampleRepository.class.getMethod("findOneLocked", Integer.class);
+ QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
+ JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
+ provider);
+
+ AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
+ Query result = jpaQuery.createQuery(new Object[] { Integer.valueOf(1) });
+ verify(result).setLockMode(LockModeType.PESSIMISTIC_WRITE);
+ }
+
interface SampleRepository extends Repository {
@QueryHints({ @QueryHint(name = "foo", value = "bar") })
@@ -108,6 +129,10 @@ public class AbstractJpaQueryTests {
@QueryHints(value = { @QueryHint(name = "bar", value = "foo") }, forCounting = false)
List findByFirstname(String firstname);
+
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @org.springframework.data.jpa.repository.Query("select u from User u where u.id = ?1")
+ List findOneLocked(Integer primaryKey);
}
class DummyJpaQuery extends AbstractJpaQuery {
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 1fbe51430..e40bfc899 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
@@ -21,6 +21,7 @@ import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.util.List;
+import javax.persistence.LockModeType;
import javax.persistence.QueryHint;
import org.junit.Before;
@@ -32,6 +33,7 @@ import org.springframework.data.domain.Page;
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.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.sample.UserRepository;
@@ -56,7 +58,7 @@ public class JpaQueryMethodUnitTests {
RepositoryMetadata metadata;
Method repositoryMethod, invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, modifyingMethod,
- nativeQuery, namedQuery;
+ nativeQuery, namedQuery, findWithLockMethod;
/**
* @throws Exception
@@ -73,8 +75,10 @@ public class JpaQueryMethodUnitTests {
sortableTwice = InvalidRepository.class.getMethod(METHOD_NAME, String.class, Sort.class, Sort.class);
modifyingMethod = UserRepository.class.getMethod("renameAllUsersTo", String.class);
- nativeQuery = InvalidRepository.class.getMethod("findByLastname", String.class);
- namedQuery = InvalidRepository.class.getMethod("findByNamedQuery");
+ nativeQuery = ValidRepository.class.getMethod("findByLastname", String.class);
+ namedQuery = ValidRepository.class.getMethod("findByNamedQuery");
+
+ findWithLockMethod = ValidRepository.class.getMethod("findOneLocked", Integer.class);
}
@Test
@@ -214,6 +218,18 @@ public class JpaQueryMethodUnitTests {
assertThat(queryMethod.getNamedQueryName(), is("Foo.bar"));
}
+ /**
+ * @see DATAJPA-73
+ */
+ @Test
+ public void discoversLockModeCorrectly() throws Exception {
+
+ JpaQueryMethod method = new JpaQueryMethod(findWithLockMethod, metadata, extractor);
+ LockModeType lockMode = method.getLockModeType();
+
+ assertEquals(LockModeType.PESSIMISTIC_WRITE, lockMode);
+ }
+
/**
* Interface to define invalid repository methods for testing.
*
@@ -244,11 +260,18 @@ public class JpaQueryMethodUnitTests {
// Modifying and Sort is not allowed
@Modifying
void updateMethod(String firstname, Sort sort);
+ }
+
+ static interface ValidRepository {
@Query(value = "query", nativeQuery = true)
List findByLastname(String lastname);
@Query(name = "Foo.bar")
List findByNamedQuery();
+
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @Query("select u from User u where u.id = ?1")
+ List findOneLocked(Integer primaryKey);
}
}
diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java
index e40ab9f25..07a82281b 100644
--- a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java
+++ b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java
@@ -15,7 +15,10 @@
*/
package org.springframework.data.jpa.repository.sample;
+import javax.persistence.LockModeType;
+
import org.springframework.data.jpa.domain.sample.Role;
+import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.repository.CrudRepository;
/**
@@ -25,4 +28,10 @@ import org.springframework.data.repository.CrudRepository;
*/
public interface RoleRepository extends CrudRepository {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.CrudRepository#findAll()
+ */
+ @Lock(LockModeType.READ)
+ public Iterable findAll();
}
diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
index d14ea82ea..996a6c93b 100644
--- a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
@@ -70,6 +70,9 @@ public class SimpleJpaRepositoryUnitTests {
repo = new SimpleJpaRepository(information, em);
}
+ /**
+ * @see DATAJPA-124
+ */
@Test
public void doesNotActuallyRetrieveObjectsForPageableOutOfRange() {