Весна - немає EntityManager з фактичною транзакцією, доступною для поточного потоку - не може надійно обробити виклик "зберегти"


137

Я отримую цю помилку, коли намагаюся викликати метод "зберегти", щоб зберегти модель об'єкта до бази даних у своєму веб-додатку Spring MVC. Не вдається знайти жодну публікацію чи сторінку в Інтернеті, яка може стосуватися саме цієї помилки. Здається, щось не в порядку з бітом EntityManagerFactory, але я досить новачок у весняному програмуванні, тому для мене здається, що все ініціалізовано добре і згідно з різними статтями підручника в Інтернеті.

диспетчер-сервлет.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.
Рохіт

Відповіді:


260

У мене була така ж проблема, і я помітив метод, як @Transactionalі він працював.

ОНОВЛЕННЯ: перевірка весняної документації за замовчуванням виглядає як PersistenceContext типу Transaction, тому метод повинен бути транзакційним ( http://docs.spring.io/spring/docs/current/spring-framework-reference/ html / orm.html ):

Анотація @PersistenceContext має необов'язковий тип атрибута, який за замовчуванням застосовується до PersistenceContextType.TRANSACTION. Цей замовчуванням є тим, що вам потрібно отримати спільний проксі EntityManager. Альтернатива, PersistenceContextType.EXTENDED, - зовсім інша справа: це призводить до так званого розширеного EntityManager, який не є безпечним для потоків, і, отже, не повинен використовуватися в одночасно доступному компоненті, такому як весняний керуючий сингл. Розширені EntityManagers повинні використовуватися лише у складових компонентів, які, наприклад, перебувають у сеансі, при цьому життєвий цикл EntityManager не прив'язаний до поточної транзакції, а повністю залежить від програми.


71
Якщо метод без @Transactionalпримітки викликає метод з @Transactionalанотацією в одному і тому ж класі файлу, ви також будете вражені цією помилкою (з цим я зіткнувся).
Якоб ван Лінген

5
Я помітив клас обслуговування @Transactional, що також працює. Не впевнений, чи це правильний шлях, але здається, що він працює добре ...
milosmns

8
Ще одна річ, яку варто згадати, - це те, що це примітка має використовуватися для публічного методу, оскільки воно не працює інакше.
Юрій Кравець

7
Пам'ятайте, будь ласка, що @Transactional працює лише на публічних методах.
Андрій_N

7
FYI для цього потрібна примітка javax.transaction.Transactional (не весняна).
java-addict301

85

Я отримав це виняток під час спроби використання спеціального методу deleteBy у сховищі даних весни. Операцію було здійснено з тестового класу JUnit.

Виняток не відбувається при використанні @Transactionalанотації на рівні класу JUnit.


8
Я опинився в тій же ситуації, замість того, щоб коментувати тестовий клас, я коментував сервісний метод, щоб він вийшов з ладу, навіть якщо це не тест.
Пол Нельсон Бейкер

@Transactionalна рівні класу може замаскувати можливі тестові проблеми, що маніпулюють різними транзакціями у ваших послугах.
Зон

1
Я використовував @Trasactionalметод репозиторію з його місця, де я фактично взаємодію з базою даних та її нормальною роботою.
Харіш Кумар Саїні

22

Ця помилка змусила мене помилуватися протягом трьох днів, ситуація, з якою я зіткнулася, призвела до тієї ж помилки. Дотримуючись всіх порад, які я міг знайти, я грав з конфігурацією, але безрезультатно.

Врешті-решт я виявив, що різниця. Служба, яку я виконував, містилася в загальній банці; проблема виявилася в тому, що AspectJ не трактує ідентифікацію служби однаково. Насправді проксі просто викликав основний метод, не виконуючи всі звичайні весняні магії перед викликом методу.

Зрештою, анотація @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, який реалізує інтерфейс, є дуже важливим. Я витратив цілий день, щоб з'ясувати цю помилку, і ваше рішення є єдиним. Велике спасибі!
Тхач Ван

1
Одне L'annotation з вашої відповіді щойно врятувало мене, ймовірно, приблизно за 3 дні. Я щойно пережив справжню силу еденеми. Дякс.
Олексій

11

У мене була така ж помилка, оскільки я перейшов з XML- у java-конфігурацію.

Справа в тому, що я не мігрував <tx:annotation-driven/>тег, як запропонував Стоун Фенг.

Тому я щойно додав, @EnableTransactionManagementяк тут запропоновано Налаштування транзакцій, керованих анотацією , навесні в класі @Configuration , і він працює зараз


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неправильний метод / level level.

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

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

Мені , звичайно, довелося розмістити @Transactionalтрохи вище методу methodWithANumberOfDatabaseActions().

Це вирішило повідомлення про помилку в моєму випадку.


0

Я видалив режим із

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

щоб зробити цю роботу


0

У мене ця проблема була цілими днями, і нічого, що я знайшов десь в Інтернеті, не допомогло мені, я розміщую тут свою відповідь, якщо вона допомагає комусь іншому.

У моєму випадку я працював над мікросервісом, який викликався за допомогою видалення, і мою анотацію @Transactional на рівні обслуговування віддалений проксі не брав.

Додавання класу делегата між шарами служби та дао та маркування методу делегата як транзакційного зафіксовано для мене.


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

Неймовірно, але правда.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.