Spring - Geçerli iş parçacığı için fiili işlem mevcut olan EntityManager yok - 'kalıcı' çağrıyı güvenilir bir şekilde işleyemez


137

Spring MVC web uygulamamda varlık modelini veritabanına kaydetmek için "kalıcı" yöntemini çağırmaya çalışırken bu hatayı alıyorum. İnternette bu belirli hatayla ilgili olabilecek herhangi bir gönderi veya sayfa bulamıyorum. EntityManagerFactory bean ile ilgili bir sorun var gibi görünüyor, ancak Spring programlamasında oldukça yeniyim, bu yüzden benim için her şey iyi ve web'deki çeşitli eğitim makalelerine göre başlatılmış gibi görünüyor.

hareket memuru-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
Hatanın dediği gibi, işlem yok. Kayıt yöntemine ile açıklama ekleyin @Transaction.
Rohit

Yanıtlar:


263

Ben de aynı sorunu yaşadım ve yöntemi @Transactionalaçıkladım ve işe yaradı.

GÜNCELLEME: Yay dokümantasyonuna bakıldığında, PersistenceContext varsayılan olarak Transaction türündedir, bu nedenle yöntemin işlemsel olması gerekir ( http://docs.spring.io/spring/docs/current/spring-framework-reference/ html / orm.html ):

@PersistenceContext ek açıklamasının, varsayılan olarak PersistenceContextType.TRANSACTION olan isteğe bağlı bir öznitelik türü vardır. Bu varsayılan, paylaşılan bir EntityManager proxy'si almak için ihtiyacınız olan şeydir. Alternatif, PersistenceContextType.EXTENDED, tamamen farklı bir meseledir: Bu, genişletilmiş bir EntityManager ile sonuçlanır; bu, iş parçacığı için güvenli değildir ve bu nedenle, Yay ile yönetilen tekil fasulye gibi eşzamanlı olarak erişilen bir bileşende kullanılmamalıdır. Genişletilmiş EntityManager'ların yalnızca, örneğin EntityManager'ın yaşam döngüsü geçerli bir işleme bağlı değil, tamamen uygulamaya bağlı olarak bir oturumda bulunan durum bilgisi olan bileşenlerde kullanılması beklenir.


71
Olmadan bir yöntemdir Eğer @Transactionalannotion bir yöntemini çağıran @Transactionalaynı classfile ek açıklamanın, ayrıca (I dönük ne olduğunu) bu hatanın saracaktır.
Jacob van Lingen

5
Hizmet sınıfına not ekledim @Transactional, bu da çalışıyor. Doğru yol olup olmadığından emin değilim ama iyi çalışıyor gibi görünüyor ...
milosmns

8
Bahsetmeye değer başka bir şey de, bu ek açıklamanın başka türlü çalışmadığı için genel yöntem için kullanılması gerektiğidir.
Yuriy Kravets

7
Unutmayın, lütfen @Transactional yalnızca genel yöntemler üzerinde çalışır.
Andrei_N

7
Bilginize, bunun javax.transaction.Transactional ek açıklamasına ihtiyacı var (İlkbahar değil).
java-addict301

85

Bahar veri havuzunda deleteBy özel bir yöntem kullanmaya çalışırken bu istisnayı yaşadım. İşlem bir JUnit test sınıfından denendi.

@TransactionalJUnit sınıfı düzeyinde ek açıklama kullanıldığında istisna oluşmaz .


8
Ben de aynı durumdaydım, test sınıfına açıklama eklemek yerine, hizmet yöntemine açıklama ekledim, böylece bir test olmasa bile aşağıya indi.
Paul Nelson Baker

@Transactionalsınıf düzeyinde, hizmetlerinizdeki farklı işlemleri yönlendiren olası test sorunlarını maskeleyebilir.
Zon

1
@TrasactionalVeritabanıyla gerçekten etkileşimde bulunduğum ve iyi çalıştığı yerden beri depo yöntemini kullandım .
Harish Kumar Saini

22

Bu hata beni üç gün kandırdı, karşılaştığım durum da aynı hatayı yarattı. Bulabildiğim tüm tavsiyelere uyarak, konfigürasyonla oynadım ama boşuna.

Sonunda buldum, fark, yürüttüğüm Hizmet ortak bir kavanozun içindeydi, sorun AspectJ'nin Hizmet örneğini aynı şekilde ele almadığı ortaya çıktı. Aslında, proxy, yöntem çağrısından önce tüm normal Spring büyüsünün yürütülmesine gerek kalmadan temeldeki yöntemi çağırıyordu.

Sonunda, örneğe göre hizmete yerleştirilen @Scope ek açıklaması sorunu çözdü:

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

}

Gönderdiğim yöntem bir silme yöntemidir, ancak ek açıklamalar tüm kalıcılık yöntemlerini aynı şekilde etkiler.

Umarım bu gönderi, bir kavanozdan bir hizmet yüklerken aynı sorunla mücadele eden başka birine yardımcı olur


@Scope(proxyMode = ScopedProxyMode.INTERFACES)Bir arayüz uygulayan DAO sınıfına eklemek gerçekten önemlidir. Bütün günü bu hatayı bulmak için harcadım ve sizin çözümünüz işe yarayan tek çözüm. Çok teşekkür ederim!
Thach Van

1
Cevabınızdan bir L'annotation, beni muhtemelen yaklaşık 3 gün kurtardı. Edenema'nın gerçek gücünü henüz deneyimledim. Дякс.
Oleksii Kyslytsyn


8

boardRepo.deleteByBoardId (kimlik);

Aynı sorunla karşılaştı. GOT javax.persistence.TransactionRequiredException: Geçerli iş parçacığı için gerçek işlem bulunan EntityManager yok

Denetleyicinin / hizmetin üzerine @Transactional ek açıklama ekleyerek çözdüm .


7

Ben aynı sorunu vardı ve katma tx:annotation-driveniçinde applicationContext.xmlve işe yaradı.


4

Aynı bileşen içindeki işlemsel olmayan bir yöntemden zaten işlemsel açıklamalı bir yönteme erişirken aynı hatayı aldım:

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

Kendinden referans alınan bileşende executeQuery () 'yi çağırarak hatayı düzelttim:

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

org.springframework.transaction.annotation.TransactionalTest sınıfı için sınıf düzeyinde ek açıklama eklemek sorunu benim için çözdü.


3

Bu hataya cevap arayan diğer kullanıcılar için sadece bir not . Diğer bir yaygın sorun şudur:

Genellikle @transactionalaynı sınıf içinden bir yöntemi çağıramazsınız .

(AspectJ'yi kullanmanın yolları ve araçları vardır, ancak yeniden düzenleme çok daha kolay olacaktır)

Bu nedenle, @transactionalyöntemleri tutan çağıran bir sınıfa ve sınıfa ihtiyacınız olacak .


2

Bizim için sorun, birden çok yapılandırma dosyasında aynı bağlam ayarlarına geldi. Aşağıdakileri birden çok yapılandırma dosyasında çoğaltmadığınızı kontrol edin.

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

2

@TransactionYanlış bir yöntem / eylem düzeyinde kullandığımda aynı hata kodunu aldım .

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

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

Tabii ki @Transactionalyöntemin hemen üstüne yerleştirmek zorunda kaldım methodWithANumberOfDatabaseActions().

Bu benim durumumdaki hata mesajını çözdü.


0

Modu şuradan kaldırdım:

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

bunu çalıştırmak için


0

Bu sorunu günlerce yaşadım ve çevrimiçi herhangi bir yerde bulduğum hiçbir şey bana yardımcı olmadı, cevabımı buraya gönderiyorum, başka birine yardımcı olması durumunda.

Benim durumumda, uzaktan erişim yoluyla çağrılan bir mikro hizmet üzerinde çalışıyordum ve hizmet düzeyindeki @Transactional ek açıklamam uzak proxy tarafından alınmıyordu.

Hizmet ve dao katmanları arasına bir temsilci sınıfı eklemek ve temsilci yöntemini işlem olarak işaretlemek bunu benim için düzeltti.


0

Bu bize yardımcı oldu, belki gelecekte başkalarına da yardımcı olabilir. @Transactionbizim için çalışmıyordu, ama bu işe yaradı:

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


0

Eğer varsa

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

ve süper sınıf

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

ve sen ara

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

Spring TransactionInterceptor alamazsınız (bu size bir işlem sağlar).

Yapman gereken bu:

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

İnanılmaz ama gerçek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.