设为首页 收藏本站
查看: 1128|回复: 0

[经验分享] [SAP ABAP开发技术总结]BAPI调用

[复制链接]
发表于 2015-9-18 11:42:06 | 显示全部楼层 |阅读模式
DSC0000.gif
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4265542.html   
18.3.2.5.           调用BAPI199
18.3.2.5.1.       BAPI事务处理... 200
18.3.2.5.2.       外部系统(Java)调用BAPI函数... 201
18.3.2.5.2.1.    直连、连接池... 201
18.3.2.5.2.2.    访问结构... 202
18.3.2.5.2.3.    访问表 (Table)203
18.3.2.5.2.4.    Java多线程调用有/无状态RFM.. 204
18.3.2.5.3.       ABAP访问Java服务... 204
18.3.2.5.4.       ABAP创建远程目标... 204
18.3.2.5.5.       连接异常registrationnot allowed. 205
18.3.2.5.6.       带状态访问... 206
18.3.2.5.       调用BAPI
SAP系统提供的BAPI的参数结构有个特点:一般会将类似的字段放在同一个结构中,同时,还会存在一个与该结构名类似(后面以X结尾)标识结构,该标识结构中的字段名与赋值的结构中的字段名一致,但是其字段类型只是一个长度为1的字符,用于标识某个字段的数据是否需要通过BAPI来变更
18.3.2.5.1.              BAPI事务处理
根据事务的ACID原则,一个独立的BAPI实现必须具有事务性,同时,BAPI事务模型还必须允许开发者在调用多个BAPI时可以将它们绑定到同一个LUW上。因此,如果同时调用几个BAPI,开发者需要在程序中进行的事务控制,决定何时执行数据库提交或回滚操作;BAPI内部则通常不包含COMMIT WORK和ROLLBACK WORK命令

操作多个BAPI时必须遵循以下原则:
l  如果有更新操作的BAPI,如创建、修改或删除一个业务对象实例,则对该实例进行另外的读取操作的BAPI只能访问上一个COMMIT WORK执行后的最新数据
l  在同一个LUW中,不能对同一个业务对象实例时行超过一次的更新操作。例如,不允许在一个LUW中创建一个新实例,随后就修改它。但可以创建多个相同的类型的不同实例

在BAPI内部,数据库更新操作必须通过同步或异步的更新过程实现(需使用 CALL FUNCTION update_function IN UPDATE TASK的方式来更新数据库),因为否则可能出现不必要的数据库提交过程,从而破坏了BAPI调用的ACID原则。同样原因,BAPI内部也不能触发新的LUW,因而其内部程序代码中不能包含以下命令:

l  CALL TRANSACTION
l  SUBMIT REPORT
l  SUBMIT REPORT AND RETURN
l  COMMIT WORK
l  ROLLBACK WORK

因此,BAPI事务中的数据库提交和回滚必须在主调程序中通过调用SAP标准业务对象BapiService(业务对象类型为SAP0001)的BAPI方法BapiService.TransactionCommit(此BAPI方法实际上还是通过调用BAPI函数BAPI_TRANSACTION_COMMIT来实现的)和BapiService.TransactionRollback(此BAPI方法实际上还是通过调用BAPI函数BAPI_TRANSACTION_ROLLBACK来实现的)来完成

外部程序直接到调用BapiService.TransactionCommit方法,才会触发BAPI方法中的数据库提交


对于BAPI的操作都要用BAPI_TRANSACTION_COMMIT来提交的,在提交前,要根据BAPI函数的执行返回参数RETURN来判断函数是否执行成功(RETURN中是否有E类消息),如果有错误消息则要用BAPI_TRANSACTION_ROLLBACK取消所做的操作,而不是COMMIT WORK,如:

  CALL FUNCTION 'BAPI_FIXEDASSET_CHANGE'
...
    IMPORTING
      return       = return.
  IF return-type <> 'S'.
    CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
  ELSE.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      EXPORTING
        wait = 'X'.
  ENDIF.

另外建议在调用BAPI_TRANSACTION_COMMIT函数进行提交BAPI操作时,加上wait参数,这样直到BAPI函数中的数据库操作提交数据库后,才去执行其后面的语句,这样后面程序依赖于此提交的数据时就不会出问题:  

  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    EXPORTING
      wait = 'X'.
WAIT为X时,会执行COMMIT WORK AND WAIT语句,否则执行COMMIT WORK语句
18.3.2.5.2.              外部系统(Java)调用BAPI函数
18.3.2.5.2.1.      直连、连接池
import java.io.File;

import java.io.FileOutputStream;

import java.util.Properties;

import com.sap.conn.jco.JCoDestination;?dest??ne??n

import com.sap.conn.jco.JCoDestinationManager;

import com.sap.conn.jco.JCoException;

import com.sap.conn.jco.ext.DestinationDataProvider;


publicclass ConnectNoPool {// 直连方式,非连接池

// 连接属性配置文件名,名称可以随便取

    static String ABAP_AS = "ABAP_AS_WITHOUT_POOL";

    static {

        Properties connectProperties = new Properties();

        connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST,

                "192.168.111.137");

        connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "00");

        connectProperties

                .setProperty(DestinationDataProvider.JCO_CLIENT, "800");

        connectProperties.setProperty(DestinationDataProvider.JCO_USER,

                "SAPECC");

        // 注:密码是区分大小写的,要注意大小写

        connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD,

                "sapecc60");

        connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "en");
      // *********连接池方式与直接不同的是设置了下面两个连接属性

       // JCO_PEAK_LIMIT - 同时可创建的最大活动连接数,0表示无限制,默认为JCO_POOL_CAPACITY的值

       // 如果小于JCO_POOL_CAPACITY的值,则自动设置为该值,在没有设置JCO_POOL_CAPACITY的情况下为0

       connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT,

              "10");

       // JCO_POOL_CAPACITY - 空闲连接数,如果为0,则没有连接池效果,默认为1

       connectProperties.setProperty(

              DestinationDataProvider.JCO_POOL_CAPACITY, "3");


        // 需要将属性配置保存属性文件,该文件的文件名为 ABAP_AS_WITHOUT_POOL.jcoDestination,

        // JCoDestinationManager.getDestination()调用时会需要该连接配置文件,后缀名需要为jcoDestination

        createDataFile(ABAP_AS, "jcoDestination", connectProperties);

    }


    // 基于上面设定的属性生成连接配置文件

    staticvoid createDataFile(String name, String suffix, Properties properties) {

        File cfg = new File(name + "." + suffix);

        if (!cfg.exists()) {

            try {

                FileOutputStream fos = new FileOutputStream(cfg, false);

                properties.store(fos, "for tests only !");

                fos.close();

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }


    publicstaticvoid connectWithoutPool() throws JCoException {

        // 到当前类所在目录中搜索 ABAP_AS_WITHOUT_POOL.jcoDestination

        // 属性连接配置文件,并根据文件中的配置信息来创建连接

        JCoDestination destination = JCoDestinationManager

                .getDestination(ABAP_AS);// 只需指定文件名(不能带扩展名jcoDestination名,会自动加上)

        System.out.println("Attributes:");

        // 调用destination属性时就会发起连接,一直等待远程响应

        System.out.println(destination.getAttributes());

    }


    publicstaticvoid main(String[] args) throws JCoException {

        connectWithoutPool();

    }

}

18.3.2.5.2.2.      访问结构
   JCoDestination destination = JCoDestinationManager

       .getDestination(ABAP_AS);

    JCoFunction function = destination.getRepository().getFunction(

       "RFC_SYSTEM_INFO");//从对象仓库中获取 RFM 函数

    function.execute(destination);

   

    JCoStructure exportStructure = function.getExportParameterList()

       .getStructure("RFCSI_EXPORT");

   

    for (int i = 0; i < exportStructure.getMetaData().getFieldCount(); i++) {

        System.out.println(exportStructure.getMetaData().getName(i) + ":\t"

           + exportStructure.getString(i));

    }

    System.out.println();

    // 也可以使用下面的方式来遍历

    for (JCoField field : exportStructure) {

        System.out.println(field.getName() + ":\t" + field.getString());

    }

    //*********也可直接通过结构中的字段名或字段所在的索引位置来读取某个字段的值

    System.out.println(exportStructure.getString(0));

    System.out.println(exportStructure.getString("RFCPROTO"));

18.3.2.5.2.3.      访问表 (Table)
   JCoDestination destination = JCoDestinationManager

       .getDestination(ABAP_AS);

    JCoFunction function = destination.getRepository().getFunction(

       "BAPI_COMPANYCODE_GETLIST");//从对象仓库中获取 RFM 函数:获取公司列表

    function.execute(destination);

   

    JCoStructure returnStructure = function.getExportParameterList()

       .getStructure("RETURN");

    //判断读取是否成功

    if (!(returnStructure.getString("TYPE").equals("") || returnStructure

       .getString("TYPE").equals("S"))) {

        throw new RuntimeException(returnStructure.getString("MESSAGE"));

    }

    //获取Table参数:COMPANYCODE_LIST

    JCoTable codes = function.getTableParameterList().getTable(

       "COMPANYCODE_LIST");

    for (int i = 0; i < codes.getNumRows(); i++) {//遍历Table

        codes.setRow(i);//将行指针指向特定的索引行

        System.out.println(codes.getString("COMP_CODE") + '\t'

           + codes.getString("COMP_NAME"));

    }

    // move the table cursor to first row

    codes.firstRow();//从首行开始重新遍历 codes.nextRow():如果有下一行,下移一行并返回True

    for (int i = 0; i < codes.getNumRows(); i++, codes.nextRow()) {

    //进一步获取公司详细信息

        function = destination.getRepository().getFunction(

       "BAPI_COMPANYCODE_GETDETAIL");

        function.getImportParameterList().setValue("COMPANYCODEID",

           codes.getString("COMP_CODE"));

    // We do not need the addresses, so set the corresponding parameter

    // to inactive.

    // Inactive parameters will be either not generated or at least

    // converted. 不需要返回COMPANYCODE_ADDRESS参数(但服务器端应该还是组织了此数据,只是未经过网络传送?)

        function.getExportParameterList().setActive("COMPANYCODE_ADDRESS",

       false);

       function.execute(destination);

      

        returnStructure = function.getExportParameterList().getStructure(

       "RETURN");

       if (!(returnStructure.getString("TYPE").equals("")

           || returnStructure.getString("TYPE").equals("S") || returnStructure

           .getString("TYPE").equals("W"))) {

            throw new RuntimeException(returnStructure.getString("MESSAGE"));

        }

        JCoStructure detail = function.getExportParameterList()

           .getStructure("COMPANYCODE_DETAIL");

        System.out.println(detail.getString("COMP_CODE") + '\t'

           + detail.getString("COUNTRY") + '\t'

           + detail.getString("CITY"));

    }// for

18.3.2.5.2.4.      Java多线程调用有/无状态RFM
有状态调用:指多次调用某个程序(如多次调用某个RFC函数、调用某个函数组中的多个不同的RFC函数、及BAPI函数——因为BAPI函数也是一种特殊的具有RFC功能的函数,它也有自己的函数组)时,在这多次调用过程中,程序运行时的内存状态(即全局变量的值)可以在每次调用后保留下来,供下一次继续使用,而不是每次调用后,程序所在的内存状态被清除。这种调用适用于那些使用到函数组中的全局变量的RFC函数的调用
无状态调用:每次的调用都是独立的一次调用(上一次调用与当前以及下一次调用之间不会共享任何全局变量),调用后不会保留内存状态,这种调用适用于那些没有使用到函数组中的全局变量的RFC函数调用

如果主调程序为Java,有状态调用的前提是:
l  多次调用RFC函数时,Java端要确保每次调用所使用的连接与上次是同一个(应该不需要是同一物理连接,只需要确保是同一远程会话,从下面演示程序来看,用的是连接池,但同一任务执行时并未去特意使用同一物理连接去发送远程调用,而只是要求是同一远程会话)
l  ABAP端需要在每次调用后,保留每一次被调用后函数组的内存状态,直到最后一次调用完成止,这需要Java与ABAP配合来完成(Java在第一次调用时,调用JCoContext.beginJCoContext.end这两个方法,告诉SAP这一调用过程将是有状态调用,需要保留内存状态,然后SAP端就会自动保留内存状态)

如果主调程序是ABAP(即ABAP程序调用ABAP函数),此种情况下没有特殊的要求,直接调用就即可,只要是在同一程序的同一运行会话其间(会话相当于Java中的同一线程吧),不管是多次调用同一个函数、还是调用同一函数组中的不同函数,则都会自动保留内存状态,直到程序运行结束,这是系统自己完成的。一个函数组好比一个类,函数组中不同的函数就相当于类中不同的方法、全局变量就相当于类中的属性,所以只要是在同一程序的同一运行会话期间,调用的同一函数所在的函数组中的全局变量都是共享的,就好比调用一类的某个方法时,该方法设置了某个类的属性,再去调用该类的其它方法时,该属性值还是保留了以前其它方法修改后的状态值。

状态调用只要保证同一Java线程中多次远程方法调用采用的都是同一会话即可
18.3.2.5.3.              ABAP访问Java服务
18.3.2.5.4.              ABAP创建远程目标
SAP通过JCO反向调用JAVA的RFC服务其实也是相对简单的,只是在JAVA端需要使用JCO创建一个RFC服务,然后在SAP端注册这个服务程序。
首先,JCo服务器程序需在网关中进行注册,在SM59中,定义一个连接类型为T的远程目标
DSC0001.png
DSC0002.png
RFC目标系统:是ABAP RFC调用Java时,需指定的目标系统名。
Program ID:是JAVA程序中使用的
DSC0003.png
Gateway Host与Gateway service值来自以下界面(Tcode:SMGW):

DSC0004.png
TCP服务sapgw是固定的,后面的00就是系统编号
所有配置好且Java服务器代码跑起来后,点击“Connection Test”按钮,如不出现红色文本,则表示链接成功(注:此时需要ServerDataProvider.JCO_PROGID设置的Program ID要与SM59中设置的相同,否则测试不成功。另要注意的是:即使Java服务器设置的Program ID乱设置,Java服务端还是能启起来,但ABAP测试连接时会不成功,也就代表ABAP不能调用Java)
18.3.2.5.5.              连接异常registrationnot allowed
Java服务启动时,如出现以下异常,则需在SAP中修改网关参数:
com.sap.conn.jco.JCoException: (113) JCO_ERROR_REGISTRATION_DENIED: CPIC-CALL: SAP_CMACCPTP3 on convId:         

LOCATION    SAP-Gateway on host LRP-ERP / sapgw00

ERROR       registration of tp JCOTEST from host JIANGZHENGJUN not allowed

……
通过事务码SMGW修改参数:
DSC0005.png

18.3.2.5.6.              带状态访问
              // 如果是某任务LUW中第一次调用时,则jcoServerCtx服务上下文为非状态,需设置为状态调用

              if (!jcoServerCtx.isStatefulSession()) {

                  // 设置为状态调用,这样在有状态调用的情况下,上下文中会携带会话ID

                  jcoServerCtx.setStateful(true);

                  cachedSession = new SessionContext();// 新建会话

                  // 将会话存储在映射表中,以便某个任务里每次远程调用都可以拿到同一会话

                  statefulSessions.put(jcoServerCtx.getSessionID(),

                         cachedSession);

              } else {// 非第一次调用

                  cachedSession = statefulSessions.get(jcoServerCtx

                         .getSessionID());

              }

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-115342-1-1.html 上篇帖子: BPC (9) 下篇帖子: PowerCenter Connect for SAP NetWeaver Overview
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表