Affairs issues springboot + mybatis multiple data sources

After 1.springboot + mybatis multi-source data for a single data source we can use @Transactional (name = "xxxTransactionManager")

To specify the transaction manager to use, but if the annotated method requires two transaction managers to support it, if this time is not annotated with @Transactional

Appropriate, attribute name only support string type, you can only fill one, if not pass the name attribute, and the project is not configured the default transaction manager, the caller

When the law will throw an exception default transaction manager is not specified. This time, we can use programmatic transaction to solve this problem.

2. The multi data source configuration has been completed, but no transaction manager, so the first configuration support transactions.

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;
org.springframework.jdbc.datasource.DataSourceTransactionManager Import;
import org.springframework.transaction.PlatformTransactionManager;

the javax.sql.DataSource Import; 
/ * * 
 * data source. 1 
 * / 
@Configuration 
@MapperScan (basePackages = " com.example.mybatis.mapper " , sqlSessionFactoryRef = " sqlSessionFactoryOne " )
 public  class DataSourceConfigOne { 

    @Bean (name = " dataSourceOne " ) 
    @Primary // indicating that the data source is a default data source
     // read the configuration parameters in application.properties mapped to an object, prefix represents a prefix parameter 
    @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; the org.springframework.transaction.PlatformTransactionManager Import; Import the javax.sql.DataSource;
/ * * * data source 2 * / @Configuration @MapperScan (basePackages = " com.example.mybatis.mapper2 " , sqlSessionFactoryRef = " sqlSessionFactoryTwo " ) public class {DataSourceConfigTwo @Bean (name = " dataSourceTwo " ) // read the configuration parameters in application.properties mapped to an object, prefix represents a prefix parameter @ConfigurationProperties (prefix = " spring.datasource.two " ) public the 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. Use the custom annotation

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

4. affairs section

@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 I want to add two layer method, the two methods are the two data sources (one is my local, Ali is a cloud)

 @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);
    }

 Using transaction annotation, invoke methods 6.controller layer on the transaction manager two data source name as a parameter.

 @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. After the commit request will find the console being given, but no data is inserted inside the database.

 

 

This article reprinted from: https: //blog.csdn.net/qq_34322777/article/details/80833935

Guess you like

Origin www.cnblogs.com/red-star/p/12535919.html