2017-01-20 13 views
0

Я пытаюсь настроить мое веб-приложение, использующее Spring и MyBatis.Выбросить исключение из-за отсутствия @Transactional с Spring и MyBatis

Вот важные фрагменты кода.

Maven зависимости в pom.xml:

... 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-core</artifactId> 
     <version>4.3.5.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-web</artifactId> 
     <version>4.3.5.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-jdbc</artifactId> 
     <version>4.3.5.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.postgresql</groupId> 
     <artifactId>postgresql</artifactId> 
     <version>9.4.1212.jre7</version> 
    </dependency> 

    <dependency> 
     <groupId>org.mybatis</groupId> 
     <artifactId>mybatis</artifactId> 
     <version>3.4.2</version> 
    </dependency> 

    <dependency> 
     <groupId>org.mybatis</groupId> 
     <artifactId>mybatis-spring</artifactId> 
     <version>1.3.1</version> 
    </dependency> 
    ... 

и конфигурация Spring фасоли:

@Configuration 
@EnableTransactionManagement 
public class DatabaseConfiguration { 

    @Value("classpath:db/mybatis/mybatis-configuration.xml") 
    private Resource myBatisConfiguration; 

    @Bean 
    public DataSource dataSource() { 
     final DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
     dataSource.setDriverClassName("org.postgresql.Driver"); 
     dataSource.setUrl("jdbc:postgresql://127.0.0.1:5432/ehdb"); 
     dataSource.setUsername(/*my username*/); 
     dataSource.setPassword(/*my password*/); 
     return dataSource; 
    } 

    @Bean 
    public PlatformTransactionManager transactionManager() { 
     final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource()); 
     transactionManager.setValidateExistingTransaction(true); 
     return transactionManager; 
    } 

    @Bean 
    public SqlSessionFactoryBean sqlSessionFactoryBean() { 
     final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); 
     sqlSessionFactory.setDataSource(dataSource()); 
     sqlSessionFactory.setConfigLocation(myBatisConfiguration); 
     return sqlSessionFactory; 
    } 

    @Bean 
    public SqlSession sqlSession() throws Exception { 
     return new SqlSessionTemplate(sqlSessionFactoryBean().getObject()); 
    } 
} 

А вот это услуга, которая должна вызывать некоторое MyBatis заявления в транзакционной методе:

@Service 
public class HelloManagerImpl implements HelloManager { 
    private final HelloDao helloDao; 

    public HelloManagerImpl(@Autowired final HelloDao helloDao) { 
     this.helloDao = helloDao; 
    } 

    @Override 
    @Transactional 
    public String doSomething() { 
     helloDao.insertRow(); // a row is inserted into DB table via MyBatis; bean sqlSession is autowired in HelloDao 
     throw new RuntimeException(); // transaction will be rolled back here 
    } 
} 

Если я вызываю метод doSomething, он работает как ожидалось. Никакая новая строка не появляется в таблице базы данных, так как транзакция откатывается из-за броска RuntimeException.

Если я прокомментирую инструкцию throw и повторю эксперимент, в таблице базы данных появится новая строка. Это ожидаемое поведение снова.

И теперь, если я дополнительно прокомментирую аннотацию @Transactional и позвонил doSomething(), метод будет успешным, и новая таблица будет вставлена ​​в таблицу. Кажется, что MyBatis автоматически создает транзакцию для оператора INSERT, если транзакция не существует.

В последнем случае я предпочел бы потерпеть неудачу. Если я забуду написать аннотацию @Transactional, это, вероятно, ошибка. Было бы замечательно, если бы исключение в таком случае заставляло меня исправлять мой код, а не создавать транзакцию молча.

Есть ли способ достичь этого, пожалуйста?

Спасибо за помощь.

+0

Поддерживает ли ваша транзакция db? Как вы знаете, движок MYISAM не делает. – Blank

+0

@Forward Да, я использую Postgres 9.6. – Cimlman

ответ

0

Поведение транзакции управляется самой весной, а весной roolbacks транзакция в случае RuntimeException.

Если вы не предоставили аннотацию @Transactional, весна не рассматривает ее как транзакцию и ничего не сделает в случае RuntimeException. Итак, сделайте хорошую практику для добавления аннотации @Transactional выше методов обслуживания.

+0

Да, я понимаю, что Spring не может откатить транзакцию без аннотации @Transactional. MyBatis (или мой собственный код, настраивающий MyBatis каким-то образом) должен вызывать исключение. Если я вызываю MyBatis (например, 'sqlSession.insert (" statementId ")'), исключение должно быть выбрано, если транзакция не выполняется. Я работаю над другим проектом, в котором Hibernate используется вместо MyBatis, и исключение выбрано в аналогичном случае. – Cimlman

+0

Вы бросаете RuntimeException после завершения задачи MyBatis, поэтому нет эффекта RuntimeException – Avinash

+0

Я знаю. :) Реализация 'helloDao.insertRow()' просто вызывает 'sqlSession.insert (« myStatementId »);'. Метод 'insert' должен вызывать исключение. Исключение должно быть выбрано MyBatis, прежде чем любой оператор SQL будет отправлен в базу данных.Я ищу что-то вроде 'MyBatisConfiguration.setThrowExceptionWheneverAnyMethodOnSqlSessionIsCalledWithoutPreceedingTransactionalAnnotation (true);'. – Cimlman

1

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

@Service 
@Transactional(readOnly = true) 
public class HelloManagerImpl implements HelloManager { 
    private final HelloDao helloDao; 

    public HelloManagerImpl(@Autowired final HelloDao helloDao) { 
    this.helloDao = helloDao; 
    } 

    @Override 
    @Transactional(readOnly = false) 
    public String doSomething() { 
    helloDao.insertRow(); // a row is inserted into DB table via MyBatis; bean sqlSession is autowired in HelloDao 
    throw new RuntimeException(); // transaction will be rolled back here 
    } 
} 
+0

+ 1Это не является конфигурацией MyBatis, которую я искал, но это разумное решение. Я также рассматриваю '@Transactional (распространение = Распространение.MANDATORY)' на классах или методах DAO. – Cimlman