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