Mùa xuân - Không có EntityManager với giao dịch thực tế có sẵn cho chuỗi hiện tại - không thể xử lý cuộc gọi 'kiên trì' một cách đáng tin cậy


136

Tôi gặp lỗi này khi cố gắng gọi phương thức "kiên trì" để lưu mô hình thực thể vào cơ sở dữ liệu trong ứng dụng web Spring MVC của tôi. Thực sự không thể tìm thấy bất kỳ bài đăng hoặc trang nào trên internet có thể liên quan đến lỗi cụ thể này. Có vẻ như có gì đó không ổn với EntityManagerFactory bean nhưng tôi khá mới đối với lập trình Spring nên đối với tôi có vẻ như mọi thứ đều được khởi tạo tốt và theo các bài viết hướng dẫn khác nhau trên web.

bộ điều phối-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>

Đăng kýControll.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
Như lỗi nói, không có giao dịch. Chú thích phương thức đăng ký với @Transaction.
Rohit

Câu trả lời:


260

Tôi đã có cùng một vấn đề và tôi đã chú thích phương pháp @Transactionalvà nó đã hoạt động.

CẬP NHẬT: kiểm tra tài liệu mùa xuân theo mặc định, PersistenceContext thuộc loại Giao dịch, vì vậy đó là lý do tại sao phương thức này phải là giao dịch ( http://docs.spring.io/spring/docs/cản/spring-framework-reference/ html / orm.html ):

Chú thích @PersistenceContext có một loại thuộc tính tùy chọn, mặc định là PersistenceContextType.TRANSACTION. Mặc định này là những gì bạn cần để nhận proxy EntityManager được chia sẻ. Sự thay thế, PersistenceContextType.EXTENDED, là một vấn đề hoàn toàn khác: Điều này dẫn đến cái gọi là EntityManager mở rộng, không an toàn cho luồng và do đó không được sử dụng trong một thành phần được truy cập đồng thời như một bean singleton được quản lý mùa xuân. EntityManager mở rộng chỉ được sử dụng trong các thành phần trạng thái, ví dụ, nằm trong một phiên, với vòng đời của EntityManager không gắn liền với giao dịch hiện tại mà hoàn toàn phụ thuộc vào ứng dụng.


71
Nếu một phương thức không có @Transactionalannotion gọi một phương thức có @Transactionalchú thích trong cùng một tập tin lớp, bạn cũng sẽ bị lỗi này (đó là điều tôi đang phải đối mặt).
Jacob van Lingen

5
Tôi chú thích lớp dịch vụ với @Transactional, điều đó cũng hoạt động. Không chắc chắn liệu đó có phải là con đường đúng hay không nhưng có vẻ như nó hoạt động tốt ...
milosmns

8
Một điều đáng nói nữa là chú thích này nên được sử dụng cho phương thức công khai vì nó không hoạt động khác.
Yuriy Kravets

7
Xin nhớ, xin vui lòng, @Transactional chỉ hoạt động trên các phương thức công khai.
Andrei_N

7
FYI này cần chú thích javax.transaction.Transactional (không phải là Spring).
java-nghiện602

85

Tôi đã có ngoại lệ này trong khi cố gắng sử dụng phương thức tùy chỉnh xóaBy trong kho lưu trữ dữ liệu mùa xuân. Các hoạt động đã được cố gắng từ một lớp kiểm tra JUnit.

Ngoại lệ không xảy ra khi sử dụng @Transactionalchú thích ở cấp lớp JUnit.


8
Tôi đã ở trong tình huống tương tự, thay vì chú thích lớp thử nghiệm, tôi đã chú thích phương thức dịch vụ để nó bị lừa ngay cả khi nó không phải là thử nghiệm.
Paul Nelson Baker

@Transactionalở cấp độ lớp có thể che dấu các sự cố kiểm tra có thể xảy ra khi thao túng các giao dịch khác nhau trong các dịch vụ của bạn.
Zon

1
Tôi đã sử dụng @Trasactionalphương thức kho lưu trữ vì đây là nơi tôi thực sự tương tác với cơ sở dữ liệu và nó hoạt động tốt.
Harish Kumar Saini

22

Lỗi này đã khiến tôi bị cáo trong ba ngày, tình huống tôi gặp phải cũng gây ra lỗi tương tự. Theo tất cả các lời khuyên tôi có thể tìm thấy, tôi đã chơi với cấu hình nhưng không có kết quả.

Cuối cùng, tôi đã tìm thấy nó, sự khác biệt, Dịch vụ tôi đang thực hiện được chứa trong một bình chung, vấn đề hóa ra là AspectJ không xử lý việc khởi tạo Dịch vụ giống nhau. Trong thực tế, proxy chỉ đơn giản gọi phương thức cơ bản mà không thực hiện tất cả các phép thuật Spring thông thường trước khi gọi phương thức.

Cuối cùng, chú thích @Scope được đặt trên dịch vụ theo ví dụ đã giải quyết vấn đề:

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

}

Phương pháp tôi đã đăng là một phương thức xóa nhưng các chú thích ảnh hưởng đến tất cả các phương thức kiên trì theo cùng một cách.

Tôi hy vọng bài đăng này sẽ giúp người khác đã đấu tranh với vấn đề tương tự khi tải dịch vụ từ một cái bình


Thêm @Scope(proxyMode = ScopedProxyMode.INTERFACES)vào lớp DAO thực hiện giao diện là rất quan trọng. Tôi đã dành cả ngày để tìm ra lỗi này và giải pháp của bạn là cách duy nhất hiệu quả. Cảm ơn rât nhiều!
Thạch Văn

1
Một câu trả lời từ câu trả lời của bạn vừa giúp tôi tiết kiệm khoảng 3 ngày. Tôi vừa trải nghiệm sức mạnh thực sự của chứng phù nề. Trả lời
Oleksii Kyslytsyn


8

boardRepo.deleteByBoardId (id);

Đối mặt với cùng một vấn đề. GOT javax.persistence.TransactionRequiredException: Không có EntityManager với giao dịch thực tế có sẵn cho chuỗi hiện tại

Tôi đã giải quyết nó bằng cách thêm chú thích @Transactional phía trên bộ điều khiển / dịch vụ.


7

Tôi đã có cùng một vấn đề và tôi đã thêm tx:annotation-drivenvào applicationContext.xmlvà nó đã làm việc.


4

Tôi đã gặp lỗi tương tự khi truy cập một phương thức đã được chú thích giao dịch từ một phương thức không giao dịch trong cùng một thành phần:

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

Tôi đã sửa lỗi bằng cách gọi execQuery () trên thành phần tự tham chiếu:

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

Việc thêm org.springframework.transaction.annotation.Transactionalchú thích ở cấp lớp cho lớp kiểm tra đã khắc phục sự cố cho tôi.


3

Chỉ cần một lưu ý cho người dùng khác đang tìm kiếm câu trả lời cho lỗi này. Một vấn đề phổ biến khác là:

Bạn thường không thể gọi một @transactionalphương thức từ trong cùng một lớp.

(Có nhiều cách và phương tiện sử dụng AspectJ nhưng tái cấu trúc sẽ dễ dàng hơn)

Vì vậy, bạn sẽ cần một lớp gọi và lớp giữ các @transactionalphương thức.


2

Đối với chúng tôi, sự cố xảy ra với các cài đặt ngữ cảnh giống nhau trong nhiều tệp cấu hình. Kiểm tra xem bạn đã không sao chép các mục sau trong nhiều tệp cấu hình.

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

2

Tôi đã có cùng một mã lỗi khi tôi sử dụng @Transactionsai phương thức / actionlevel.

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

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

Tôi đã phải đặt @Transactionalngay phía trên phương pháp methodWithANumberOfDatabaseActions(), tất nhiên.

Điều đó đã giải quyết thông báo lỗi trong trường hợp của tôi.


0

Tôi đã xóa chế độ khỏi

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

để làm việc này


0

Tôi đã có vấn đề này trong nhiều ngày và không có gì tôi tìm thấy ở bất cứ đâu trực tuyến đã giúp tôi, tôi đang đăng câu trả lời của mình ở đây trong trường hợp nó giúp được bất cứ ai khác.

Trong trường hợp của tôi, tôi đang làm việc trên một dịch vụ siêu nhỏ được gọi thông qua điều khiển từ xa và chú thích @Transactional của tôi ở cấp độ dịch vụ không được chọn bởi proxy từ xa.

Thêm một lớp đại biểu giữa các lớp dịch vụ và dao và đánh dấu phương thức đại biểu là giao dịch đã cố định điều này cho tôi.


0

Điều này đã giúp chúng tôi, có thể nó có thể giúp đỡ những người khác trong tương lai. @Transactionđã không làm việc cho chúng tôi, nhưng điều này đã làm:

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


0

Nếu bạn có

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

và siêu hạng

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

và bạn gọi

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

bạn sẽ không nhận được Spring TransactionInterceptor (cung cấp cho bạn một giao dịch).

Đây là những gì bạn cần làm:

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

Không thể tin được nhưng sự thật.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.