Spring - Kein EntityManager mit der tatsächlichen Transaktion für den aktuellen Thread verfügbar - kann den Aufruf "Persist" nicht zuverlässig verarbeiten

134

Ich erhalte diese Fehlermeldung, wenn ich versuche, die Methode "persist" aufzurufen, um das Entitätsmodell in meiner Spring MVC-Webanwendung in der Datenbank zu speichern. Ich kann im Internet keinen Beitrag oder keine Seite finden, die sich auf diesen bestimmten Fehler beziehen können. Es scheint, als ob etwas mit der EntityManagerFactory-Bean nicht stimmt, aber ich bin ziemlich neu in der Spring-Programmierung. Für mich scheint alles gut initialisiert zu sein und entspricht verschiedenen Tutorial-Artikeln im 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";

        }




    }
Michał Bil
quelle
1
Wie der Fehler sagt, gibt es keine Transaktion. Kommentieren Sie die Registermethode mit @Transaction.
Rohit

Antworten:

258

Ich hatte das gleiche Problem und habe die Methode wie kommentiert @Transactionalund es hat funktioniert.

UPDATE: Wenn Sie die Spring-Dokumentation überprüfen, sieht es so aus, als ob der PersistenceContext standardmäßig vom Typ Transaction ist. Deshalb muss die Methode transaktional sein ( http://docs.spring.io/spring/docs/current/spring-framework-reference/). html / orm.html ):

Die Annotation @PersistenceContext verfügt über einen optionalen Attributtyp, der standardmäßig PersistenceContextType.TRANSACTION ist. Diese Standardeinstellung ist erforderlich, um einen freigegebenen EntityManager-Proxy zu erhalten. Die Alternative PersistenceContextType.EXTENDED ist eine völlig andere Angelegenheit: Dies führt zu einem sogenannten erweiterten EntityManager, der nicht threadsicher ist und daher nicht in einer Komponente verwendet werden darf, auf die gleichzeitig zugegriffen wird, z. B. einer von Spring verwalteten Singleton-Bean. Erweiterte EntityManager sollten nur in statusbehafteten Komponenten verwendet werden, die sich beispielsweise in einer Sitzung befinden, wobei der Lebenszyklus des EntityManager nicht an eine aktuelle Transaktion gebunden ist, sondern vollständig der Anwendung entspricht.

mlg
quelle
71
Wenn eine Methode ohne @TransactionalAnnotation eine Methode mit der @TransactionalAnnotation in derselben Klassendatei aufruft, werden Sie ebenfalls von diesem Fehler betroffen sein (das war es, was mir bevorstand).
Jacob van Lingen
5
Ich habe die Serviceklasse mit kommentiert @Transactional, das funktioniert auch. Ich
bin
8
Eine andere erwähnenswerte Sache ist, dass diese Annotation für öffentliche Methoden verwendet werden sollte, da sie sonst nicht funktioniert.
Yuriy Kravets
7
Denken Sie bitte daran, dass @Transactional nur mit öffentlichen Methoden funktioniert.
Andrei_N
7
Zu Ihrer Information, dies erfordert die Annotation javax.transaction.Transactional (nicht die Annotation Spring).
Java-Addict301
85

Ich habe diese Ausnahme beim Versuch, eine benutzerdefinierte Methode deleteBy im Spring-Datenrepository zu verwenden. Die Operation wurde aus einer JUnit-Testklasse versucht.

Die Ausnahme tritt nicht auf, wenn die @TransactionalAnnotation auf JUnit-Klassenebene verwendet wird.

Kishore Guruswamy
quelle
8
Ich war in der gleichen Situation, anstatt die Testklasse zu kommentieren, habe ich die Servicemethode mit Anmerkungen versehen, damit sie herunterrutschte, auch wenn es kein Test war.
Paul Nelson Baker
@TransactionalAuf Klassenebene können mögliche Testprobleme maskiert werden, die unterschiedliche Transaktionen in Ihren Diensten manipulieren.
Zon
1
Ich habe @Trasactionaldie Repository-Methode verwendet, da sie der Ort ist, an dem ich tatsächlich mit der Datenbank interagiere, und sie funktioniert einwandfrei.
Harish Kumar Saini
22

Dieser Fehler hatte mich drei Tage lang gefesselt, die Situation, mit der ich konfrontiert war, führte zu demselben Fehler. Nach allen Ratschlägen, die ich finden konnte, spielte ich mit der Konfiguration, aber ohne Erfolg.

Schließlich fand ich es, der Unterschied, dass der Dienst, den ich ausführte, in einem gemeinsamen Glas enthalten war. Es stellte sich heraus, dass AspectJ die Service-Instanziierung nicht gleich behandelte. Tatsächlich rief der Proxy einfach die zugrunde liegende Methode auf, ohne dass die gesamte normale Spring-Magie vor dem Methodenaufruf ausgeführt wurde.

Am Ende löste die @ Service-Annotation, die gemäß dem Beispiel auf dem Dienst platziert wurde, das Problem:

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

}

Die von mir veröffentlichte Methode ist eine Löschmethode, aber die Anmerkungen wirken sich auf alle Persistenzmethoden auf dieselbe Weise aus.

Ich hoffe, dieser Beitrag hilft jemand anderem, der mit dem gleichen Problem zu kämpfen hat, wenn er einen Dienst aus einem Glas lädt

Chris March
quelle
Das Hinzufügen @Scope(proxyMode = ScopedProxyMode.INTERFACES)zur DAO-Klasse, die eine Schnittstelle implementiert, ist wirklich wichtig. Ich habe den ganzen Tag damit verbracht, diesen Fehler herauszufinden, und Ihre Lösung ist die einzige, die funktioniert. Vielen Dank!
Thach Van
1
Eine L'annotation aus Ihrer Antwort hat mich wahrscheinlich um die 3 Tage gerettet. Ich habe gerade die wahre Kraft des Ödems erlebt. Дякс.
Oleksii Kyslytsyn
8

boardRepo.deleteByBoardId (id);

Konnte das gleiche Problem. GOT javax.persistence.TransactionRequiredException: Kein EntityManager mit tatsächlicher Transaktion für aktuellen Thread verfügbar

Ich habe es behoben, indem ich @Transactional Annotation über dem Controller / Service hinzugefügt habe .

Vikram S.
quelle
7

Ich hatte das gleiche Problem und ich fügte tx:annotation-drivenin applicationContext.xmlund es funktionierte.

Stein Feng
quelle
4

Ich hatte den gleichen Fehler beim Zugriff auf eine bereits mit Transaktionen versehene Methode von einer nicht-transaktionsbezogenen Methode innerhalb derselben Komponente:

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

Ich habe den Fehler behoben, indem ich executeQuery () für die selbstreferenzierte Komponente aufgerufen habe:

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
        }
    }
YDZOGODOQ
quelle
3

Das Hinzufügen der org.springframework.transaction.annotation.TransactionalAnmerkung auf Klassenebene für die Testklasse hat das Problem für mich behoben.

Löwe
quelle
3

Nur eine Notiz für andere Benutzer, die nach Antworten auf diesen Fehler suchen. Ein weiteres häufiges Problem ist:

Sie können im Allgemeinen keine @transactionalMethode innerhalb derselben Klasse aufrufen .

(Es gibt Mittel und Wege, AspectJ zu verwenden, aber das Refactoring wird viel einfacher sein.)

Sie benötigen also eine aufrufende Klasse und eine Klasse, die die @transactionalMethoden enthält.

Sparkyspider
quelle
2

Für uns war das Problem auf dieselben Kontexteinstellungen in mehreren Konfigurationsdateien zurückzuführen. Stellen Sie sicher, dass Sie Folgendes nicht in mehreren Konfigurationsdateien dupliziert haben.

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

Ich hatte den gleichen Fehlercode, als ich @Transactionauf einer falschen Methode / Aktionsebene verwendet habe.

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

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

Ich musste das natürlich @Transactionaldirekt über der Methode platzieren methodWithANumberOfDatabaseActions().

Das hat die Fehlermeldung in meinem Fall gelöst.

ein anderer Knoten
quelle
0

Ich habe den Modus aus entfernt

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

um diese Arbeit zu machen

ropo
quelle
0

Ich hatte dieses Problem seit Tagen und nichts, was ich irgendwo online gefunden habe, hat mir geholfen. Ich poste meine Antwort hier, falls es jemand anderem hilft.

In meinem Fall arbeitete ich an einem Microservice, der über Remoting aufgerufen wurde, und meine @ Transactional-Annotation auf Serviceebene wurde vom Remote-Proxy nicht erfasst.

Das Hinzufügen einer Delegatenklasse zwischen der Service- und der Dao-Ebene und das Markieren der Delegatenmethode als Transaktionsmethode haben dies für mich behoben.

fleeblewidget
quelle
0

Das hat uns geholfen, vielleicht kann es in Zukunft anderen helfen. @Transactionarbeitete nicht für uns, aber das tat:

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

JLane
quelle
0

Wenn Sie haben

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

und superklasse

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

und du rufst an

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

Sie erhalten keinen Spring TransactionInterceptor (der Ihnen eine Transaktion gibt).

Folgendes müssen Sie tun:

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

Unglaublich aber wahr.

user2006754
quelle