zoukankan      html  css  js  c++  java
  • springboot+mybatis多数据源的事务问题

    1.springboot+mybatis实现多数据源后,针对单个数据源我们可以使用@Transactional(name="xxxTransactionManager")

    来指定使用的事务管理器,但是如果被注解的方法需要同时支持两个事务管理器呢,这个时候如果用@Transactional注解就不

    合适了,属性name只支持字符串类型,所以只能填一个,如果不传name属性,而且项目没有配置默认的事务管理器,在调用方

    法时则会抛出未指定默认事务管理器的异常。这个时候,我们可以用编程式事务来解决这个问题。

    2.多数据源配置已经完成,但是还没有事务管理器,所以先配置支持事务。

    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.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    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.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import javax.sql.DataSource;
    /**
     * 数据源1
     */
    @Configuration
    @MapperScan(basePackages = "com.example.mybatis.mapper",sqlSessionFactoryRef = "sqlSessionFactoryOne")
    public class DataSourceConfigOne {
    
        @Bean(name = "dataSourceOne")
        @Primary// 表示这个数据源是默认数据源
        // 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀
        @ConfigurationProperties(prefix = "spring.datasource.one")
        public DataSource dataSourceOne() {
            return  DataSourceBuilder.create().build();
        }
    
        @Bean(name = "sqlSessionFactoryOne")
        @Primary
        public SqlSessionFactory sqlSessionFactoryOne(@Qualifier("dataSourceOne") DataSource datasource)throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(datasource);
            bean.setMapperLocations(
                    // 设置mybatis的xml所在位置
                    new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
            bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
            return bean.getObject();
        }
        @Bean(name = "transactionManagerOne")
        public PlatformTransactionManager transactionManagerOne(@Qualifier("dataSourceOne") DataSource dataSourceOne) {
            return new DataSourceTransactionManager(dataSourceOne);
        }
    
        @Primary
        public SqlSessionTemplate sqlsessiontemplateOne(@Qualifier("sqlsessiontemplateOne") SqlSessionFactory sessionfactory) {
            return new SqlSessionTemplate(sessionfactory);
        }
    }



    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.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource;
    /** * 数据源2 */ @Configuration @MapperScan(basePackages = "com.example.mybatis.mapper2",sqlSessionFactoryRef = "sqlSessionFactoryTwo") public class DataSourceConfigTwo { @Bean(name = "dataSourceTwo") // 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀 @ConfigurationProperties(prefix = "spring.datasource.two") public DataSource dataSourceTwo() { return DataSourceBuilder.create().build(); } @Bean(name = "sqlSessionFactoryTwo") public SqlSessionFactory sqlSessionFactoryTwo(@Qualifier("dataSourceTwo") DataSource datasource)throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(datasource); bean.setMapperLocations( // 设置mybatis的xml所在位置 new PathMatchingResourcePatternResolver().getResources("classpath:mapper2/*.xml")); bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);//下划线-驼峰映射 return bean.getObject(); } @Bean(name = "transactionManagerTwo") public PlatformTransactionManager transactionManagerTwo(@Qualifier("dataSourceTwo") DataSource dataSourceTwo) { return new DataSourceTransactionManager(dataSourceTwo); } public SqlSessionTemplate sqlsessiontemplateTwo(@Qualifier("sqlsessiontemplateTwo") SqlSessionFactory sessionfactory) { return new SqlSessionTemplate(sessionfactory); } }

    3.使用自定义注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD,ElementType.PARAMETER})
    public @interface MoreTransaction {
        String[] value() default {};
    }

    4.事务切面

    @Aspect
    @Component
    public class TransactionAop {
        @Pointcut("@annotation(com.example.mybatis.config.aop.annotation.MoreTransaction)")
        public void MoreTransaction() {
        }
    
        @Pointcut("execution(* com.example.mybatis.controller.*.*(..))")
        public void excudeController() {
        }
    
        @Around(value = "MoreTransaction()&&excudeController()&&@annotation(annotation)")
        public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, MoreTransaction annotation) throws Throwable {
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<>();
            Stack<TransactionStatus> transactionStatuStack = new Stack<>();
    
            try {
                if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, annotation)) {
                    return null;
                }
                Object ret = thisJoinPoint.proceed();
                commit(dataSourceTransactionManagerStack, transactionStatuStack);
                return ret;
            } catch (Throwable e) {
                rollback(dataSourceTransactionManagerStack, transactionStatuStack);
                log.error(String.format("MultiTransactionalAspect, method:%s-%s occors error:",
                        thisJoinPoint.getTarget().getClass().getSimpleName(), thisJoinPoint.getSignature().getName()), e);
                throw e;
            }
        }
    
        /**
         * 开启事务处理方法
         *
         * @param dataSourceTransactionManagerStack
         * @param transactionStatuStack
         * @param multiTransactional
         * @return
         */
        private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                                        Stack<TransactionStatus> transactionStatuStack,MoreTransaction multiTransactional) {
    
            String[] transactionMangerNames = multiTransactional.value();
            if (ArrayUtils.isEmpty(multiTransactional.value())) {
                return false;
            }
            for (String beanName : transactionMangerNames) {
                DataSourceTransactionManager dataSourceTransactionManager =(DataSourceTransactionManager) SpringContextUtil.getBean(beanName);
                TransactionStatus transactionStatus = dataSourceTransactionManager
                        .getTransaction(new DefaultTransactionDefinition());
                transactionStatuStack.push(transactionStatus);
                dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
            }
            return true;
        }
    
        /**
         * 提交处理方法
         *
         * @param dataSourceTransactionManagerStack
         * @param transactionStatuStack
         */
        private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                            Stack<TransactionStatus> transactionStatuStack) {
            while (!dataSourceTransactionManagerStack.isEmpty()) {
                dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
            }
        }
    
        /**
         * 回滚处理方法
         * @param dataSourceTransactionManagerStack
         * @param transactionStatuStack
         */
        private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                              Stack<TransactionStatus> transactionStatuStack) {
            while (!dataSourceTransactionManagerStack.isEmpty()) {
                dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
            }
        }
    }

    5.service层两个我要新增的方法,这两个方法是两个数据源(一个是我本地的,一个是阿里云的)

     @Override
        public int addUserInfo(UserInfo record) {
            return userInfoMapper.insert(new UserInfo().setId(UUID.randomUUID().toString()).setUserAccount("吾问无为谓"));
        }
      @Override
        public int addUser(User record) {
            return userMapper.insertSelective(record);
        }

     6.controller层的调用方法上使用事务注解,将两个数据源的事务管理器名字作为参数传入。

     @GetMapping("/dataSourceList")
        @ResponseBody
        @MoreTransaction(value = {"transactionManagerOne","transactionManagerTwo"})
        public ResultData getDataSourceList(){
            int i=userService.addUser(new User().setPhone("11111111111").setUserName("哈哈哈"));
            int a=1/0;
            int k=userService.addUserInfo(new UserInfo().setId(UUID.randomUUID().toString()).setUserAccount("吾问无为谓"));
            Map map=new HashMap();
            map.put("k",k);
            return ResultData.success(map);
        }

    7.提交请求后会发现控制台报错,但是数据库里面并没有插入数据。

    参考链接:https://blog.csdn.net/qq_34322777/article/details/80833935

  • 相关阅读:
    Debug权限提升
    QTime的本质上是一个int,QDateTime本质上是一个qint64
    QString先于QObject存在,因为QObject::objectName()返回的是QString
    C++杂记:运行时类型识别(RTTI)与动态类型转换原理
    gcc/g++编译(生动形象,从最容易入手的hello world解释了库的概念)
    分布式存储
    开源代码——Crouton
    android studio
    WebRTC
    FileProvider是个什么东西?
  • 原文地址:https://www.cnblogs.com/red-star/p/12535919.html
Copyright © 2011-2022 走看看