Spring boot + Hibernate + JPA Не доступен транзакционный EntityManager
Я использую spring boot 1.2.3.RELEASE версию с JPA за спящий режим. Я испытываю следующее исключение
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 бросает.
Спасибо
Ответы
Ответ 1
Во-первых, я делаю цитату Spring -Data JPA Documentation, чтобы оправдать, почему метод delete
работает в вашем случае (Я имею в виду опцию 2).
Методы CRUD для экземпляров репозитория по умолчанию являются транзакционными. Для операции чтения, установлен флаг конфигурации транзакции readOnly
для true, все остальные настроены с помощью простого @Transactional
, так что применяется конфигурация транзакции по умолчанию. Подробнее см. В JavaDoc CrudRepository
Метод delete
на самом деле является методом CrudRepository
. Ваш репозиторий расширяет JpaRepository
, который расширяет CrudRespository
, поэтому он принадлежит интерфейсу CrudRepository и в соответствии с приведенной выше транзакцией является транзакционным.
Если вы прочитали раздел Метод транзакционных запросов, вы увидите, что это то же самое, что опция 4 и вы узнаете, как применять пользовательское транзакционное поведение для всех методов вашего репозитория.
Кроме того, в примере 61 документации показан тот же сценарий, что и параметр 3.
Теперь имейте в виду, что вы не работаете с логикой JDBC, и в этом случае база данных заботится о транзакциях, но в рамках ORM. Структуры ORM требуют транзакции, чтобы инициировать синхронизацию между кешем объекта и базой данных.
Поэтому вы должны знать и предоставлять контекст транзакции для методов, которые выполняют логику ORM, например deleteByCustomerId
.
По умолчанию @Transactional
(я имею в виду без каких-либо параметров) установите режим распространения на REQUIRED
и флаг readOnly на false. Когда вы вызываете метод, аннотированный внутри, транзакция является intialized, если никто не существует. Именно по этой причине работает обходной путь @LucasSaldanha (такой же, как пример с использованием фасада для определения транзакций для нескольких вызовов репозитория) и опция 4. В другом случае, без транзакции, вы попадаете в исключенную опцию 1.
Ответ 2
Хорошо, я выяснил, как это работает.
Просто добавьте аннотацию @Transactional
(org.springframework.transaction.annotation.Transactional) в свой метод deleteOrder в OrderService.
@Transactional
public void deleteOrder(long customerId){
repo.deleteByCustomerId(customerId);
}
Я действительно не знаю, почему второй работает. Я предполагаю, что, поскольку это прямой метод из интерфейса CrudRepository, он знает, как его выполнить атомарно.
Первый - вызов deleteByCustomerId. Этот вызов будет обработан, чтобы узнать клиента с указанным идентификатором, а затем удалит его. По какой-то причине он использует явную транзакцию.
Опять же, это просто догадка. Я попытаюсь связаться с некоторыми разработчиками spring и, возможно, открыть проблему, чтобы проверить это поведение.
Надеюсь, что это поможет!
Ссылка: http://spring.io/guides/gs/managing-transactions/