2017-01-20 49 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

相关问题