DATAJPA-73 - Added support for locking.
Repository query methods can now be equipped with a @Lock annotation that carries the LockModeType to be used when executing the query. Beyond that, CRUD methods can be redeclared to carry lock metadata as well.
interface UserRepository extends Repository<User, Long> {
// CRUD method redeclaration
@Lock(LockModeType.READ)
List<User> findAll();
// Query method
@Lock(LockModeType.READ)
List<User> findByLastname(String lastname);
}
This commit is contained in:
@@ -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<Role> criteriaQuery;
|
||||
@Mock
|
||||
JpaEntityInformation<Role, Integer> information;
|
||||
@Mock
|
||||
TypedQuery<Role> 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 <T, ID extends Serializable> JpaEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
return (JpaEntityInformation<T, ID>) information;
|
||||
}
|
||||
};
|
||||
|
||||
RoleRepository repository = factory.getRepository(RoleRepository.class);
|
||||
|
||||
repository.findAll();
|
||||
|
||||
verify(query).setLockMode(LockModeType.READ);
|
||||
}
|
||||
}
|
||||
@@ -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<User, Integer> {
|
||||
|
||||
@QueryHints({ @QueryHint(name = "foo", value = "bar") })
|
||||
@@ -108,6 +129,10 @@ public class AbstractJpaQueryTests {
|
||||
|
||||
@QueryHints(value = { @QueryHint(name = "bar", value = "foo") }, forCounting = false)
|
||||
List<User> findByFirstname(String firstname);
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@org.springframework.data.jpa.repository.Query("select u from User u where u.id = ?1")
|
||||
List<User> findOneLocked(Integer primaryKey);
|
||||
}
|
||||
|
||||
class DummyJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
@@ -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<User> findByLastname(String lastname);
|
||||
|
||||
@Query(name = "Foo.bar")
|
||||
List<User> findByNamedQuery();
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select u from User u where u.id = ?1")
|
||||
List<User> findOneLocked(Integer primaryKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Role, Integer> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll()
|
||||
*/
|
||||
@Lock(LockModeType.READ)
|
||||
public Iterable<Role> findAll();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ public class SimpleJpaRepositoryUnitTests {
|
||||
repo = new SimpleJpaRepository<User, Long>(information, em);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-124
|
||||
*/
|
||||
@Test
|
||||
public void doesNotActuallyRetrieveObjectsForPageableOutOfRange() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user