J'utilise la version de démarrage à ressort 1.2.3.LELEASE avec JPA sur veille prolongée. Je vis l'exception suivante
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]
Voici la structure de mon programme
Classe de configuration
@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));
}
}
Dans le code de la classe de service ci-dessus, quelqu'un peut-il me guider, s'il vous plaît, pourquoi 2 œuvres et 1 exception au lancer.
Merci
Tout d'abord, je cite la Spring-Data JPA Documentation pour expliquer pourquoi la méthode delete
fonctionne dans votre cas (je parle de l'option 2).
Les méthodes CRUD sur les instances de référentiel sont transactionnelles par défaut. Pour opérations de lecture, l'indicateur de configuration de transaction
readOnly
est défini sur true, tous les autres sont configurés avec un@Transactional
simple afin que La configuration de transaction par défaut s'applique. Pour plus de détails, voir JavaDoc de CrudRepository
La méthode delete
est en réalité une méthode de CrudRepository
. Votre référentiel s'étend JpaRepository
qui étend CrudRespository
, il appartient donc à l'interface CrudRepository et, selon la citation ci-dessus, est transactionnel.
Si vous lisez la section Méthode de requête transactionnelle , vous constaterez qu’elle est identique à l’option 4 et que vous saurez comment appliquer un comportement transactionnel personnalisé à toutes les méthodes de votre référentiel . le Exemple 61 de la documentation montre le même scénario que l'option 3.
N'oubliez pas que vous ne travaillez pas avec la logique JDBC. Dans ce cas, la base de données prend en charge les transactions, mais dans un cadre basé sur ORM. Les infrastructures ORM nécessitent une transaction afin de déclencher la synchronisation entre le cache d'objets et la base de données . Vous devez donc être conscient et fournir un contexte de transaction pour les méthodes utilisant une logique ORM telle que deleteByCustomerId
.
Par défaut, @Transactional
(je veux dire sans paramètre), définissez le mode de propagation sur REQUIRED
et readOnly flag sur false. Lorsque vous appelez une méthode annotée à l'intérieur, une transaction est initialisée s'il n'en existe aucune. C’est la raison pour laquelle la solution de contournement de @LucasSaldanha (identique à l’exemple Utilisation d’une façade pour définir des transactions pour plusieurs appels au référentiel}) et l’option 4. Autrement, sans transaction, vous tombez dans l’exception levée de l’option 1.
Ok j'ai trouvé un moyen de le faire fonctionner.
Il suffit de mettre une annotation @Transactional
(org.springframework.transaction.annotation.Transactional) dans votre méthode deleteOrder à OrderService.
@Transactional
public void deleteOrder(long customerId){
repo.deleteByCustomerId(customerId);
}
Je ne sais vraiment pas pourquoi le second fonctionne. Je suppose que puisque c’est une méthode directe de l’interface CrudRepository, il sait comment l’exécuter de manière atomique.
Le premier est un appel à deleteByCustomerId. Cet appel sera traité pour trouver le client avec l'identifiant spécifié, puis le supprimer. Pour une raison quelconque, l'utilisation d'une transaction explicite est utilisée.
Encore une fois, ce n'est qu'une supposition. Je vais essayer de contacter des développeurs Spring et peut-être ouvrir un problème pour vérifier ce comportement.
J'espère que ça aide!
Référence: http://spring.io/guides/gs/managing-transactions/
J'ai toujours l'exception No transactional EntityManager available
même après avoir annoté ma méthode search()
avec @Transactional
.
J'ai suivi ce tutoriel qui décrit comment configurer la recherche Hibernate dans Spring Boot.
Le problème pour moi était que j'avais une dépendance différente sur hibernate-search-orm
. La dépendance qui a fonctionné pour moi sans aucun problème était
compile("org.hibernate:hibernate-search-orm:5.7.0.Final")
Après avoir ajouté ceci au fichier de construction de Gradle, tout a fonctionné comme prévu.
J'espère que cela aide aussi quelqu'un d'autre.