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:
Oliver Gierke
2011-12-06 10:58:31 +01:00
parent d49d4d5f0a
commit c915077074
14 changed files with 434 additions and 25 deletions

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -70,6 +70,9 @@ public class SimpleJpaRepositoryUnitTests {
repo = new SimpleJpaRepository<User, Long>(information, em);
}
/**
* @see DATAJPA-124
*/
@Test
public void doesNotActuallyRetrieveObjectsForPageableOutOfRange() {