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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user