SpringBoot+MyBatisPlus+Oracle双数据源在WebService接口事务生效

SpringBoot+MyBatisPlus+Oracle双数据源,开发WebService接口接收数据后插入双数据源数据库内,然后需要增加增加事务管理。
首先我先尝试用dynamic配置多数据源成功,但是在开启事务时使用@DSTransaction不生效,后来通过查询又使用编程式事务和声明式事务多数据源成功,事务怎么也不生效。
后来我不通过webservice接口测试,写一个测试类,都可以成功,这时候我意识到我的方法都是没问题的查找问题的方向错了,然后我就去查找webservice接口内事务不生效的问题。我将数据处理的方法统一写到一个类里,然后进行事务处理,后来问题就解决了!
如果直接按照业务层方式,在webservice实现是不可行的,@Autowired无法自动注入,还会报空指针的错误,因为在webservice的自动注入不是在spring容器中找bean对象,所以按照service层方式是无法取得对象,所以我找到一篇文章,里面教我如何手动给webservice注入我们所需要的对象,例如我们想调用service层的方法,那就把service层的接口找出来,并手动注入,放在webservice的实现类,然后再调用方法。
1.以下是我的文件路径
SpringBoot+MyBatisPlus+Oracle双数据源在WebService接口事务生效_第1张图片
2.application.yml文件如下

server:
  port: 21069

spring:
  #datasource:
    #dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      #primary: master
      # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      #strict: false
  datasource:
    master:
      #   oracle
      driverClassName: oracle.jdbc.driver.OracleDriver
      url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
      username: 用户名
      password: 密码
    secondary:
      #   oracle
      driverClassName: oracle.jdbc.driver.OracleDriver
      url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
      username: 用户名
      password: 密码
mybatis-plus:
    configuration:
        # 默认启用驼峰命名方式 此处数据库不需要驼峰命名
        map-underscore-to-camel-case: false

3.数据源1代码配置如下

package com.jlzh.lftwebservice.dao.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.Value;
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.support.TransactionTemplate;

import javax.sql.DataSource;

/**
 * @ClassName: DBConfig1
 * @ Description: 主数据源配置
 * @author:王红艳
 * @date:
 */
@Configuration
@MapperScan(basePackages = "com.jlzh.lftwebservice.dao.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DBConfig1 {

   @Value("${spring.datasource.master.url}")
   private String url;
    @Value("${spring.datasource.master.username}")
    private String username;
    @Value("${spring.datasource.master.password}")
    private String password;
    @Value("${spring.datasource.master.driverClassName}")
    private String driverClassName;
    /**本数据源扫描的mapper路径*/
    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";

    /**创建数据源*/
    @Bean(name = "masterDataSource")
    @Primary
    public DataSource masterDataSource() {
        DataSource build =  DataSourceBuilder.create()
                .driverClassName(driverClassName)
                .url(url)
                .username(username)
                .password(password)
                .build();
        return build;
    }

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(masterDataSource());
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
        return bean.getObject();
    }

    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }

    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(masterSqlSessionFactory());
    }

    @Bean(name = "masterTransactionTemplate")
    @Primary
    public TransactionTemplate masterTransactionTemplate(){
        return new TransactionTemplate(masterTransactionManager());
    }
}

4.数据源2代码配置如下

package com.jlzh.lftwebservice.dao.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.Value;
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.support.TransactionTemplate;

import javax.sql.DataSource;
/**
 * @ClassName: DBConfig2
 * @ Description: 第二个数据源配置
 * @author:王红艳
 * @date:
 */
@Configuration
@MapperScan(basePackages = "com.jlzh.lftwebservice.dao.mapper.secondary", sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class DBConfig2 {

    @Value("${spring.datasource.secondary.url}")
    private String url;
    @Value("${spring.datasource.secondary.username}")
    private String username;
    @Value("${spring.datasource.secondary.password}")
    private String password;
    @Value("${spring.datasource.secondary.driverClassName}")
    private String driverClassName;
    /**本数据源扫描的mapper路径*/
    static final String MAPPER_LOCATION = "classpath:mapper/secondary/*.xml";

    /**创建数据源*/
    @Bean(name = "secondaryDataSource")
    public DataSource secondaryDataSource() {
        DataSource build =  DataSourceBuilder.create()
                .driverClassName(driverClassName)
                .url(url)
                .username(username)
                .password(password)
                .build();
        return build;
    }

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(secondaryDataSource());
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
        return bean.getObject();
    }

    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager() {
        return new DataSourceTransactionManager(secondaryDataSource());
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(secondarySqlSessionFactory());
    }

    @Bean(name = "secondaryTransactionTemplate")
    public TransactionTemplate secondaryTransactionTemplate(){
        return new TransactionTemplate(secondaryTransactionManager());
    }
}

5.mapper代码如下(只举例其中一个)

package com.jlzh.lftwebservice.dao.mapper.master;

import com.jlzh.lftwebservice.dao.pojo.ScriptExecutionMaster;
import org.apache.ibatis.annotations.*;


@Mapper
public interface ScriptExecutionMasterMapper {

    int insert(ScriptExecutionMaster entity);
}

6.mapper.xml代码如下(只举例其中一个)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jlzh.lftwebservice.dao.mapper.master.ScriptExecutionMasterMapper">
    <resultMap id="BaseResultMap" type="com.jlzh.lftwebservice.dao.pojo.ScriptExecutionMaster">
        <id column="executionID" jdbcType="INTEGER" property="executionID" />
        <result column="scriptCode" jdbcType="VARCHAR" property="scriptCode" />
        <result column="scriptVersion" jdbcType="VARCHAR" property="scriptVersion" />
        <result column="status" jdbcType="VARCHAR" property="status" />
        <result column="testCount" jdbcType="VARCHAR" property="testCount" />
        <result column="testMax" jdbcType="VARCHAR" property="testMax" />
        <result column="applicant" jdbcType="VARCHAR" property="applicant" />
        <result column="threadSafe" jdbcType="VARCHAR" property="threadSafe" />
        <result column="creationDate" jdbcType="DATE" property="creationDate" />
    </resultMap>

    <insert id="insert" parameterType="com.jlzh.lftwebservice.dao.pojo.ScriptExecutionMaster">
        <selectKey keyProperty="executionID" keyColumn="user_id" order="AFTER" resultType="integer">
            SELECT MAX(EXECUTIONID) FROM E2S_SCRIPTEXECUTION
        </selectKey>
        INSERT INTO E2S_SCRIPTEXECUTION (EXECUTIONID, SCRIPTCODE, SCRIPTVERSION, STATUS, TESTCOUNT, TESTMAX, APPLICANT, THREADSAFE, CREATIONDATE)
        values ((SELECT MAX(EXECUTIONID)+1 FROM E2S_SCRIPTEXECUTION), #{scriptCode, jdbcType=VARCHAR}, #{scriptVersion, jdbcType=VARCHAR}, #{status, jdbcType=VARCHAR}, #{testCount, jdbcType=VARCHAR}, #{testMax, jdbcType=VARCHAR}, #{applicant, jdbcType=VARCHAR}, #{threadSafe, jdbcType=VARCHAR}, #{creationDate, jdbcType=DATE})
    </insert>

    <select id="selectAll" resultMap="BaseResultMap">
        select EXECUTIONID, SCRIPTCODE, SCRIPTVERSION, STATUS, TESTCOUNT, TESTMAX, APPLICANT, THREADSAFE, CREATIONDATE
        from E2S_SCRIPTEXECUTION
    </select>
</mapper>

7.事务管理方法类

package com.jlzh.lftwebservice.dao.bean;

import com.jlzh.lftwebservice.dao.pojo.ImportEntitiesSecondary;
import com.jlzh.lftwebservice.dao.pojo.ImportTransfersSecondary;
import com.jlzh.lftwebservice.dao.pojo.ImportValuesSecondary;
import com.jlzh.lftwebservice.dao.pojo.ScriptExecutionMaster;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: SAPServerBean
 * @ Description: 处理所有中间表数据
 * @author:王红艳
 * @date:2022/10/22
 */
@Service
public class SAPServerBean {
    @Autowired
    @Qualifier("masterTransactionTemplate")
    TransactionTemplate masterTransactionTemplate;

    @Autowired
    @Qualifier("secondaryTransactionTemplate")
    TransactionTemplate secondaryTransactionTemplate;

    public void addValueData(String woCode, String lotCode, String sapUnit, long sapQty){
        masterTransactionTemplate.execute((status1) ->{
            secondaryTransactionTemplate.execute((status2)->{
                try {
                    I//此处填写事务处理方法
                }catch (Exception e){
                    e.printStackTrace();
                    status1.setRollbackOnly();  // 事务1回滚
                    status2.setRollbackOnly();  // 事务2回滚
                }
                return true; // 事务2提交
            });
            return true;    // 事务1提交
        });
    }
}

8.手动调用工具类代码

package com.jlzh.lftwebservice.common;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @ClassName: SpringContextUtils
 * @ Description: Spring容器工具类
 * @author:王红艳
 * @date:
 */
@Component("springContextUtils")
public class SpringContextUtils implements ApplicationContextAware {
    /**
     * Spring应用上下文环境
     */
    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的回调方法,设置上下文环境
     *
     * @param applicationContext
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * @return ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object
     * @throws BeansException
     */
    public static Object getBean(String name) throws BeansException {
        return applicationContext.getBean(name);
    }

    /**
     * 根据class获取对应的bean对象
     * @param clz
     * @return
     */
    public static Object getBean(Class<?> clz){
        return applicationContext.getBean(clz);
    }
}

9.webservice调用方法代码

package com.jlzh.lftwebservice.service.sap.server.impl;

import com.jlzh.lftwebservice.common.SpringContextUtils;
import com.jlzh.lftwebservice.dao.bean.*;
import com.jlzh.lftwebservice.dao.pojo.ImportEntitiesSecondary;
import com.jlzh.lftwebservice.dao.pojo.ImportTransfersSecondary;
import com.jlzh.lftwebservice.dao.pojo.ImportValuesSecondary;
import com.jlzh.lftwebservice.dao.pojo.ScriptExecutionMaster;
import com.jlzh.lftwebservice.service.sap.server.ISAPServerService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: ISAPServerServiceImpl
 * @ Description: 与sap交互服务端实现
 * @author:
 * @date:
 */

@WebService
public class ISAPServerServiceImpl implements ISAPServerService {

    SAPServerBean sAPServerBean=(SAPServerBean) SpringContextUtils.getBean(SAPServerBean.class);
    @Override
    public void receiveWorkOrder(@WebParam(name = "BOM_CODE") String bomCode                                 
    ) {
        sAPServerBean.addValueData(woCode, lotCode, sapUnit, sapQty);
    }

}

注:编程式事务配置类
SpringBoot+MyBatisPlus+Oracle双数据源在WebService接口事务生效_第2张图片

你可能感兴趣的:(1024程序员节,spring,boot,idea,oracle,mybatis)