Java – Spring JPA repository transactionality

javajpaspring-dataspring-transactions

1 quick question on Spring JPA repositories transactionality.
I have a service that is not marked as transactional and calls Spring JPA repository method

userRegistrationRepository.deleteByEmail(email);

And it is defined as

@Repository
public interface UserRegistrationRepository extends JpaRepository<UserRegistration, Long> {

    UserRegistration findByEmail(String email);

    void deleteByEmail(String email);

}

The problem is that it fails with "No EntityManager with actual transaction available for current thread – cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException" exception.

Ok, I can solve it by marking the service or deleteByEmail(..) method as transactional, but I just can't understand why it crashes now. Spring documentation explicitly states that "CRUD methods on repository instances are transactional by default." (http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions), but apparently this one is not… So Is this statement related to only members of CrudRepository?

ps: that's for Spring Data JPA 1.9.4

Best Answer

You are right. Only CRUD methods (CrudRepository methods) are by default marked as transactional. If you are using custom query methods you should explicitly mark it with @Transactional annotation.

@Repository
public interface UserRegistrationRepository extends JpaRepository<UserRegistration, Long> {

    UserRegistration findByEmail(String email);

    @Transactional
    void deleteByEmail(String email);

}

You should also be aware about consequences of marking repository interface methods instead of service methods. If you are using default transaction propagation configuration (Propagation.REQUIRED) then:

The transaction configuration at the repositories will be neglected then as the outer transaction configuration determines the actual one used.

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

If you want more information about how it is implemented, take a look at default CrudRepository / JpaRepository implementation - SimpleJpaRepository (which you are probably using):

https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

The interesting lines are here:

@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

and some of transactional methods here:

@Transactional
public void deleteById(ID id) {
@Transactional
public <S extends T> S save(S entity) {
Related Topic