@Transactional註解異常報錯之多數據源詳解
@Transactional註解報錯之多數據源
如果在加上@Transactional註解之後報錯,先查看程序是否為多數據源,之前專門有一章講解springboot的多數據源實現。多數據源的情況下加事物註解,有可能會出現問題,以下是解決方案。
1.在配置數據源的同時
一定到在其中一個配置上加上@Primary註解,其他的不要加。
package com.wys.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; /** * @program: * @description: 數據庫配置1 * @author: wys * @create: 2019-12-03 16:20 **/ @Configuration @MapperScan(basePackages = "com.wys.mapper.**", sqlSessionFactoryRef = "oneSqlSessionFactory") public class OneDataSourceConfig { @Value("${spring.datasource.one.driver-class-name}") String driverClass; @Value("${spring.datasource.one.url}") String url; @Value("${spring.datasource.one.username}") String userName; @Value("${spring.datasource.one.password}") String passWord; @Primary @Bean(name = "oneDataSource") @ConfigurationProperties("spring.datasource.one") public DataSource masterDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(userName); dataSource.setPassword(passWord); return dataSource; } @Bean(name = "oneSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("oneDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mybatis/mapper-postgre/*.xml")); return sessionFactoryBean.getObject(); } @Bean(name = "oneSqlSessionFactory") public SqlSessionTemplate sqlSessionFactoryTemplate(@Qualifier("oneSqlSessionFactory")SqlSessionFactory sqlSessionFactory ) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
2.一定要在需要使用事物註解的數據源配置裡
加上創建事務管理器
package com.wys.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; /** * @program: * @description: 數據庫配置1 * @author: wys * @create: 2019-12-03 16:20 **/ @Configuration @MapperScan(basePackages = "com.wys.mapper.**", sqlSessionFactoryRef = "oneSqlSessionFactory") public class OneDataSourceConfig { @Value("${spring.datasource.one.driver-class-name}") String driverClass; @Value("${spring.datasource.one.url}") String url; @Value("${spring.datasource.one.username}") String userName; @Value("${spring.datasource.one.password}") String passWord; @Primary @Bean(name = "oneDataSource") @ConfigurationProperties("spring.datasource.one") public DataSource masterDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(userName); dataSource.setPassword(passWord); return dataSource; } @Bean(name = "oneSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("oneDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mybatis/mapper-postgre/*.xml")); return sessionFactoryBean.getObject(); } / // 創建事務管理器1 @Bean(name = "oneManager1") public PlatformTransactionManager txManager(@Qualifier("oneDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } ///////////////////////// @Bean(name = "oneSqlSessionFactory") public SqlSessionTemplate sqlSessionFactoryTemplate(@Qualifier("oneSqlSessionFactory")SqlSessionFactory sqlSessionFactory ) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
在需要加註解的地方加上transactionManager 配置即可。
@Transactional(transactionManager ="oneManager1",rollbackFor=Exception.class)
@Transactional 錯誤使用的幾種場景
@RestController public class AController { @Autowired AService aService; // 回滾 @GetMapping("direct") public void direct() { aService.testTransactional(); } // 不回滾 @GetMapping("indirect") public void indirect() { aService.testTransactionalIndirect(); } // 不回滾 @GetMapping("nonPublic") public void nonPublic() { aService.testTransactionalNonPublic(); } // 不回滾 @GetMapping("catchException") public void catchException() { aService.testTransactionalCatchException(); } // 不回滾 @GetMapping("sqlException") public void sqlException() throws SQLException { aService.testTransactionalSQLException(); } // 回滾 @GetMapping("sqlExceptionWithRollbackfor") public void sqlExceptionWithRollbackfor() throws SQLException { aService.testTransactionalSQLExceptionWithRollbackfor(); } }
@Service public class AService { @Autowired TestTableDAO testTableDAO; // 回滾 @Transactional public void testTransactional() { ATestTable er = new ATestTable(); er.setSummary("test"); testTableDAO.save(er); throw new RuntimeException("exception"); } // 不回滾: 類內部方法調用本類內部的其他方法並不會引起事務行為,即使被調用方法使用@Transactional註解進行修飾 public void testTransactionalIndirect() { testTransactional(); } // 不回滾: @Transaction註解隻對方法名為pubic的才生效 @Transactional void testTransactionalNonPublic() { ATestTable er = new ATestTable(); er.setSummary("test"); testTableDAO.save(er); throw new RuntimeException("exception"); } // 不回滾 @Transactional public void testTransactionalCatchException() { ATestTable er = new ATestTable(); er.setSummary("test"); testTableDAO.save(er); try { throw new RuntimeException("exception"); } catch (Exception e) { System.out.println("catch"); } } // 不回滾: @Transactional默認情況下隻回滾RuntimeException和Error @Transactional public void testTransactionalSQLException() throws SQLException { ATestTable er = new ATestTable(); er.setSummary("test"); testTableDAO.save(er); throw new SQLException("exception"); } // 回滾: 指定在 SQLException 異常發生時回滾 @Transactional(rollbackFor = { SQLException.class }) public void testTransactionalSQLExceptionWithRollbackfor() throws SQLException { ATestTable er = new ATestTable(); er.setSummary("test"); testTableDAO.save(er); throw new SQLException("exception"); } }
@Repository public interface TestTableDAO extends JpaRepository<ATestTable, Integer>, JpaSpecificationExecutor<ATestTable> { }
@Entity @Data @Table(name = "test") public class ATestTable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; @Column(name = "summary", length = 512) String summary; }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- springboot如何連接兩個數據庫(多個)
- SpringBoot整合Mybatis Plus多數據源的實現示例
- MybatisPlus的IPage失效的問題解決方案
- springboot配置mybatis和事務管理方式
- 在SSM中配置瞭事務控制但沒生效的問題