it-swarm.com.ru

Spring boot + Hibernate + JPA Нет доступного транзакционного EntityManager

Я использую весеннюю загрузку версии 1.2.3.RELEASE с JPA поверх Hibernate. Я испытываю следующее исключение

org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.Java:410) ~[EntityManagerFactoryUtils.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.Java:223) ~[HibernateJpaDialect.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.Java:417) ~[AbstractEntityManagerFactoryBean.class:4.1.6.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.Java:59) ~[ChainedPersistenceExceptionTranslator.class:4.1.6.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.Java:213) ~[DataAccessUtils.class:4.1.6.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.Java:147) ~[PersistenceExceptionTranslationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.Java:122) ~[CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.class:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:92) [ExposeInvocationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:207) [JdkDynamicAopProxy.class:4.1.6.RELEASE]
at com.Sun.proxy.$Proxy110.deleteByCustomerId(Unknown Source) ~[na:na]

Caused by: javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.Java:275) ~[SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.class:4.1.6.RELEASE]
at com.Sun.proxy.$Proxy102.remove(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.Java:270) ~[JpaQueryExecution$DeleteExecution.class:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.Java:74) ~[JpaQueryExecution.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.Java:97) ~[AbstractJpaQuery.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.Java:88) ~[AbstractJpaQuery.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.Java:395) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.Java:373) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]

Ниже приведена структура моей программы 
Класс конфигурации

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableTransactionManagement
public class WSApplication {
    public static void main(final String[] args) {
        SpringApplication.run(WSApplication.class, args);
    }
}

@Entity
@Table(Orders)
public class Order {
    @id
    @GeneratedValue
    private long id;

    @Column(name = "customerId")
    private Long customerId;

    // getter & setter methods
    // equals & hashCode methods
}

public interface OrderRepository extends JpaRepository<Order, Long> {

    List<Order> findByCustomerId(Long customerId);

    // 4- @Transactional works fine
    void deleteByCustomerId(Long cusotmerId);

}

public class OrderService {

    @Autowired
    private OrderRepository repo;

    // 3- @Transactional works fine
    public void deleteOrder(long customerId){
        //1- throws exception
        repo.deleteByCustomerId(customerId); 

        //2- following works fine
        //repo.delete(repo.findByCustomerId(customerId).get(0));
    }

}

В приведенном выше коде класса обслуживания, может кто-нибудь, пожалуйста, объясните мне, почему 2 работает и 1 выбрасывает исключение.

Спасибо

12
amique

Сначала я приведу цитату из документации JPA Spring-Data , чтобы обосновать, почему метод delete работает в вашем случае (я имею в виду параметр 2).

Методы CRUD в экземплярах репозитория являются транзакционными по умолчанию. За Операции чтения. В конфигурации транзакции установлен флаг readOnly в true все остальные настроены с простым @Transactional, так что применяется конфигурация транзакции по умолчанию. Подробнее см. JavaDoc of CrudRepository

Метод delete на самом деле является методом CrudRepository. Ваш репозиторий расширяет JpaRepository, который расширяет CrudRespository, поэтому он принадлежит интерфейсу CrudRepository и согласно приведенной выше цитате является транзакционным.

Если вы прочитаете раздел Transactional Query Method вы увидите, что это то же самое, что и параметр 4, и вы узнаете, как применить настраиваемое поведение транзакций для всех методов вашего репозитория . Также, Пример 61 документации показывает тот же сценарий, что и опция 3.

Теперь имейте в виду, что вы не работаете с логикой JDBC, в этом случае база данных заботится о транзакциях, но в рамках ORM-среды. Каркасам ORM требуется транзакция, чтобы запустить синхронизацию между кешем объектов и базой данных . Поэтому вы должны знать и предоставлять контекст транзакции для методов, которые выполняют логику ORM, такую ​​как deleteByCustomerId.

По умолчанию @Transactional (я имею в виду без каких-либо параметров) устанавливает режим распространения на REQUIRED и readOnly помечает как false. Когда вы вызываете метод, аннотированный внутри, транзакция инициализируется, если никто не существует. Это причина того, почему обходное решение @LucasSaldanha (аналогично примеру Использование фасада для определения транзакций для нескольких вызовов репозитория) и опции 4 работает. В противном случае, без транзакции вы попадаете в выброшенное исключение опции 1.

13
Guillermo

Хорошо, я нашел способ заставить это работать.

Просто поместите аннотацию @Transactional (org.springframework.transaction.annotation.Transactional) в ваш метод deleteOrder в OrderService.

@Transactional
public void deleteOrder(long customerId){
    repo.deleteByCustomerId(customerId);
}

Я действительно не знаю, почему второй работает. Я предполагаю, что, поскольку это прямой метод из интерфейса CrudRepository, он знает, как выполнить его атомарно.

Первый - это вызов deleteByCustomerId. Этот вызов будет обработан, чтобы узнать клиента с указанным идентификатором, а затем удалит его. По какой-то причине он использует явную транзакцию.

Опять же это просто предположение. Я попытаюсь связаться с некоторыми весенними разработчиками и, возможно, открыть вопрос, чтобы проверить это поведение.

Надеюсь, поможет!

Ссылка: http://spring.io/guides/gs/managing-transactions/

2
Lucas Saldanha

Я по-прежнему получаю исключение No transactional EntityManager available даже после аннотирования моего метода search() с помощью @Transactional.

Я следовал этому учебнику который описывает, как настроить поиск в Hibernate в Spring Boot.

Проблема для меня заключалась в том, что у меня была другая зависимость от hibernate-search-orm. Зависимость, которая работала для меня без проблем, была

compile("org.hibernate:hibernate-search-orm:5.7.0.Final")

После добавления этого в файл сборки gradle все заработало как положено. 

Надеюсь, это поможет кому-то еще.

1
Patrick