Primavera - Nessun EntityManager con transazione effettiva disponibile per il thread corrente - impossibile elaborare in modo affidabile la chiamata 'persist'


137

Ottengo questo errore quando provo a invocare il metodo "persist" per salvare il modello di entità nel database nella mia applicazione web Spring MVC. Non riesco davvero a trovare alcun post o pagina in Internet che possa riguardare questo particolare errore. Sembra che ci sia qualcosa di sbagliato nel bean EntityManagerFactory ma sono abbastanza nuovo nella programmazione di Spring, quindi per me sembra che tutto sia inizializzato bene e secondo vari articoli tutorial nel 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";

        }




    }

1
Come dice l'errore, non c'è transazione. Annota il metodo di registrazione con @Transaction.
Rohit,

Risposte:


260

Ho avuto lo stesso problema e ho annotato il metodo @Transactionale ha funzionato.

AGGIORNAMENTO: controllando la documentazione di primavera sembra che PersistenceContext sia di tipo Transaction, quindi è per questo che il metodo deve essere transazionale ( http://docs.spring.io/spring/docs/current/spring-framework-reference/ html / orm.html ):

L'annotazione @PersistenceContext ha un tipo di attributo opzionale, il cui valore predefinito è PersistenceContextType.TRANSACTION. Questa impostazione predefinita è ciò di cui hai bisogno per ricevere un proxy EntityManager condiviso. L'alternativa, PersistenceContextType.EXTENDED, è un affare completamente diverso: questo si traduce in un cosiddetto EntityManager esteso, che non è thread-safe e quindi non deve essere utilizzato in un componente con accesso simultaneo come un bean singleton gestito da Spring. Gli EntityManager estesi devono essere utilizzati solo in componenti con stato che, ad esempio, risiedono in una sessione, con il ciclo di vita dell'EntityManager non legato a una transazione corrente ma piuttosto completamente all'applicazione.


71
Se un metodo senza l' @Transactionalannotion chiama un metodo con l' @Transactionalannotazione nello stesso file di classe, anche tu sarai colpito da questo errore (è quello che stavo affrontando).
Jacob van Lingen,

5
Ho annotato la classe di servizio con @Transactional, che funziona anche. Non sono sicuro che sia la strada giusta da percorrere ma sembra che funzioni bene ...
milosmns

8
Un'altra cosa degna di nota è che questa annotazione dovrebbe essere usata per un metodo pubblico poiché non funziona diversamente.
Yuriy Kravets

7
Ricorda, per favore, che @Transactional funziona solo con metodi pubblici.
Andrei_N

7
Cordiali saluti, questo ha bisogno dell'annotazione javax.transaction.Transactional (non quella di primavera).
java-addict301,

85

Ho ottenuto questa eccezione durante il tentativo di utilizzare un metodo personalizzato deleteBy nel repository di dati di primavera. L'operazione è stata tentata da una classe di test JUnit.

L'eccezione non si verifica quando si utilizza l' @Transactionalannotazione a livello di classe JUnit.


8
Ero nella stessa situazione, invece di annotare la classe di test ho annotato il metodo di servizio in modo che gocciolasse anche se non era un test.
Paul Nelson Baker,

@Transactionala livello di classe può mascherare possibili problemi di test che manipolano diverse transazioni nei tuoi servizi.
Zon

1
Ho usato @Trasactionalil metodo repository dal momento che è il luogo in cui sto effettivamente interagendo con il database e il suo funzionamento bene.
Harish Kumar Saini,

22

Questo errore mi ha provocato per tre giorni, la situazione che ho affrontato ha prodotto lo stesso errore. Seguendo tutti i consigli che ho trovato, ho giocato con la configurazione ma senza risultati.

Alla fine l'ho trovata, la differenza, il servizio che stavo eseguendo era contenuto in un vaso comune, il problema si è rivelato essere AspectJ che non trattava l'istanza del servizio allo stesso modo. In effetti il ​​proxy stava semplicemente chiamando il metodo sottostante senza che tutta la normale magia di Primavera fosse eseguita prima della chiamata del metodo.

Alla fine l'annotazione @Scope collocata sul servizio come nell'esempio ha risolto il problema:

@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();
    }

}

Il metodo che ho pubblicato è un metodo di eliminazione ma le annotazioni influenzano tutti i metodi di persistenza allo stesso modo.

Spero che questo post aiuti qualcun altro che ha lottato con lo stesso problema durante il caricamento di un servizio da un vaso


L'aggiunta @Scope(proxyMode = ScopedProxyMode.INTERFACES)alla classe DAO che implementa un'interfaccia è davvero importante. Ho trascorso l'intera giornata a capire questo errore e la tua soluzione è l'unica che funziona. Grazie mille!
Thach Van,

1
Un'annotazione dalla tua risposta mi ha appena salvato probabilmente circa 3 giorni. Ho appena sperimentato il vero potere dell'edenema. Дякс.
Oleksii Kyslytsyn


8

boardRepo.deleteByBoardId (id);

Di fronte allo stesso problema. GOT javax.persistence.TransactionRequiredException: nessuna EntityManager con transazione effettiva disponibile per il thread corrente

L'ho risolto aggiungendo l' annotazione transazionale sopra il controller / servizio.


7

Ho avuto lo stesso problema e ho aggiunto tx:annotation-drivenin applicationContext.xmle ha funzionato.


4

Ho avuto lo stesso errore durante l'accesso a un metodo con annotazioni già transazionali da un metodo non transazionale all'interno dello stesso componente:

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();

Ho corretto l'errore chiamando executeQuery () sul componente autoreferenziale:

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
        }
    }

3

L'aggiunta org.springframework.transaction.annotation.Transactionaldell'annotazione a livello di classe per la classe di test ha risolto il problema per me.


3

Solo una nota per gli altri utenti che cercano risposte per l'errore. Un altro problema comune è:

In genere non è possibile chiamare un @transactionalmetodo all'interno della stessa classe.

(Esistono modi e mezzi per utilizzare AspectJ ma il refactoring sarà molto più semplice)

Quindi avrai bisogno di una classe chiamante e di una classe che contenga i @transactionalmetodi.


2

Per noi, il problema si riduceva alle stesse impostazioni di contesto in più file di configurazione. Verifica di non aver duplicato quanto segue in più file di configurazione.

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

2

Ho avuto lo stesso codice di errore quando ho usato @Transactionsu un metodo / actionlevel errato.

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

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

Ho dovuto posizionare @Transactionalil metodo appena sopra il metodo methodWithANumberOfDatabaseActions(), ovviamente.

Ciò ha risolto il messaggio di errore nel mio caso.


0

Ho rimosso la modalità da

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

per far funzionare questo


0

Ho avuto questo problema per giorni e nulla che ho trovato ovunque online mi ha aiutato, sto pubblicando la mia risposta qui nel caso in cui aiuti qualcun altro.

Nel mio caso, stavo lavorando a un microservizio richiamato tramite remoting e la mia annotazione transazionale a livello di servizio non veniva rilevata dal proxy remoto.

Aggiungendo una classe delegata tra i livelli di servizio e dao e contrassegnando il metodo delegato come transazionale, questo è stato risolto per me.


0

Questo ci ha aiutato, forse può aiutare gli altri in futuro. @Transactionnon funzionava per noi, ma questo ha fatto:

@ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")


0

Se hai

@Transactional // Spring Transactional
class MyDao extends Dao {
}

e super classe

class Dao {
    public void save(Entity entity) { getEntityManager().merge(entity); }
}

e tu chiami

@Autowired MyDao myDao;
myDao.save(entity);

non riceverai un Spring TransactionInterceptor (che ti dà una transazione).

Questo è quello che hai bisogno di fare:

@Transactional 
class MyDao extends Dao {
    public void save(Entity entity) { super.save(entity); }
}

Incredibile ma vero.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.