【问题标题】:Spring MVC JDBC DataSourceTransactionManager: Data committed even after readonly=trueSpring MVC JDBC DataSourceTransactionManager:即使在 readonly=true 之后也提交了数据
【发布时间】:2012-05-07 05:21:54
【问题描述】:

我目前正在开发一个 Spring MVC 应用程序。我已经配置了一个 JDBC TransactionManager,并且我正在使用 AOP XML 进行声明性事务管理。但是,即使我将该方法配置为在 read-only=true 上运行,它仍然会提交交易。

数据库:Oracle 10g

我的数据库配置.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schem...ring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>

    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:com/mybatis/mappers/*.xml" />
    </bean>


    <!--
        the transactional advice (what 'happens'; see the <aop:advisor/> bean
        below)
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true" />
            <!-- other methods use the default transaction settings (see below) -->
            <tx:method name="*" read-only="true" rollback-for="RuntimeException"/>
        </tx:attributes>
    </tx:advice>

    <!--
        ensure that the above transactional advice runs for any execution of
        an operation defined by the FooService interface
    -->
    <aop:config>
        <aop:pointcut id="fooServiceOperation"
            expression="execution(* com.service.EmployeeService.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" />
    </aop:config>


</beans>

我的控制器

package com.service;

import java.util.List;

import com.mybatis.dao.EmployeeMapperInterface;
import com.spring.model.Employee;

public class EmployeeService implements EmployeeBaseService{

    EmployeeMapperInterface employeeMapper;

    public EmployeeMapperInterface getEmployeeMapper() {
        return employeeMapper;
    }

    public void setEmployeeMapper(EmployeeMapperInterface employeeMapper) {
        this.employeeMapper = employeeMapper;
    }

    @Override
    public Employee getEmployeeById(long empId){
        //retrieve from database
        List empList = employeeMapper.getEmployeeWithId(empId);
        if(empList != null && empList.size()>0){
            return (Employee) empList.get(0);   
        }
        return null;

    }




      @Override
    public long saveEmployee(Employee employee){
        long empId = 0l;
        if(employee.getEmpId()==0){
            empId  = new Long( employeeMapper.insertEmployee(employee));
        }else{
             employeeMapper.updateEmployee(employee);
             empId  =  employee.getEmpId();
        }
        try {
            System.out.println("gonna sleep");
            Thread.sleep(10);

        } catch (InterruptedException e) {

            e.printStackTrace();
        }
        return empId;
    }

如何防止自动提交?我还注意到,即使我不放任何事务管理代码,代码仍然会提交。请注意,事务建议是在我为 RuntimeException 放置 no-rollback-for 然后执行 1/0 时调用的,如果我放置与 rollback-for 相同的内容,它会正确提交数据并回滚。 我还通过将线程置于睡眠状态来尝试查询超时,即使这样也不起作用,但我认为超时可能是针对实际查询的,所以没关系。 提前致谢!

【问题讨论】:

    标签: spring-mvc jdbc transactions autocommit


    【解决方案1】:

    建议read-only 只是建议。当标记为read-only 时,底层事务管理系统不需要阻止写入,更多的是作为优化提示,表示此方法是只读的,因此您不必担心它会改变事物.如果在只读事务中进行更改,一些事务管理器会抱怨,有些则不会。一般通过 JNDI 获取的datasources 不会。在任何情况下,您都不应该依赖read-only 建议来防止将更改写回磁盘。

    防止更改被持久化的选项是:

    • 标记事务rollback only或抛出具有相同效果的异常

    • 在更改之前从事务会话中分离/驱逐对象

    • 克隆对象并使用克隆

    【讨论】:

    • 感谢老专业人士的回答。我已经尝试了选项 1,正如您在我的 rollback-for 属性中看到的那样。但是,我仍然不能 100% 确定只读属性只是一个建议。通过static.springsource.org/spring/docs/2.5.x/reference/…,它明确指出 只读状态:只读事务不会修改任何数据。在某些情况下(例如使用 Hibernate 时),只读事务可能是一种有用的优化。
    • 要补充一点,我该如何进行 2 和 3?我想运行更新,但看不到另一个会话中反映的结果,仅此而已。
    【解决方案2】:

    DataSourceTransactionManager 使用doBegin 方法开始事务。
    从此方法DataSourceUtils.prepareConnectionForTransaction 调用。
    在此方法中,您可以看到以下代码块:

        if (definition != null && definition.isReadOnly()) {
            try {
                if (logger.isDebugEnabled()) {
                    logger.debug("Setting JDBC Connection [" + con + "] read-only");
                }
                con.setReadOnly(true);
            }
            catch (SQLException ex) {
    

    因此,您可以配置您的日志框架,将DataSourceUtils 类的日志级别设置为调试。

    或者你可以在这个地方设置断点并手动调试。


    根据这个article,我希望SET TRANSACTION READ ONLY 将在您的Oracle 连接上执行。


    Oracle docs我们可以看到你在成功的情况下获得的好处:

    默认情况下,Oracle 的一致性模型保证语句级读取一致性,但不保证事务级读取一致性(可重复读取)。如果您想要事务级别的读取一致性,并且您的事务不需要更新,那么您可以指定一个只读事务。在指示您的事务是只读的之后,您可以对任何数据库表执行任意数量的查询,因为您知道只读事务中每个查询的结果相对于单个时间点是一致的。

    【讨论】:

    • 正是我所期待的,这就是为什么它如此令人困惑!如果我们只执行 SET TRANSACTION READ,我们无法对同一事务进行任何更新/插入,那么为什么它允许在这里?
    • 正如我在回答中所写的 - 尝试调试。也许建议不起作用。
    • 不,建议有效,因为 rollback-for 和 no-rollback-for 效果很好。
    【解决方案3】:

    只读行为是严格特定于驱动程序的。 Oracle 驱动程序完全忽略此标志。例如,如果在只读事务中运行,在 Oracle 中执行的相同更新语句将修改数据库,而在 HSQL2 中,我遇到了数据库级别的异常。

    除了通过 api 或异常显式回滚以防止在 Oracle 中提交之外,我不知道其他方法。同样,您的代码也可以在不同的驱动程序和数据库之间移植。

    【讨论】:

      【解决方案4】:

      答案在Spring MVC Mybatis transaction commit

      还提供了详细的堆栈跟踪。 总而言之,

      1. 只读只是一个建议,它不能保证任何东西,我会 真的很喜欢 Spring 文档对此进行更新。
      2. 每当使用 Mybatis 在 Oracle 中执行查询时,它都是在自动启动的事务的上下文中, 已提交(或回滚,如果引发了 execption),并由 Mybatis。
      3. 记录应用程序是个好主意,它帮助我了解实际事务是如何开始的等等

      .

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-13
        • 1970-01-01
        • 2021-09-01
        • 1970-01-01
        • 2011-01-26
        • 1970-01-01
        • 2021-03-24
        相关资源
        最近更新 更多