Wiosna - brak EntityManagera z aktualną transakcją dostępną dla bieżącego wątku - nie może niezawodnie przetworzyć wywołania „trwałego”

155

Ten błąd pojawia się podczas próby wywołania metody „persist” w celu zapisania modelu jednostki do bazy danych w mojej aplikacji internetowej Spring MVC. Nie mogę znaleźć żadnego posta lub strony w Internecie, które mogą odnosić się do tego konkretnego błędu. Wygląda na to, że coś jest nie tak z fasolą EntityManagerFactory, ale jestem całkiem nowy w programowaniu Spring, więc wydaje mi się, że wszystko jest dobrze zainicjowane i zgodnie z różnymi artykułami samouczków w sieci.

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
źródło
1
Jak mówi błąd, nie ma transakcji. Opisz metodę rejestracji za pomocą @Transaction.
Rohit

Odpowiedzi:

292

Miałem ten sam problem i oznaczyłem metodę jako @Transactionali zadziałała.

AKTUALIZACJA: sprawdzanie dokumentacji wiosennej wygląda na to, że domyślnie PersistenceContext jest typu Transaction, dlatego metoda musi być transakcyjna ( http://docs.spring.io/spring/docs/current/spring-framework-reference/ html / orm.html ):

Adnotacja @PersistenceContext ma opcjonalny typ atrybutu, który domyślnie to PersistenceContextType.TRANSACTION. To ustawienie domyślne jest tym, czego potrzebujesz, aby otrzymać udostępniony serwer proxy EntityManager. Alternatywa, PersistenceContextType.EXTENDED, to zupełnie inna sprawa: skutkuje to tak zwanym rozszerzonym EntityManager, który nie jest bezpieczny dla wątków i dlatego nie może być używany w komponencie współbieżnie dostępnym, takim jak zarządzana przez Spring pojedyncza fasola. Rozszerzone EntityManagers powinny być używane tylko w komponentach stanowych, które na przykład znajdują się w sesji, a cykl życia EntityManager nie jest powiązany z bieżącą transakcją, ale raczej całkowicie zależy od aplikacji.

mlg
źródło
79
Jeśli metoda bez @Transactionaladnotacji wywoła metodę z @Transactionaladnotacją w tym samym pliku klasy, również zostaniesz dotknięty tym błędem (właśnie to miałem do czynienia).
Jacob van Lingen
5
Dodałem do klasy usługi adnotację @Transactional, która również działa. Nie jestem pewien, czy to właściwa droga, ale wygląda na to, że działa dobrze ...
milosmns
8
Inną rzeczą wartą wspomnienia jest to, że ta adnotacja powinna być używana dla metody publicznej, ponieważ w przeciwnym razie nie działa.
Yuriy Kravets
8
Pamiętaj proszę, że @Transactional działa tylko na metodach publicznych.
Andrei_N
8
FYI to wymaga adnotacji javax.transaction.Transactional (nie Spring).
java-addict301
87

Otrzymałem ten wyjątek podczas próby użycia niestandardowej metody deleteBy w repozytorium danych wiosny. Podjęto próbę wykonania operacji z klasy testowej JUnit.

Wyjątek nie występuje w przypadku korzystania z @Transactionaladnotacji na poziomie klasy JUnit.

Kishore Guruswamy
źródło
9
Byłem w tej samej sytuacji, zamiast dodawać adnotacje do klasy testowej, dodałem adnotację do metody usługi, więc spłynęła, nawet jeśli nie był to test.
Paul Nelson Baker
@Transactionalna poziomie klasy może maskować możliwe problemy testowe, które manipulują różnymi transakcjami w usługach.
Strefa
2
Użyłem @Trasactionalmetody repozytorium, ponieważ jest to miejsce, w którym faktycznie wchodzę w interakcję z bazą danych i działa dobrze.
Harish Kumar Saini
22

Ten błąd kazał mi leczyć przez trzy dni, sytuacja, w której się znalazłem, spowodowała ten sam błąd. Postępując zgodnie ze wszystkimi radami, które znalazłem, bawiłem się konfiguracją, ale bezskutecznie.

W końcu znalazłem to, różnica, wykonywana usługa była zawarta w zwykłym jar, problem okazał się być taki, że AspectJ nie traktuje tak samo instancji usługi. W efekcie proxy po prostu wywoływało metodę bazową bez wykonywania całej normalnej magii Springa przed wywołaniem metody.

W końcu adnotacja @Scope umieszczona na serwisie jak na przykładzie rozwiązała 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();
    }

}

Opublikowana przeze mnie metoda to metoda usuwania, ale adnotacje wpływają na wszystkie metody utrwalania w ten sam sposób.

Mam nadzieję, że ten post pomoże komuś innemu, kto zmagał się z tym samym problemem podczas ładowania usługi ze słoika

Chris March
źródło
Dodanie @Scope(proxyMode = ScopedProxyMode.INTERFACES)do klasy DAO implementującej interfejs jest naprawdę ważne. Spędziłem cały dzień, aby rozwiązać ten błąd, a twoje rozwiązanie jako jedyne działa. Dziękuję Ci bardzo!
Thach Van
1
Jedna notatka z Twojej odpowiedzi uratowała mi prawdopodobnie około 3 dni. Właśnie doświadczyłem prawdziwej mocy obrzęku. Дякс.
Oleksii Kyslytsyn
9

boardRepo.deleteByBoardId (id);

W obliczu tego samego problemu. GOT javax.persistence.TransactionRequiredException: brak EntityManager z dostępną rzeczywistą transakcją dla bieżącego wątku

Rozwiązałem to dodając adnotację @Transactional nad kontrolerem / usługą.

Vikram S
źródło
7

Miałem ten sam problem i dodałem tx:annotation-drivenw applicationContext.xmli to działało.

Stone Feng
źródło
4

Wystąpił ten sam błąd podczas uzyskiwania dostępu do metody już transakcyjnej z adnotacjami z metody nietransakcyjnej w tym samym komponencie:

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

Naprawiłem błąd, wywołując metodę executeQuery () na komponencie, do którego się odwołujemy:

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
źródło
4

Dodanie org.springframework.transaction.annotation.Transactionaladnotacji na poziomie klasy do klasy testowej rozwiązało problem.

Lew
źródło
3

Tylko uwaga dla innych użytkowników szukających odpowiedzi na ten błąd. Innym częstym problemem jest:

Zwykle nie można wywołać @transactionalmetody z tej samej klasy.

(Istnieją sposoby i sposoby korzystania z AspectJ, ale refaktoryzacja będzie znacznie łatwiejsza)

Potrzebujesz więc klasy wywołującej i klasy, która przechowuje @transactionalmetody.

sparkyspider
źródło
2

Dla nas problem sprowadzał się do tych samych ustawień kontekstu w wielu plikach konfiguracyjnych. Sprawdź, czy nie zduplikowano następujących elementów w wielu plikach konfiguracyjnych.

<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
Nick West
źródło
2

Miałem ten sam kod błędu, gdy użyłem @Transactionniewłaściwej metody / poziomu działania.

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

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

Oczywiście musiałem umieścić to @Transactionaltuż nad metodą methodWithANumberOfDatabaseActions().

To rozwiązało komunikat o błędzie w moim przypadku.

inny węzeł
źródło
1

Jeśli masz

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

i superklasy

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

i dzwonisz

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

nie otrzymasz Spring TransactionInterceptor (który daje Ci transakcję).

Oto, co musisz zrobić:

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

Niewiarygodne, ale prawdziwe.

user2006754
źródło
0

Usunąłem tryb z

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

aby to działało

ropo
źródło
0

Miałem ten problem od kilku dni i nic, co znalazłem w Internecie, nie pomogło mi, zamieszczam tutaj swoją odpowiedź na wypadek, gdyby pomogła komukolwiek innemu.

W moim przypadku pracowałem nad mikrousługą wywoływaną za pośrednictwem komunikacji zdalnej, a moja adnotacja @Transactional na poziomie usługi nie była odbierana przez zdalny serwer proxy.

Dodanie klasy delegata między warstwami usługi i dao i oznaczenie metody delegata jako transakcyjnej rozwiązało ten problem.

fleeblewidget
źródło
0

To nam pomogło, może pomoże innym w przyszłości. @Transactionnie działał dla nas, ale to:

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

JLane
źródło