Java使用Jco连接SAP操作详解

        最近加入的新项目需要和SAP进行接口对接,想尝试将项目代码中关于SAP连接部分剥离出来,可能存在些错误,欢迎大家指正!!!

        项目环境:Windows, IDEA 2023.1, jdk11, Spring Boot  2.7.2

SAP Java Connector(SAP JCo)

        SAP Jco 是一个可以使JAVA应用程序能够通过SAP的RFC协议与SAP进行通信的开发库。SAP JCo支持双向通信:入站远程函数调用(Java调用ABAP)以及出站远程函数调用(ABAP调用Java)。JCo同时也支持Connection Pools和Direct两种方式的连接。直接连接需要开发者来控制连接的创建和释放,使用连接池方式可以让池来管理连接的分配、管理和释放 。

 1.下载

        官网地址:    SAP Java Connector,里面有详细的操作步骤和一些要求(因为项目是web开发我就没有进行下载,直接进行了下一步操作)

2.安装

     Wriprin's Cloudreve 相关文件下载链接

  • 在 Windows 服务器中,将 sapjco3.jar 和 sapjco3.dll 文件放到 项目的 lib 文件夹。
  • 在 Linux 服务器中,将 sapjco3.jar 和 libsapjco3.so 放到 项目的 lib 文件夹中。
  • 在pom中引入依赖,引入jar包
     <dependency>
         <groupId>com.sap</groupId>
         <artifactId>sapjco3</artifactId>
         <version>1.0</version>
         <scope>system</scope>
         <systemPath>${project.basedir}/lib/sapjco3.jar</systemPath>
     </dependency>

连接SAP

这个版本是比较复杂的,为了解耦合将很多内容进行了拆分,但是认真看也能看到懂,目录结构也是比较干净的,纯净版无废话版本(等找个时间再撸...)

1.编辑yml
  • 根据SAP信息编辑application.yml,同时在为它写一个文件用于读取这个配置,注意保证前缀的对应

2.创建一个配置对象

内容上其实和配置文件上高度重合的,与配置文件不同的是多了个defaultkey,用于记录自定义的destinationName

3.创建一个类实现DestinationDataProvider

        DestinationDataProvider相比较于存储在xxx.jcodestination文件中可以更灵活的存放SAP连接参数,在同时还能存放了多个destination。

        在实现这个类的同时,必须完成两件事:

①:改写getDestinationProperties()

②:通过Environment.registerDestinationDataProvide() 方法进行注册,使其生效

这个代码主要可以分为四部分,可以根据自己的需求书写:

(1)为了实现线程安全加入了内部类的懒汉单例模式

  (2)  注册

(3)重写getDestinationProperties(),这部分重写是因为我把destination以hashMap的方式存储了,key=settings.defaultKey

(4)创建destination,并且放入hashMap中

package com.example.jcodemo.config;

import com.example.jcodemo.client.semaphore.JCoClientCreatedOnErrorSemaphore;
import com.sap.conn.jco.ext.*;
import org.apache.commons.lang.StringUtils;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

/**
 * FileName: JCoDataProvider
 * @Author:  Miao
 * Date:    2024/3/14:11:42
 * Description: JCo destination and server data provider
 *  将连接变量信息存放在内存里,安全性更高
 */
public class JCoDataProvider implements DestinationDataProvider {

    /**
     * 定义成员变狼
     */
    private final Map<String, Properties> clientSettingsProviders;
    private  DestinationDataEventListener destinationDataEventListener;

    /**
     * 初始化成员
     */
    private JCoDataProvider() {
        this.clientSettingsProviders = new ConcurrentHashMap<>();
    }

    /**
     * 利用内部类懒汉加载的单例模式,保证只有一个实例对象
     */
    private static class JCoDataProviderInstance {
        private static final JCoDataProvider INSTANCE = new JCoDataProvider();
    }

    public static JCoDataProvider getSingleton() {  // singleton
        return JCoDataProviderInstance.INSTANCE;
    }

    /**
     * 注册provider,使其生效
     */
    public static void registerInEnvironment(){
        Environment.registerDestinationDataProvider(JCoDataProviderInstance.INSTANCE);
    }

    /**
     * 重写getDestinationProperties()方法,后面用来获取和执行SAP 函数的时候会用到,所以一定要写对
     * @param destinationName
     * @return
     */
    @Override
    public Properties getDestinationProperties(String destinationName) {
        if(clientSettingsProviders.containsKey(destinationName)){
            return clientSettingsProviders.get(destinationName);
        }
        return null;
    }

    @Override
    public boolean supportsEvents() {
        return false;
    }

    @Override
    public void setDestinationDataEventListener(DestinationDataEventListener destinationDataEventListener) {
        this.destinationDataEventListener = destinationDataEventListener;
    }

    /**
     * 创建destination,并且放到成员变量中
     * @param settings
     */
    public void registerClientSettings(JCoSettings settings) {
        clientSettingsProviders.compute(settings.getDefaultKey(), (clientName, clientSettings) -> {
            // exist check
            if (clientSettings != null) {
                throw new RuntimeException("Destination: [" + clientName + "] has been already registered.");
            }
            // refer
            return referDestinationData(settings);
        });
    }

    public static Properties referDestinationData
            (JCoSettings settings){
        Properties properties = new Properties();

        if(StringUtils.isNotEmpty( settings.getAshost())){
            properties.setProperty(DestinationDataProvider.JCO_ASHOST,settings.getAshost());
        }
        properties.setProperty(DestinationDataProvider.JCO_SYSNR, settings.getSysnr());
        properties.setProperty(DestinationDataProvider.JCO_CLIENT, settings.getClient());
        properties.setProperty(DestinationDataProvider.JCO_USER, settings.getUser());
        properties.setProperty(DestinationDataProvider.JCO_PASSWD,settings.getPassword());
        properties.setProperty(DestinationDataProvider.JCO_LANG, settings.getLanguage());
        // pool settings
        properties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, settings.getPoolCapacity());
        properties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, settings.getPeakLimit());

        //zhengqh
        if (StringUtils.isNotEmpty(settings.getMshost())) {
            properties.setProperty(DestinationDataProvider.JCO_MSHOST, settings.getMshost());
        }
        if (StringUtils.isNotEmpty(settings.getGroup())) {
            properties.setProperty(DestinationDataProvider.JCO_GROUP, settings.getGroup());
        }
        if  (StringUtils.isNotEmpty(settings.getR3name())) {
            properties.setProperty(DestinationDataProvider.JCO_R3NAME, settings.getR3name());
        }

        if  (StringUtils.isNotEmpty(settings.getMsserv())) {
            properties.setProperty(DestinationDataProvider.JCO_MSSERV, settings.getMsserv());
        }
        return  properties;
    }


}
4.创建一个JCoClient对象

        用于管理和SAP系统的一些列操作(初始化连接,释放,获取配置,函数处理等等),并且在启动程序中直接注入到Spring容器中,后面就可以直接使用啦

package com.example.jcodemo.client;

import com.example.jcodemo.config.JCoDataProvider;
import com.example.jcodemo.config.JCoSettings;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.JCoFunction;

/**
 * FileName: DefaultJCoClient
 * @Author:  Miao
 * Date:    2024/3/13:15:03
 * Description: Deafault implement of {@link JCoClient}
 */
public class JCoClient{

    private final JCoSettings settings;


    public JCoClient(JCoSettings settings) {
        this.settings = settings;
        initJCoConnection(settings);
    }

    /**
     * 创建client
     * @param settings {@link JCoSettings}
     */
    public static void initJCoConnection(JCoSettings settings)  {
        try {
            // register client properties.
            JCoDataProvider.getSingleton().registerClientSettings(settings);

            //ping test
            JCoDestinationManager
                    .getDestination(settings.getDefaultKey())
                    .ping();
        } catch (JCoException e) {
            throw new RuntimeException("Unable to create:["+settings.getDefaultKey()+"]",e);
        }

    }
    /**
     * get Jco destination
     * @return The destination {@link JCoDestination}
     */

    public JCoDestination getDestination()  {
        try {
            return JCoDestinationManager
                    .getDestination(this.settings.getDefaultKey());
        }catch (JCoException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Release a connection
     */

    public void release() {
        JCoDataProvider.getSingleton().unRegisterClientSettings(settings.getDefaultKey());
    }

    /**
     * Get client configuration
     * @return The configuration {@link JCoSettings}
     */

    public JCoSettings getSettings() {
        return this.settings;
    }


    public void close() throws Exception {
        this.release();
    }


}

5.调用RFC函数

RFC函数传入参数接口/返回接口的方法有较多,想要了解更多的可以阅读官方文档SAP Java Connector (Netweaver 2004S SPS 09)

package com.example.jcodemo.demos.web;

import com.example.jcodemo.client.JCoClient;
import com.example.jcodemo.demos.web.Pojo.ZWMSI01Res;
import com.sap.conn.jco.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
 */
@Controller
public class BasicController {
    @Autowired
    private JCoClient client;

    // http://127.0.0.1:8080/hello?name=lisi
    @RequestMapping("/executeFunction")
    @ResponseBody
    public String executeFunction(@RequestParam(name = "name", defaultValue = "ZWMSI01") String name) throws JCoException {
        //1.获取函数RFC函数对象
        JCoFunction function = client.getDestination().getRepository().getFunctionTemplate(name).getFunction();

        //2.设置输入参数(如果有)
        // JCoParameterList importParameterList = function.getImportParameterList();
        // importParameterList.setValue("I_FLAG","X");

        //3.调用并获取返回值
        JCoResponse response = new DefaultRequest(function).execute(client.getDestination());

        //4.封装返回值
        Map<String, Object> invokeResult  = new HashMap<>();
        for (JCoField jCoField : response) {
            String fieldName = jCoField.getName();
            JCoTable tableValue = function.getTableParameterList().getTable(fieldName);

            if(tableValue.isEmpty()){
                invokeResult.put(fieldName,null);
                break;
            }
            ArrayList<ZWMSI01Res> resArrayList = new ArrayList<>();
            ZWMSI01Res zwmsi01Res = new ZWMSI01Res();
            for (int i = 0; i < tableValue.getNumColumns(); i++) {
                tableValue.setRow(i);

                zwmsi01Res.setWerks(tableValue.getString("WERKS"));
                zwmsi01Res.setVkorg(tableValue.getString("VKORG"));
                zwmsi01Res.setName(tableValue.getString("NAME1"));
                resArrayList.add(zwmsi01Res);
            }
            invokeResult.put(fieldName,resArrayList);
        }
        return invokeResult.toString();
    }

    static class DefaultRequest extends com.sap.conn.jco.rt.DefaultRequest {
        DefaultRequest(JCoFunction function) {
            super(function);
        }
    }

}

  • 12
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值