Spring 只读事务将数据提交到数据库

4
我正在尝试实现一个服务方法的“只读”事务,遵循Spring Framework Reference的11.5.2项,但事务仍自动将数据提交到数据库。我使用的是Spring 3.1.0.RELEASE、Hibernate 3.5.5-Final和Oracle 11g Express Edition Release 11.2.0.2.0。以下是我的设置:
关于建议、切入点、顾问和事务管理器的XML:
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" />
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="myServiceMethods" expression="execution(* my.example.service.*.*(..))" />
    <aop:advisor pointcut-ref="myServiceMethods" advice-ref="transactionAdvice" />
</aop:config>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
    <!-- the sessionFactory bean declaration does not set the -->
    <!-- hibernate.connection.autocommit property as either true or false -->
</bean>

服务接口:

package my.example.service;

public interface MyService {

    void getFoo();

    void bar();
}

服务实现:

package my.example.service.impl;

import my.example.dao.MyDao;
import my.example.domain.MyEntity;
import my.example.service.MyService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyServiceImpl implements MyService {

    @Autowired
    private MyDao dao;

    public void getFoo() {
        MyEntity example = this.dao.getMyEntity(1L);
        example.setSomeInteger(2);
        example.setSomeString("three");
    }

    public void bar() {
        MyEntity example = this.dao.getMyEntity(4L);
        example.setSomeInteger(5);
        example.setSomeString("six");
    }
}

调用 getFoo()bar() 后,数据库将被更新,即使 getFoo() 被标记为只读。但如果我更改这两行代码:
<tx:method name="get*" read-only="true" />
<tx:method name="*"/>

到:

<tx:method name="*" read-only="true" />

这两种方法都尊重read-only属性,并且数据不会提交到数据库。

发生了什么?我做错了什么?我错过了什么?

2个回答

4
我发现为什么“只读”事务会被覆盖。在web.xml中声明了OpenSessionInViewFilter,之前的开发人员使用它来防止在控制器/接口中加载用户及其角色时出现LazyInitializationException
以下是过滤器声明:
<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>

我通过配置过滤器来解决了这个问题,使每个事务都可以使用自己的会话(称为“延迟模式”)。
以下是启用“延迟模式”的过滤器声明:
<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>singleSession</param-name>
        <param-value>false</param-value>
    </init-param>
</filter>

请在过滤器的Javadoc中阅读注意事项。我的DAO类扩展了org.springframework.orm.hibernate3.support.HibernateDaoSupport,继承方法getSession()getSessionFactory().getCurrentSession()返回的Hibernate会话其刷新模式设置为FlushMode.MANUAL,而不是我在阅读this SO answer后期望的FlushMode.NEVER

0

为什么不在@Transactional注解上使用readOnly标志呢?

在你的方法上注释@Transactional(readOnly=true),Hibernate将尝试以只读方式执行事务,并且(我认为,你可能需要再次检查)如果尝试写入,则会抛出异常。

在这方面,试图重新发明轮子是没有意义的。


实际应用程序有39个服务类,每个类大约有20个公共方法,这就是为什么之前的开发人员选择了XML方法。现在,如果我在XML上启用<tx:annoation-driven/>并将@Transactional(readOnly = true)添加到单个方法中,则会被<tx:method name="*"/>忽略/覆盖。数据仍然被提交。 - boobsbr
完全从XML中删除事务通知、切入点和顾问,添加<tx:annotation-driven/>,并在单个方法上注释@Transactional@Transactional(readOnly=true)可以工作,如果只读方法内部没有调用SessionFactory.getCurrentSession().saveOrUpdate(myEntityInstance)。如果在只读方法内部调用saveOrUpdate(),不会抛出任何异常。 - boobsbr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接