Ich verwende Spring Boot 1.2.3.RELEASE-Version mit JPA über dem Ruhezustand. Ich erlebe folgende Ausnahme
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]
Nachfolgend ist meine Programmstruktur
Konfigurationsklasse
@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));
}
}
Kann sich jemand in dem oben genannten Service-Klassencode anführen, warum 2 funktioniert und 1 eine Ausnahme auslöst.
Vielen Dank
Zunächst zitiere ich die Spring-Data JPA Documentation , um zu begründen, warum die delete
-Methode in Ihrem Fall funktioniert (ich meine die Option 2).
CRUD-Methoden für Repository-Instanzen sind standardmäßig transaktional. Zum Bei Leseoperationen wird das Flag
readOnly
der Transaktionskonfiguration gesetzt wahr, alle anderen sind mit einem einfachen@Transactional
konfiguriert, so dass Die Standardtransaktionskonfiguration gilt. Für Details siehe JavaDoc von CrudRepository
Die delete
-Methode ist eigentlich eine Methode der CrudRepository
. Ihr Repository erweitert JpaRepository
, wodurch CrudRespository
erweitert wird. Daher gehört es zur CrudRepository-Schnittstelle und ist gemäß dem oben genannten Zitat transaktional.
Wenn Sie den Abschnitt Transaktionsabfragemethode lesen, werden Sie feststellen, dass die Option 4 derselbe ist und Sie wissen, wie Sie ein benutzerdefiniertes Transaktionsverhalten für alle Methoden Ihres Repositorys anwenden. Auch das Example 61 der Dokumentation zeigt das gleiche Szenario wie die Option 3.
Denken Sie jetzt daran, dass Sie nicht mit JDBC-Logik arbeiten. In diesem Fall kümmert sich die Datenbank um die Transaktionen, jedoch innerhalb eines ORM-basierten Rahmens. Für ORM-Frameworks ist eine Transaktion erforderlich, um die Synchronisation zwischen dem Objektcache und der Datenbank auszulösen. Sie müssen also einen Transaktionskontext für Methoden bereitstellen, die ORM-Logik ausführen, wie deleteByCustomerId
.
Standardmäßig setzen @Transactional
(ich meine ohne Parameter) den Ausbreitungsmodus auf REQUIRED
und das Flag readOnly auf false. Wenn Sie eine mit Anmerkungen versehene Methode aufrufen, wird eine Transaktion initialisiert, wenn keine vorhanden ist. Dies ist der Grund, warum der Workaround von @LucasSaldanha (wie im Beispiel Verwenden einer Fassade zum Definieren von Transaktionen für mehrere Repository-Aufrufe) und die Option 4 funktioniert. Andernfalls fallen Sie ohne Transaktion in die geworfene Ausnahme der Option 1.
Ok, ich habe herausgefunden, wie es funktioniert.
Geben Sie einfach eine @Transactional
-Annotation (org.springframework.transaction.annotation.Transactional) in Ihre deleteOrder -Methode unter OrderService ein.
@Transactional
public void deleteOrder(long customerId){
repo.deleteByCustomerId(customerId);
}
Ich weiß wirklich nicht, warum der zweite funktioniert. Ich schätze, da es sich um eine direkte Methode der CrudRepository - Schnittstelle handelt, weiß man, wie es atomar ausgeführt wird.
Die vorherige ist ein Aufruf der deleteByCustomerId. Dieser Anruf wird verarbeitet, um den Kunden mit der angegebenen ID herauszufinden und diese zu löschen. Aus irgendeinem Grund verwendet es eine explizite Transaktion.
Wieder ist es nur eine Vermutung. Ich werde versuchen, einige Frühlingsentwickler zu kontaktieren und möglicherweise ein Problem zu eröffnen, um dieses Verhalten zu überprüfen.
Ich hoffe es hilft!
Ich erhielt immer noch die No transactional EntityManager available
-Ausnahme, nachdem ich meine search()
-Methode mit @Transactional
versehen hatte.
Ich folgte diesem Tutorial , das beschreibt, wie die Hibernate-Suche in Spring Boot eingerichtet wird.
Das Problem für mich war, dass ich eine andere Abhängigkeit von hibernate-search-orm
hatte. Die Abhängigkeit, die für mich ohne Probleme funktionierte, war
compile("org.hibernate:hibernate-search-orm:5.7.0.Final")
Nachdem dies der Gradle-Build-Datei hinzugefügt wurde, funktionierte alles wie erwartet.
Hoffe, das hilft auch jemand anderem.