web-dev-qa-db-fra.com

Spring - Pas de EntityManager avec transaction réelle disponible pour le thread actuel - impossible de traiter de manière fiable l'appel 'persistant'

Je reçois cette erreur lorsque j'appelle la méthode "persist" pour enregistrer le modèle d'entité dans une base de données dans mon application Web Spring MVC. Impossible de trouver vraiment un message ou une page sur Internet pouvant être en relation avec cette erreur particulière. Il semble que quelque chose ne va pas avec le bean EntityManagerFactory, mais je suis assez nouveau dans la programmation Spring, donc pour moi, tout semble être initialisé correctement et selon divers articles de tutoriel sur le Web.

dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-4.0.xsd
  http://www.springframework.org/schema/jdbc
  http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
  http://www.springframework.org/schema/data/jpa
  http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
  http://www.springframework.org/schema/data/repository
  http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
  http://www.springframework.org/schema/jee
  http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">

    <context:component-scan base-package="wymysl.Controllers" />
    <jpa:repositories base-package="wymysl.repositories"/> 
    <context:component-scan base-package="wymysl.beans" /> 
    <context:component-scan base-package="wymysl.Validators" /> 
    <bean
     class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
     <bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>

     <bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean>

     <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <property name="driverClassName" value="Oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:Oracle:thin:@localhost:1521:xe" />
        <property name="username" value="system" />
        <property name="password" value="polskabieda1" />
    </bean>

 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
            <property name="showSql" value="true" />
            <property name="generateDdl" value="false" />
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.max_fetch_depth">3</prop>
            <prop key="hibernate.jdbc.fetch_size">50</prop>
            <prop key="hibernate.jdbc.batch_size">10</prop>
        </props>
    </property>
</bean>

    <mvc:annotation-driven />

    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:messages" />
</bean>

    <bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
             <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/jsp/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:resources mapping="/resources/*" location="/resources/css/"  
    cache-period="31556926"/>



</beans>

RegisterController.Java

@Controller
public class RegisterController {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    PasswordValidator passwordValidator;

    @InitBinder
    private void initBinder(WebDataBinder binder) {
        binder.setValidator(passwordValidator);
    }

    @RequestMapping(value = "/addUser", method = RequestMethod.GET)
    public String register(Person person) {


        return "register";

    }

    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public String register(@ModelAttribute("person") @Valid @Validated Person person, BindingResult result) {
        if(result.hasErrors()) {
            return "register";
        } else {
            entityManager.persist(person);
            return "index";

        }




    }
96
Michał Bil

J'ai eu le même problème et j'ai annoté la méthode comme @Transactional et cela a fonctionné.

UPDATE: en vérifiant la documentation print, il semble que PersistenceContext soit par défaut de type Transaction. C'est pourquoi la méthode doit être transactionnelle ( http://docs.spring.io/spring/docs/current/spring- framework-reference/html/orm.html ):

L'annotation @PersistenceContext a un type d'attribut facultatif, qui par défaut est PersistenceContextType.TRANSACTION. Cette valeur par défaut correspond à ce dont vous avez besoin pour recevoir un proxy EntityManager partagé. L’alternative, PersistenceContextType.EXTENDED, est une affaire complètement différente: il en résulte un EntityManager étendu, qui n’est pas thread-safe et ne doit donc pas être utilisé dans un composant accédé simultanément tel qu'un bean singleton géré par Spring. Les EntityManagers étendus ne sont supposés être utilisés que dans des composants avec état qui, par exemple, résident dans une session, le cycle de vie de EntityManager n'étant pas lié à une transaction en cours, il dépend entièrement de l'application.

195
mlg

J'ai eu cette exception en essayant d'utiliser une méthode personnalisée deleteBy dans le référentiel de données Spring. L'opération a été tentée à partir d'une classe de test JUnit.

L'exception ne se produit pas lors de l'utilisation de l'annotation @Transactional au niveau de la classe JUnit.

60

Cette erreur m'a rongé pendant trois jours, la situation à laquelle j'ai été confronté a produit la même erreur. Après tous les conseils que j'ai pu trouver, j'ai joué avec la configuration, mais en vain.

Finalement, j'ai trouvé que la différence, le service que j'exécutais était contenu dans un pot commun, le problème s'est avéré être AspectJ ne traitant pas l'instanciation du service de la même manière. En réalité, le proxy appelait simplement la méthode sous-jacente sans que toute la magie Spring normale soit exécutée avant l'appel de la méthode.

Finalement, l'annotation @Scope placée sur le service conformément à l'exemple a résolu le problème:

@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Transactional
public class CoreServiceImpl implements CoreService {
    @PersistenceContext
    protected EntityManager entityManager;

    @Override
    public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
        CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
        criteriaDelete.from(clazz);
        return entityManager.createQuery(criteriaDelete).executeUpdate();
    }

}

La méthode que j'ai publiée est une méthode de suppression, mais les annotations affectent toutes les méthodes de persistance de la même manière.

J'espère que ce message aidera quelqu'un qui a eu des difficultés avec le même problème lors du chargement d'un service depuis un bocal

15
Chris March

J'ai eu la même erreur parce que je suis passé de la configuration XML à la configuration Java.

Le fait était que je n’ai pas migré la balise <tx:annotation-driven/>, comme l’a suggéré Stone Feng.

Je viens donc d'ajouter @EnableTransactionManagement comme suggéré ici Configuration de transactions basées sur des annotations au printemps dans la classe @Configuration , et cela fonctionne maintenant.

7
Sergej Werfel

J'ai eu le même problème et j'ai ajouté tx:annotation-driven dans applicationContext.xml et cela a fonctionné.

4
Stone Feng

boardRepo.deleteByBoardId (id);

Face au même problème. GOT javax.persistence.TransactionRequiredException: aucun EntityManager avec transaction réelle disponible pour le thread actuel

Je l'ai résolu en ajoutant @ Transactional annotation au-dessus du contrôleur/service.

2
Vikram S

La même erreur s'est produite lors de l'accès à une méthode déjà annotée de transaction à partir d'une méthode non transactionnelle au sein du même composant:

Before:
    @Component
    public class MarketObserver {
        @PersistenceContext(unitName = "maindb")
        private EntityManager em;

        @Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
        public void executeQuery() {
          em.persist(....);
        }


        @Async
        public void startObserving() {
          executeQuery(); //<-- Wrong
        }
    }

    //In another bean:
     marketObserver.startObserving();

J'ai corrigé l'erreur en appelant executeQuery () sur le composant auto-référencé:

Fixed version:
    @Component
    public class MarketObserver {
        @PersistenceContext(unitName = "maindb")
        private EntityManager em;

        @Autowired
        private GenericApplicationContext context;

        @Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
        public void executeQuery() {
          em.persist(....);
        }


        @Async
        public void startObserving() {
          context.getBean(MarketObserver.class).executeQuery(); //<-- Works
        }
    }
1
YDZOGODOQ

Pour nous, le problème était lié aux mêmes paramètres de contexte dans plusieurs fichiers de configuration. Vérifiez que vous n'avez pas dupliqué les éléments suivants dans plusieurs fichiers de configuration.

<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
1
Nick West

J'avais le même code d'erreur lorsque j'ai utilisé @Transaction sur une méthode/un niveau d'action incorrect.

methodWithANumberOfDatabaseActions() { 
   methodA( ...)
   methodA( ...)
}

@Transactional
void methodA( ...) {
  ... ERROR message
}

Je devais placer le @Transactional juste au-dessus de la méthode methodWithANumberOfDatabaseActions(), bien sûr.

Cela a résolu le message d'erreur dans mon cas.

0
user9835597

J'avais ce problème depuis des jours et rien que je n'ai trouvé nulle part en ligne ne m'aidait, je publie ma réponse ici au cas où cela aiderait quelqu'un d'autre.

Dans mon cas, je travaillais sur un microservice appelé par le biais de la communication à distance, et mon annotation @Transactional au niveau du service n'était pas capturée par le proxy distant.

Ajouter une classe de délégué entre les couches service et dao et marquer la méthode de délégué comme étant transactionnelle a résolu ce problème.

0
fleeblewidget

J'ai enlevé le mode de

<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />

faire ce travail

0
ropo