Forward ported fix of Hades bug #421.

Moved transaction configuration into JpaRepository interface and redeclared methods of Repository and PagingAndSortingRepository. This is unfortunately necessary as otherwise the custom transaction configuration inside a user's repository interface (e.g. redefining transaction configuration in findAll()) would not be considered as AbstractFallbackTransactionAttributeSource prefers implementation configuration as this is usually more specific (except in our case). This unfortunately requires the redeclaration of the methods.
This commit is contained in:
Oliver Gierke
2011-02-09 13:53:07 +01:00
parent 6b1aabfa91
commit a005f0bd6e
5 changed files with 170 additions and 11 deletions

View File

@@ -22,19 +22,132 @@ import javax.persistence.EntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* JPA specific extension of {@link Repository}.
* JPA specific extension of {@link Repository}. Redeclares methods from
* {@link Repository} and {@link PagingAndSortingRepository} to apply
* transaction configuration to those. We need to do this to allow specific
* extensions of the interface to override transaction configuration. If we'd
* annotated the implementation this configuration would always enjoy precedence
* over the configuration applied on an interface.
*
* @author Oliver Gierke
*/
@Transactional(readOnly = true)
public interface JpaRepository<T, ID extends Serializable> extends
PagingAndSortingRepository<T, ID> {
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#save(java.lang.Object)
*/
@Transactional
T save(T entity);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#save(java.lang.Iterable)
*/
@Transactional
List<T> save(Iterable<? extends T> entities);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#findById(java.io.Serializable
* )
*/
T findById(ID id);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#exists(java.io.Serializable
* )
*/
boolean exists(ID id);
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#findAll()
*/
List<T> findAll();
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#count()
*/
Long count();
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#delete(java.lang.Object)
*/
@Transactional
void delete(T entity);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#delete(java.lang.Iterable)
*/
@Transactional
void delete(Iterable<? extends T> entities);
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#deleteAll()
*/
@Transactional
void deleteAll();
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.PagingAndSortingRepository#findAll
* (org.springframework.data.domain.Sort)
*/
List<T> findAll(Sort sort);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.PagingAndSortingRepository#findAll
* (org.springframework.data.domain.Pageable)
*/
Page<T> findAll(Pageable pageable);
/**
* Returns a single entity matching the given {@link Specification}.
*
@@ -67,6 +180,7 @@ public interface JpaRepository<T, ID extends Serializable> extends
/**
* Flushes all pending changes to the database.
*/
@Transactional
void flush();
@@ -76,6 +190,7 @@ public interface JpaRepository<T, ID extends Serializable> extends
* @param entity
* @return the saved entity
*/
@Transactional
T saveAndFlush(T entity);
@@ -86,5 +201,6 @@ public interface JpaRepository<T, ID extends Serializable> extends
*
* @param entities
*/
@Transactional
void deleteInBatch(Iterable<T> entities);
}

View File

@@ -35,7 +35,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
@@ -49,7 +48,6 @@ import org.springframework.util.Assert;
* @param <ID> the type of the entity's identifier
*/
@org.springframework.stereotype.Repository
@Transactional
public class SimpleJpaRepository<T, ID extends Serializable> extends
JpaRepositorySupport<T, ID> {
@@ -174,7 +172,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* org.springframework.data.repository.Repository#readById(java.io.Serializable
* )
*/
@Transactional(readOnly = true)
public T findById(ID id) {
Assert.notNull(id, "The given id must not be null!");
@@ -189,7 +186,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* org.springframework.data.repository.Repository#exists(java.io.Serializable
* )
*/
@Transactional(readOnly = true)
public boolean exists(ID id) {
Assert.notNull(id, "The given id must not be null!");
@@ -202,7 +198,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
*
* @see org.springframework.data.repository.Repository#readAll()
*/
@Transactional(readOnly = true)
public List<T> findAll() {
return getQuery(null, (Sort) null).getResultList();
@@ -216,7 +211,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* org.springframework.data.repository.Repository#readAll(org.springframework
* .data.domain.Sort)
*/
@Transactional(readOnly = true)
public List<T> findAll(Sort sort) {
return getQuery(null, sort).getResultList();
@@ -229,7 +223,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* @see org.springframework.data.repository.Repository#readAll(org.
* springframework.data.domain.Pageable)
*/
@Transactional(readOnly = true)
public Page<T> findAll(Pageable pageable) {
if (null == pageable) {
@@ -262,7 +255,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* @see org.springframework.data.jpa.repository.JpaRepository#readAll(org.
* springframework.data.jpa.domain.Specification)
*/
@Transactional(readOnly = true)
public List<T> findAll(Specification<T> spec) {
return getQuery(spec, (Sort) null).getResultList();
@@ -276,7 +268,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
* springframework.data.jpa.domain.Specification,
* org.springframework.data.domain.Pageable)
*/
@Transactional(readOnly = true)
public Page<T> findAll(Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = getQuery(spec, pageable);
@@ -291,7 +282,6 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
*
* @see org.springframework.data.repository.Repository#count()
*/
@Transactional(readOnly = true)
public Long count() {
return em.createQuery(getCountQueryString(), Long.class)

View File

@@ -15,10 +15,15 @@
*/
package org.springframework.data.jpa.repository.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.custom.UserCustomExtendedRepository;
import org.springframework.data.jpa.repository.support.TransactionalRepositoryTests.DelegatingTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
@@ -43,6 +48,16 @@ public class CustomRepositoryFactoryConfigTests {
@Autowired(required = false)
UserCustomExtendedRepository userRepository;
@Autowired
DelegatingTransactionManager transactionManager;
@Before
public void setup() {
transactionManager.resetCount();
}
@Test(expected = UnsupportedOperationException.class)
public void testCustomFactoryUsed() {
@@ -50,4 +65,24 @@ public class CustomRepositoryFactoryConfigTests {
Assert.notNull(userRepository);
userRepository.customMethod(1);
}
@Test
public void reconfiguresTransactionalMethodWithoutGenericParameter() {
userRepository.findAll();
assertFalse(transactionManager.getDefinition().isReadOnly());
assertThat(transactionManager.getDefinition().getTimeout(), is(10));
}
@Test
public void reconfiguresTransactionalMethodWithGenericParameter() {
userRepository.findById(1);
assertFalse(transactionManager.getDefinition().isReadOnly());
assertThat(transactionManager.getDefinition().getTimeout(), is(10));
}
}

View File

@@ -15,7 +15,10 @@
*/
package org.springframework.data.jpa.repository.custom;
import java.util.List;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -27,4 +30,18 @@ import org.springframework.data.jpa.domain.sample.User;
public interface UserCustomExtendedRepository extends
CustomGenericRepository<User, Integer> {
/**
* Sample method to test reconfiguring transactions on CRUD methods in
* combination with custom factory.
*
* @see #421
*/
@Transactional(readOnly = false, timeout = 10)
List<User> findAll();
@Transactional(readOnly = false, timeout = 10)
User findById(Integer id);
}

View File

@@ -7,6 +7,7 @@
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<import resource="../infrastructure.xml" />
<import resource="../tx-manager.xml" />
<jpa:repositories base-package="org.springframework.**.repository.custom"
factory-class="org.springframework.data.jpa.repository.custom.CustomGenericJpaRepositoryFactoryBean" />