最近在做一个项目,项目基于SpringBoot实现后端,其中用到了Oracle数据库和MongoDB作为数据存储。 其中有的功能要求同时对Oracle和MongoDB进行读写操作,特别是写操作如果失败了需要对Oracle和MongoDB进行回滚,最初我们直接使用了SpringBoot提供的Transactional(rollbackForException。class)注解进行事务控制,可系统上线后发现异常发生时并没有回滚MongoDB的写操作,导致Oracle和MongoDB之间出现了数据差异。 为了解决这个我们我们基于SpringBoo提供的平台事务管理器PlatformTransactionManager通过注解和AOP编程实现了一个跨数据库的事务异常管理器。实现方式如下: 1。先定义一个事务注解Target({ElementType。METHOD})Retention(RetentionPolicy。RUNTIME)InheritedDocumentedpublicinterfaceMultiTransactional{String〔〕values()default{};} 2。使用AOP技术定一个事务处理的切面处理类Slf4jAspectComponentpublicclassMultiTransactionalAspect{Around(annotation(multiTransactional))publicObjectaround(ProceedingJoinPointpjp,MultiTransactionalmultiTransactional)throwsThrowable{StackPlatformTransactionManagertransactionManagerStacknewStack();StackTransactionStatustransactionStatusStacknewStack();try{if(!openTransaction(transactionManagerStack,transactionStatusStack,multiTransactional)){returnnull;}Objectretpjp。proceed();commit(transactionManagerStack,transactionStatusStack);returnret;}catch(Throwablee){rollback(transactionManagerStack,transactionStatusStack);log。error({},class:{},method:{},e。getMessage(),pjp。getTarget()。getClass()。getSimpleName(),pjp。getSignature()。getName(),e);throwe;}}privatebooleanopenTransaction(StackPlatformTransactionManagertransactionManagerStack,StackTransactionStatustransactionStatusStack,MultiTransactionalmultiTransactional){String〔〕transactionMangerNamesmultiTransactional。values();判断是否配置了事务管理器,如果指定则使用指定的,如果未指定,则启动使用全部事务管理器if(ArrayUtils。isNotEmpty(multiTransactional。values())){for(StringbeanName:transactionMangerNames){PlatformTransactionManagertransactionManager(PlatformTransactionManager)SpringContextHolder。getBean(beanName);TransactionStatustransactionStatustransactionManager。getTransaction(newDefaultTransactionDefinition());transactionStatusStack。push(transactionStatus);transactionManagerStack。push(transactionManager);}}else{MapString,PlatformTransactionManagertransactionManagerMapSpringContextHolder。getBeansOfType(PlatformTransactionManager。class);for(PlatformTransactionManagertransactionManager:transactionManagerMap。values()){TransactionStatustransactionStatustransactionManager。getTransaction(newDefaultTransactionDefinition());transactionStatusStack。push(transactionStatus);transactionManagerStack。push(transactionManager);}}returntrue;}privatevoidcommit(StackPlatformTransactionManagertransactionManagerStack,StackTransactionStatustransactionStatusStack){while(!transactionManagerStack。isEmpty()){transactionManagerStack。pop()。commit(transactionStatusStack。pop());}}privatevoidrollback(StackPlatformTransactionManagertransactionManagerStack,StackTransactionStatustransactionStatusStack){while(!transactionManagerStack。isEmpty()){transactionManagerStack。pop()。rollback(transactionStatusStack。pop());}}} 3。MongoDB数据库事务配置(只有MongoDB4。0以上集群部署的情况下才支持事务)ConfigurationpublicclassMongoTransactionManagerConfiguration{BeanConditionalOnProperty(namefood。mongodb。transaction,havingValuetrue)publicMongoTransactionManagermongoTransactionManager(MongoDatabaseFactoryfactory){returnnewMongoTransactionManager(factory);}} 4。在SpringBoot中强制注册一个事务管理器强制添加一个默认的TransactionManagerConfiguration(proxyBeanMethodsfalse)publicclassTransactionManagerConfiguration{BeanPrimaryDataSourceTransactionManagertransactionManager(Environmentenvironment,DataSourcedataSource,ObjectProviderTransactionManagerCustomizerstransactionManagerCustomizers){DataSourceTransactionManagertransactionManagercreateTransactionManager(environment,dataSource);transactionManagerCustomizers。ifAvailable((customizers)customizers。customize(transactionManager));returntransactionManager;}privateDataSourceTransactionManagercreateTransactionManager(Environmentenvironment,DataSourcedataSource){returnenvironment。getProperty(spring。dao。exceptiontranslation。enabled,Boolean。class,Boolean。TRUE)?newJdbcTransactionManager(dataSource):newDataSourceTransactionManager(dataSource);}} 至此程序就支持跨Oracle和MongoDB的事务处理了,在需要跨库事务处理的方法上加上MultiTransactional注解即可。 例如:MultiTransactionalprivatevoidupdateDataToDb(OracleDataInfooracleDataInfo,MongoDBDataInfoMongoDBDataInfo)throwsException{操作Oracle数据库this。oracleDataService。deal(oracleDataInfo);操作MongoDB数据库this。mongoDBDataService。update(MongoDBDataInfo);} 这种事务处理的方式通过改造可以支持所有平台的事务,包括关系型数据库、MongoDB等等。