Spring MVC JDBC DataSourceTransactionManager: данные, принятые даже после readonly=true

В настоящее время я занимаюсь разработкой приложения Spring MVC. Я настроил JDBC TransactionManager, и я делаю декларативное управление транзакциями с использованием AOP XML. Однако, даже если я настраиваю метод для выполнения только для чтения =true, он все равно фиксирует транзакцию.

База данных: Oracle 10g

Моя база-config.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;
    }

Как предотвратить автоматическую фиксацию? Я также заметил, что даже если я не добавлю код управления транзакциями, он все равно будет зафиксирован. Заметьте, однако, что совет по транзакциям вызывается так, как если бы я поставил no-rollback-for для RuntimeException, а затем сделал 1/0, он корректно фиксирует данные и откатывается, если я ставлю то же самое, что и rollback-for. Я также опробовал тайм-аут запроса, поместив поток в спящий режим, даже это не работает, но я думаю, что тайм-аут может быть для реального запроса, так что это нормально. Заранее спасибо!

4 ответа

Решение

Ответ на коммит транзакции Mybatis Spring MVC

Подробные следы стека также доступны. Подвести итоги,

  1. Только чтение - это всего лишь совет, и он ничего не гарантирует, и я бы очень хотел, чтобы документы Spring были обновлены по этому поводу.
  2. всякий раз, когда запрос выполняется в Oracle с использованием Mybatis, он находится в контексте транзакции, которая автоматически запускается, фиксируется (или откатывается, если выполняется исключение) и закрывается Mybatis.
  3. Регистрация приложения была хорошей идеей, и она помогла мне узнать, как запускаются реальные транзакции и т. Д.

,

Совет read-only это всего лишь совет. Не требуется, чтобы базовая система управления транзакциями предотвращала запись, когда что-то помечено read-onlyЭто скорее подсказка по оптимизации, говорящая, что этот метод предназначен только для чтения, поэтому вам не нужно беспокоиться о том, что он что-то меняет. Некоторые менеджеры транзакций будут жаловаться, если в транзакцию только для чтения вносятся изменения, а некоторые нет. В общем-то, datasourceS приобретается через JNDI не будет. В любом случае не стоит полагаться на read-only совет, предотвращающий запись изменений на диск.

Ваши варианты предотвращения сохранения изменений:

  • Отметить транзакцию rollback only или выбросить исключение, имеющее тот же эффект

  • Отключите / удалите объект из сеанса транзакции перед его изменением.

  • Клонировать объект и использовать клон

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) {

Таким образом, вы можете сконфигурировать свою инфраструктуру журналирования, чтобы установить уровень журнала на DEBUG для DataSourceUtils учебный класс.

Или вы можете установить точку останова в этом месте и отладить вручную.


Согласно этой статье я ожидаю, что SET TRANSACTION READ ONLY будет выполнен на вашем соединении Oracle.


И из документов Oracle мы могли видеть преимущества, которые вы получаете в случае успеха:

По умолчанию модель согласованности для Oracle гарантирует согласованность чтения на уровне инструкций, но не гарантирует согласованность чтения на уровне транзакций (повторяемые операции чтения). Если вы хотите согласованности чтения на уровне транзакции и если ваша транзакция не требует обновлений, вы можете указать транзакцию только для чтения. Указав, что ваша транзакция доступна только для чтения, вы можете выполнить любое количество запросов к любой таблице базы данных, зная, что результаты каждого запроса в транзакции только для чтения согласуются по отношению к одному моменту времени.

Поведение только для чтения строго зависит от драйвера. Драйвер Oracle полностью игнорирует этот флаг. Например, те же операторы обновления, которые выполняются в Oracle, изменят базу данных, если они выполняются в транзакции только для чтения, тогда как в HSQL2 я получал исключения уровня db.

Я не знаю другого способа, кроме явного отката через API или исключения для предотвращения фиксации в Oracle. Таким образом, ваш код будет переносимым между различными драйверами и базами данных.

Другие вопросы по тегам