В методе Spring @Transactional транзакция не начинается

Я столкнулся со странной проблемой при разработке приложения с использованием Spring (3.0.5), Hibernate (3.6.0) и Wicket (1.4.14). Проблема в том, что я не могу сохранить или изменить какой-либо объект в базе данных. Под «не могу» я подразумеваю, что все изменения в объекте или вызовы EntityManager.persist(foo) просто молча игнорируются. Подбирает работу.

Пример простой - на какой-то странице калитки я пытаюсь сохранить объект в базу данных, как показано ниже.

public class ComicDetailsPage extends PublicBasePage {

@Override
protected void onConfigure() {
    System.out.println("In onConfigure");
    super.onConfigure();
    comicDAO.insert("abc");

}

@SpringBean(name="comicDAO")
private ComicDAO comicDAO;

    (....)

Вот комиксДАО

@Service
public class ComicDAO {

@PersistenceContext
private EntityManager em;

(...)

@Transactional
public void insert(String title) {
    Comic c = new Comic();
    c.setTitle(title);
    em.persist(c);
}

@Transactional
public Comic add1toTitle(int pk) {
    System.out.println("Beginning fetching");
    Comic c = em.find(Comic.class, pk);
    System.out.println("Fetched updating");
    c.setTitle(c.getTitle()+"1");
    System.out.println("Updated persisting");
    em.persist(c);
    System.out.println("Persisted returning");
    return c;
}

Я включил ведение журнала, и вот соответствующая часть журналов (и для Hibernate, и для Spring установлено значение TRACE). Я добавил ** к строкам, которые, по моему мнению, здесь важны.

In onConfigure
01:53:19.330 [qtp2119047503-15] DEBUG o.s.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'txManager'
**01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13006687993**
**01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - begin**
01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
01:53:19.335 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - current autocommit status: true
01:53:19.335 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - disabling autocommit
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction begin
01:53:19.336 [qtp2119047503-15] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13006687993
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - setting flush mode to: AUTO
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - setting cache mode to: NORMAL
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.engine.IdentifierValue - id unsaved-value: 0
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.event.def.AbstractSaveEventListener - transient instance of: pl.m4ks.comics.entity.Comic
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.event.def.DefaultPersistEventListener - saving transient instance
**01:53:19.338 [qtp2119047503-15] TRACE org.hibernate.event.def.AbstractSaveEventListener - saving [pl.m4ks.comics.entity.Comic#<null>]**
**01:53:19.341 [qtp2119047503-15] DEBUG org.hibernate.event.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress**
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - closing session
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.jdbc.ConnectionManager - connection already null in cleanup : no action
01:53:19.341 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - commit
**01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - automatically flushing session**
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - before transaction completion
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - before transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - after transaction completion
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - closing session
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.ConnectionManager - performing cleanup
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - after transaction completion

Конечно, ни один объект не сохраняется в базе данных.

Последний файл - мой applicationCONtext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans (...)>

    <context:component-scan base-package="pl.m4ks.comics"/>
    <context:annotation-config /> 

    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:8889/comics" />
        <property name="username" value="root"/>
        <property name="password" value="root" />          
    </bean>

    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="main" />
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
         <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        <property name="packagesToScan">
             <value>pl.m4ks.comics</value>
        </property>
    </bean>


    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <tx:annotation-driven transaction-manager="txManager"  proxy-target-class="true"/>
</beans>

Я понятия не имею, в чем может быть проблема и как ее решить. Я не хочу управлять транзакциями в своем коде — для этого и нужен Spring.


person M4ks    schedule 21.03.2011    source источник
comment
Каков фактический тип дао в классе калитки (система вне .getClass()). А какие конструкторы есть у DAO?   -  person Bozho    schedule 21.03.2011
comment
Удален тег калитки, так как он не имеет отношения к этому вопросу (ваша проблема не в коде калитки). Добавлены теги Java и JPA.   -  person Sean Patrick Floyd    schedule 21.03.2011


Ответы (3)


а) Вы определяете как Hibernate SessionFactory, так и JPA EntitymanagerFactory. Что это будет? Либо используйте Hibernate Session API, либо JPA Entitymanager API с Hibernate в качестве провайдера, но не оба одновременно.

б) Вы определили HibernateTransactionManager, но, поскольку вы используете EntityManager в своем коде, вместо этого вам нужен JpaTransactionManager:

<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="myEmf"/>
</bean

Вот закомментированная версия вашего applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans (...)>

    <context:component-scan base-package="pl.m4ks.comics"/>
    <context:annotation-config /> 

    <bean id="dataSource" 
    class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:8889/comics" />
        <property name="username" value="root"/>
        <property name="password" value="root" />          
    </bean>

    <!-- use either this: -->
    <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="main" />
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- or this -->
    <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
         <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        <property name="packagesToScan">
             <value>pl.m4ks.comics</value>
        </property>
    </bean>
    <!-- (but not both) --> 

    <!-- this is correct for AnnotationSessionFactoryBean, but not if you use
         LocalContainerEntityManagerFactoryBean --> 
    <bean id="txManager" 
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

    <!-- not necessary, <context:annotation-config /> automatically includes this -->
    <bean 
    class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
</beans>

И замечание по дизайну: DAO не должны быть транзакционными. Вы должны использовать сервисный уровень, который управляет транзакциями. См. этот вопрос (и многие другие) для справки.

person Sean Patrick Floyd    schedule 21.03.2011
comment
Очень спасибо за ответ. Я попытался отредактировать applicationCOntext.xml - я только что прокомментировал bean-компонент entityManagerFactory. Теперь я получаю org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 0, так как пытаюсь использовать entityManager в своем DAO. - person M4ks; 21.03.2011
comment
@M4ks M4ks, тогда вы должны закомментировать SessionFactory, а не EntityManagerFactory - person Sean Patrick Floyd; 21.03.2011
comment
Полагаю, что так. Но как я могу выполнить любую операцию с базой данных без entityManager? - person M4ks; 21.03.2011
comment
@ M4ks, ты меня неправильно понял. Избавьтесь от SessionFactory, а не от EnitiyManagerFactory. И добавьте JpaTransactionManager. Все будет хорошо - person Sean Patrick Floyd; 21.03.2011
comment
Работает! Я нашел разницу между Session API и EntityManager. Я останусь со вторым. Большое Вам спасибо! - person M4ks; 21.03.2011
comment
Я провел много часов с подобной проблемой. Спасибо за решение! - person kpater87; 21.07.2016
comment
@ kpater87 для этого и предназначен StackOverflow. Рад, что вы оценили это! - person Sean Patrick Floyd; 21.07.2016
comment
@SeanPatrickFloyd большое спасибо. Я попробовал это с обеими фабриками сеансов, поскольку некоторые старые DAO использовали фабрику гибернации, и она все еще работает. - person Ankush; 26.12.2019

Вы пытались установить hibernate.connection.autocommit=true в конфигурации гибернации? Это решило бы проблему. Но насколько эффективен этот подход, вам предстоит выяснить.

person Ravi    schedule 06.02.2017

вам нужно вызвать следующий метод в EntityManager

flush()

Чтобы сделать фактическое сохранение в базе данных

person blob    schedule 21.03.2011
comment
Это неправильно. Если Spring TransactionManager настроен правильно, entityManager.flush() будет вызываться автоматически. - person Sean Patrick Floyd; 21.03.2011