2017-01-20 46 views
0

我正在嘗試設置使用Spring和MyBatis的Web應用程序。用Spring和MyBatis丟失@Transactional的異常

以下是重要的代碼片段。

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> 
    ... 

春豆及配置:

@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

事務的行爲是由Spring本身管理的,並且在RuntimeException的情況下會彈回一個事務。

如果您沒有提供@Transactional註釋,spring不會將其視爲事務,並且在RuntimeException的情況下不會執行任何操作。因此,請將@Transactional註釋放在服務方法上方。

+0

是的,我知道Spring不能在沒有@Transactional註解的情況下回滾事務。 MyBatis(或者我自己的代碼以某種方式定製MyBatis)應該拋出一個異常。如果我調用MyBatis(例如'sqlSession.insert(「statementId」)'),如果沒有事務正在進行,則應拋出異常。我正在使用Hibernate而不是MyBatis的另一個項目,並且在類似情況下引發異常。 – Cimlman

+0

你在MyBatis任務完成後拋出RuntimeException,所以沒有任何效果RuntimeException – Avinash

+0

我知道。 :)'helloDao.insertRow()'的實現只是調用'sqlSession.insert(「myStatementId」);'。 'insert'方法應該拋出異常。在任何SQL語句發送到數據庫之前,MyBatis應拋出異常。我正在尋找像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配置,但它是一個合理的解決方法。我也在考慮DAO類或方法上的@Transactional(propagation = Propagation.MANDATORY)。 – Cimlman

相關問題