ฤดูใบไม้ผลิ - ไม่มี EntityManager ที่มีการทำธุรกรรมจริงสำหรับเธรดปัจจุบัน - ไม่สามารถดำเนินการเรียก 'คงอยู่' ได้อย่างน่าเชื่อถือ


137

ฉันได้รับข้อผิดพลาดนี้เมื่อพยายามเรียกใช้วิธี "คงอยู่" เพื่อบันทึกโมเดลเอนทิตีลงในฐานข้อมูลในเว็บแอปพลิเคชัน Spring MVC ของฉัน ไม่พบโพสต์หรือหน้าใด ๆ ในอินเทอร์เน็ตที่สามารถเกี่ยวข้องกับข้อผิดพลาดนี้โดยเฉพาะ ดูเหมือนว่ามีบางอย่างผิดปกติกับ EntityManagerFactory bean แต่ฉันค่อนข้างใหม่ในการเขียนโปรแกรม Spring ดังนั้นสำหรับฉันดูเหมือนว่าทุกอย่างจะเริ่มต้นได้ดีและอ้างอิงจากบทความการสอนต่างๆในเว็บ

มอบหมายงาน-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
ตามที่ระบุไว้ว่าไม่มีข้อผิดพลาดเกิดขึ้น @Transactionคำอธิบายวิธีการลงทะเบียนกับ
Rohit

คำตอบ:


260

ฉันมีปัญหาเดียวกันและฉันอธิบายวิธีการ@Transactionalและมันทำงานได้

อัปเดต: ตรวจสอบเอกสารสปริงที่ดูเหมือนว่าโดยค่าเริ่มต้น PersistenceContext เป็นประเภทธุรกรรมดังนั้นจึงเป็นเหตุผลว่าทำไมจึงต้องทำธุรกรรม ( http://docs.spring.io/spring/docs/current/spring-framework-reference/) html / orm.html ):

คำอธิบายประกอบ @PersistenceContext มีประเภทแอตทริบิวต์ที่เป็นตัวเลือกซึ่งมีค่าเริ่มต้นเป็น PersistenceContextType.TRANSACTION ค่าเริ่มต้นนี้เป็นสิ่งที่คุณต้องได้รับพร็อกซี EntityManager ที่ใช้ร่วมกัน ทางเลือก PersistenceContextType.EXTENDED เป็นเรื่องที่แตกต่างอย่างสิ้นเชิง: ผลลัพธ์ใน EntityManager ที่เรียกว่า Extended ซึ่งไม่ได้เป็นเธรดที่ปลอดภัยจึงไม่ควรใช้ในองค์ประกอบที่เข้าถึงได้อย่างพร้อมกันเช่นถั่วที่มีการจัดการแบบสปริง Extended EntityManagers ควรจะใช้ในส่วนประกอบ stateful ที่ตัวอย่างเช่นอยู่ในเซสชั่นโดยมีวงจรชีวิตของ EntityManager ที่ไม่ได้เชื่อมโยงกับธุรกรรมปัจจุบัน แต่ค่อนข้างจะขึ้นอยู่กับแอปพลิเคชันทั้งหมด


71
หากวิธีใดที่ไม่มี@Transactionalannotion เรียกเมธอดที่มี@Transactionalหมายเหตุประกอบใน classfile เดียวกันคุณก็จะได้รับข้อผิดพลาดนี้ (นั่นคือสิ่งที่ฉันกำลังเผชิญอยู่)
Jacob van Lingen

5
ฉันใส่หมายเหตุประกอบคลาสบริการด้วย@Transactionalซึ่งก็ใช้ได้เช่นกัน ไม่แน่ใจว่ามันเป็นวิธีที่ถูกต้องหรือไม่ แต่ดูเหมือนว่ามันจะทำงานได้ดี ...
milosmns

8
สิ่งหนึ่งที่ควรค่าแก่การกล่าวถึงคือควรใช้คำอธิบายประกอบนี้เป็นวิธีสาธารณะเนื่องจากไม่ได้ผล
Yuriy Kravets

7
โปรดจำไว้ว่า @Transactional ใช้งานได้เฉพาะวิธีการสาธารณะเท่านั้น
Andrei_N

7
FYI ต้องใช้คำอธิบายประกอบ javax.transaction.Transactional (ไม่ใช่ Spring)
java-addict301

85

ฉันได้รับข้อยกเว้นนี้ขณะที่พยายามใช้วิธีการกำหนดเอง deleteBy ในที่เก็บข้อมูลฤดูใบไม้ผลิ มีการพยายามดำเนินการจากคลาสทดสอบ JUnit

ข้อยกเว้นไม่เกิดขึ้นเมื่อใช้การเพิ่มความคิดเห็น@Transactionalที่ระดับ JUnit


8
ฉันอยู่ในสถานการณ์เดียวกันแทนที่จะใส่คำอธิบายประกอบในชั้นเรียนที่ฉันทำหมายเหตุวิธีการบริการดังนั้นมันจึงไหลลงแม้ว่าจะไม่ได้ทำการทดสอบ
พอลเนลสันเบเกอร์

@Transactionalในระดับชั้นสามารถปกปิดปัญหาการทดสอบที่เป็นไปได้ที่จัดการธุรกรรมที่แตกต่างในบริการของคุณ
Zon

1
ฉันใช้@Trasactionalวิธีการเก็บข้อมูลเนื่องจากเป็นสถานที่ที่ฉันโต้ตอบกับฐานข้อมูลและทำงานได้ดี
Harish Kumar Saini

22

ข้อผิดพลาดนี้ทำให้ฉันโกรธเป็นเวลาสามวันสถานการณ์ที่ฉันเผชิญทำให้เกิดข้อผิดพลาดเดียวกัน ทำตามคำแนะนำทั้งหมดที่ฉันหาได้ฉันเล่นกับการตั้งค่า แต่ไม่เกิดประโยชน์

ในที่สุดฉันก็พบว่าความแตกต่างบริการที่ฉันกำลังดำเนินการอยู่ในขวดทั่วไปปัญหากลายเป็น AspectJ ไม่ได้ให้บริการอินสแตนซ์เดียวกัน พร็อกซีก็แค่เรียกวิธีการพื้นฐานโดยไม่ต้องใช้เวทย์มนตร์ Spring ปกติทั้งหมดก่อนการเรียกเมธอด

ในตอนท้ายคำอธิบายประกอบ @Scope วางอยู่บนบริการตามตัวอย่างแก้ไขปัญหา:

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

}

วิธีที่ฉันโพสต์เป็นวิธีการลบ แต่คำอธิบายประกอบมีผลกระทบต่อวิธีการคงอยู่ทั้งหมดในวิธีเดียวกัน

ฉันหวังว่าโพสต์นี้จะช่วยคนอื่นที่ประสบปัญหาเดียวกันเมื่อโหลดบริการจากโถ


การเพิ่ม@Scope(proxyMode = ScopedProxyMode.INTERFACES)คลาส DAO ที่ใช้ส่วนต่อประสานนั้นสำคัญมาก ฉันใช้เวลาทั้งวันเพื่อหาข้อผิดพลาดนี้และทางออกของคุณเป็นเพียงงานเดียว ขอบคุณมาก!
Thach Van

1
การแจ้งเตือนแบบลอตจากคำตอบของคุณเพิ่งช่วยฉันได้ประมาณ 3 วัน ฉันเพิ่งได้สัมผัสกับพลังที่แท้จริงของ edenema Дякс
Oleksii Kyslytsyn

11

ฉันมีข้อผิดพลาดเดียวกันเพราะฉันเปลี่ยนจาก XML- เป็น java-configuration

ประเด็นคือฉันไม่ได้ย้าย<tx:annotation-driven/>แท็กตามที่ Stone Feng แนะนำ

ดังนั้นฉันเพิ่งเพิ่ม@EnableTransactionManagementดังที่แนะนำที่นี่ การตั้งค่าคำอธิบายประกอบขับเคลื่อนธุรกรรมใน Spring ใน @Configuration Classและตอนนี้มันทำงานได้


8

boardRepo.deleteByBoardId (ID);

ประสบปัญหาเดียวกัน GOT javax.persistence.TransactionRequiredException: ไม่มี EntityManager พร้อมธุรกรรมจริงสำหรับเธรดปัจจุบัน

ฉันแก้ไขด้วยการเพิ่มคำอธิบายประกอบ@Transactionalเหนือตัวควบคุม / บริการ



4

ฉันมีข้อผิดพลาดเดียวกันเมื่อเข้าถึงวิธีการทำรายการที่ทำหมายเหตุประกอบไว้แล้วจากวิธีที่ไม่ทำธุรกรรมภายในองค์ประกอบเดียวกัน:

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

ฉันแก้ไขข้อผิดพลาดโดยการเรียกใช้ executeQuery () บนคอมโพเนนต์ที่อ้างอิงตนเอง:

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.Transactionalคำอธิบายประกอบที่ระดับชั้นสำหรับชั้นทดสอบแก้ไขปัญหาให้ฉัน


3

เพียงบันทึกสำหรับผู้ใช้รายอื่นที่ค้นหาคำตอบสำหรับข้อผิดพลาด ปัญหาทั่วไปอีกประการหนึ่งคือ:

โดยทั่วไปคุณไม่สามารถเรียก@transactionalวิธีการจากภายในชั้นเรียนเดียวกัน

(มีวิธีและวิธีการใช้ AspectJ แต่การเปลี่ยนโครงสร้างจะง่ายขึ้น)

ดังนั้นคุณจะต้องมีคลาสการเรียกและคลาสที่มี@transactionalวิธีการ


2

สำหรับเราแล้วปัญหาเกิดจากการตั้งค่าบริบทเดียวกันในหลายไฟล์การกำหนดค่า ตรวจสอบว่าคุณไม่ได้ทำซ้ำสิ่งต่อไปนี้ในไฟล์ปรับแต่งหลายไฟล์

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

2

ฉันมีรหัสข้อผิดพลาดเหมือนกันเมื่อฉันใช้@Transactionวิธี / actionlevel ผิด

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

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

แน่นอนว่าฉันต้องวาง@TransactionalวิธีการmethodWithANumberOfDatabaseActions()ดังกล่าว

ที่แก้ไขข้อผิดพลาดในกรณีของฉัน


0

ฉันลบโหมดจาก

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

เพื่อให้งานนี้


0

ฉันมีปัญหานี้มาหลายวันแล้วและไม่พบสิ่งใดที่ออนไลน์ช่วยฉันฉันโพสต์คำตอบของฉันที่นี่ในกรณีที่ช่วยคนอื่น

ในกรณีของฉันฉันกำลังทำงานกับ microservice ที่ถูกเรียกผ่าน remoting และคำอธิบายประกอบ @Transactional ของฉันที่ระดับบริการไม่ได้รับพรจากพร็อกซีระยะไกล

การเพิ่มคลาสของผู้รับมอบสิทธิ์ระหว่างบริการและเลเยอร์ dao และทำเครื่องหมายวิธีผู้แทนเป็นธุรกรรมเพื่อแก้ไขปัญหานี้สำหรับฉัน


0

สิ่งนี้ช่วยเราบางทีมันอาจช่วยคนอื่นได้ในอนาคต @Transactionไม่ได้ทำงานสำหรับเรา แต่สิ่งนี้ทำ:

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


0

ถ้าคุณมี

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

และซุปเปอร์คลาส

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

และคุณโทร

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

คุณจะไม่ได้รับ Spring TransactionInterceptor (ที่ให้การทำธุรกรรมกับคุณ)

นี่คือสิ่งที่คุณต้องทำ:

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

ไม่น่าเชื่อ แต่จริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.