SpringBoot整合Mybatis中如何實現事務控制?
作為一名資深的CURD程序員,事務控制/事務管理是一項不可避免的工作,也是最常見的一項功能,簡單說,事務管理就是在執行業務操作時,由于數據操作在順序執行的過程中,任何一步操作都有可能發生異常,異常會導致后續操作無法完成,此時由于業務邏輯并未正確的完成,之前成功操作數據的并不可靠,需要在這種情況下進行回退。
1、默認的事務管理配置方式:
在引入相關的依賴之后(比如springboot的web組件依賴、父依賴、mysql驅動依賴以及mybatis依賴等)
<!-- springboot 整合 mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency>需要在設計service層的時候,將方法用@Transational注解進行注釋,默認的話在拋出Exception.class異常的時候,就會觸發方法中所有數據庫操作回滾。
而@Transational注解也擁有許多的參數,比如:
rollbackFor:可以指定需要進行回滾的異常,指定Class對象數組,且該Class必須繼承自Throwable;value:用于在多數據源的情況下,進行事務管理器的指定(下面描述下多數據源事務這種情況);noRollbackFor:有rollbackFor自然有noRollbackFor,顧名思義,用于指定不需要進行回滾的異常;readOnly:是讀寫還是只讀事務,默認是false,讀寫;還有許多,不一一描述了....
實例:
@Servicepublic class TestTransactionalService @Autowired private TestMapper testMapper; @Transactional //當拋出Exception的時候,將進行回滾操作 public int insertTest(TestEntity testEntity) { testEntity.setName("get out! helloService") return testMapper.insertOne(testEntity); }}另外,在SpringBoot的啟動類中,需要增加@EnableTransactionManagement注解,用于啟動事務管理。
實例:
@EnableTransactionManagement@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}至此,SpringBoot整合Mybatis的單數據源的事務管理便配置完成
2、多數據源的事務配置方式:
第一種方式基本上滿足了普通項目的事務管理功能, 但當項目是比較大型的項目的時候(比如電商項目),可能會存在多個數據源,這時候會出現多個事務管理器,也就需要在聲明的時候為不同數據源的數據操作指定不同的事務管理器。
1)首先,需要引入數據源依賴
<!-- 數據源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.19</version> </dependency>2)配置properties配置文件(或者yml文件...)
#主數據源,多數據源的情況下,需要指定主數據源,在后續的config中進行,此時我們將#base數據源作為主數據源來看待~spring.datasource.base.jdbc-url=jdbc:mysql://localhost:3306/test1spring.datasource.base.username=rootspring.datasource.base.password=rootspring.datasource.base.driver-class-name=com.mysql.jdbc.Driver#從數據源spring.datasource.second2.jdbc-url=jdbc:mysql://localhost:3306/test2spring.datasource.second2.username=rootspring.datasource.second2.password=rootspring.datasource.second2.driver-class-name=com.mysql.jdbc.Driver3)新增配置類,讀取配置文件,進行數據源的配置
注意,配置類需要對DataSource、DataSourceTransactionManager、SqlSessionFactory 、SqlSessionTemplate四個數據項進行配置;
其中DataSource類型需要引入javax.sql.DataSource;
配置主數據源:
如上文所說,當系統中有多個數據源時,必須有一個數據源為主數據源,在配置類中我們使用@Primary修飾。
通過@MapperScan注解對指定dao包建立映射,確保在多個數據源下,自動選擇合適的數據源,而在service層里不需要做特殊說明,否則需要通過@Transactional的value屬性進行指定
@Configuration @MapperScan(basePackages = "com.livinghome.base", sqlSessionTemplateRef = "baseSqlSessionTemplate",sqlSessionFactoryRef = "baseSqlSessionFactory") public class BaseDataSourceConfig { /**讀取base數據源**/ @Bean(name = "baseDataSource") @ConfigurationProperties(prefix = "spring.datasource.base") @Primary public DataSource setDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "baseTransactionManager") @Primary public DataSourceTransactionManager setTransactionManager(@Qualifier("baseDataSource") DataSource dataSource) { return new DruidDataSource(); } @Bean(name = "baseSqlSessionFactory") @Primary public SqlSessionFactory setSqlSessionFactory(@Qualifier("baseDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/base/*.xml")); return bean.getObject(); } @Bean(name = "baseSqlSessionTemplate") @Primary public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("baseSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }配置從數據源:
@Configuration@MapperScan(basePackages = "com.livinghome.second2", sqlSessionTemplateRef = "zentaoSqlSessionTemplate",sqlSessionFactoryRef = "zentaoSqlSessionFactory")public class Second2DataSourceConfig { @Bean(name = "second2DataSource") @ConfigurationProperties(prefix = "spring.datasource.second2") public DataSource setDataSource() { return new DruidDataSource(); } @Bean(name = "second2TransactionManager") public DataSourceTransactionManager setTransactionManager(@Qualifier("second2DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "second2SqlSessionFactory") public SqlSessionFactory setSqlSessionFactory(@Qualifier("second2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/second2/*.xml")); return bean.getObject(); } @Bean(name = "second2SqlSessionTemplate") public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("second2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); }}4)到了這里,我們基本的多數據源事務管理便已經完成了(真的)....
對于service層,不需要進行事務管理器的指定,因為我們上面使用了@MapperScan進行了包指定,當然也可以手動指定,方式便是 @Transactional(transactionManager="baseTransactionManager")
便可手動指定為base數據源。
另外,還有分布式事務管理,也就是在一次操作中,操作了不同的數據源的情況,對于service而言,便是在一次service里調用了兩個數據源的方法,這種情況常見于微服務架構中,例如電商系統(第二次使用電商系統舉例..)。
從百度上copy了一個簡單的下單流程:
在微服務中,2、3、4步驟是涉及了3個系統以及3個數據庫的,當某個操作出現問題時,會出現多數據源的事務管理問題,傳統的方式是通過將不同數據源的事務都注冊到一個全局事務中(可以通過jpa+atomikos來進行),但有大神告訴我這種方式性能差,具體還未有實踐,不是很清楚。
我說完了... 因為對微服務架構學習還在進行中,所以對于分布式事務問題我還沒有太多的理解和實踐,等我回來....
同時,歡迎大神為我指點明路!!
——沒事待在家里不出門的 居家程序員。(我不想脫發!)