使用 SAP APM 和 S/4HANA Cloud SDK 时 OData 导航 returns 异常

OData navigation returns exception when using SAP APM and S/4HANA Cloud SDK

我在 SAP Web IDE 全栈中创建了一个 APM 业务应用程序。

S/4HANA SDK 读取业务合作伙伴 S/4HANA API。自定义业务伙伴数据保存在 SAP Cloud Platform HDI 容器中。业务伙伴实体与自定义实体具有 0..1 关联。

当自定义数据存在时 OData 导航 属性 成功,但当自定义数据不存在时 return 出现空指针异常。 OData $expand 在这两种情况下都不成功,returning error while trying to invoke the method java.util.Map.size() of a null object loaded from local variable 'm'。我可能错误地期望关联实体 return 键 属性 的值而不是其他属性的值。

以下是数据模型、服务、业务合作伙伴操作、错误和日志。

数据模型CDS

entity BusinessPartner {
    Key BusinessPartner     : String(10);
    LastName                : String(40);
    FirstName               : String(40);
    status                  : Association to PartnerStatus;
}

entity PartnerStatus {
    Key BusinessPartner     : String(10);
    StatusConfirmed         : String(1);
}

服务 CDS

service BusinessPartnerService {
    @cds.persistence.skip
    entity BusinessPartner @readonly as projection on s4c.BusinessPartner;

    entity PartnerStatus   @readonly as projection on s4c.PartnerStatus;
}

业务合作伙伴@Query 和@Read

@Query(serviceName = "BusinessPartnerService", entity = "BusinessPartner")
public QueryResponse queryPartners(QueryRequest queryRequest) throws ServletException {
    final List<BusinessPartner> businessPartners;

    try {
        businessPartners = new DefaultBusinessPartnerService()
            .getAllBusinessPartner()
            .top(5)
            .execute();
    } catch (ODataException e) {
        throw new ServletException(e);
    }

    QueryResponse queryResponse = QueryResponse.setSuccess().setData(businessPartners).response();

    return queryResponse;
}

@Read(serviceName = "BusinessPartnerService", entity = "BusinessPartner")
public ReadResponse readPartner(ReadRequest readRequest) throws ServletException {
    String id = String.valueOf(readRequest.getKeys().get("BusinessPartner"));

    final BusinessPartner businessPartner;

    try {
        businessPartner = new DefaultBusinessPartnerService()
            .getBusinessPartnerByKey(id)
            .execute();
    } catch (ODataException e) {
        throw new ServletException(e);
    }

    ReadResponse readResponse = ReadResponse.setSuccess().setData(businessPartner).response();

    return readResponse;
}

错误

<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <code>CDSRuntimeException.NO_ENTITY_FOUND</code>
    <message xml:lang="en-US">No Entity found for the URL.</message>
</error>

日志

  2018-11-06T11:02:50.53-0500 [APP/PROC/WEB/0] OUT { "written_at":"2018-11-06T16:02:50.538Z","written_ts":1056610886992277,"component_id":"df464a53-1905-4c4b-bcc5-ef59761d2218","component_name":"ge2qqww1rr7SxSEK-apm-s4c-srv","DCComponent":"","organization_name":"-","component_type":"application","space_name":"dev","component_instance":"0","organization_id":"-","correlation_id":"-","CSNComponent":"","space_id":"5e8e1eb0-f9ca-40d8-8ba9-1880ca345628","Application":"ge2qqww1rr7SxSEK-apm-s4c-srv","container_id":"10.0.137.3","type":"log","logger":"com.sap.cloud.sdk.service.prov.v2.rt.core.CloudSDKODataErrorCallback","thread":"http-nio-0.0.0.0-3000-exec-2","level":"ERROR","categories":[],"msg":"Request URL: https://ge2qqww1rr7sxsek-apm-s4c-srv.cfapps.us10.hana.ondemand.com/odata/v2/BusinessPartnerService/BusinessPartner?$expand=status\nStatusCode:500","stacktrace":["java.lang.NullPointerException: while trying to invoke the method java.util.Map.size() of a null object loaded from local variable 'm'","\tat java.util.HashMap.putMapEntries(HashMap.java:501)","\tat java.util.HashMap.putAll(HashMap.java:785)","\tat com.sap.gateway.core.api.provider.data.BaseDataProvider.getSingleNavigationPathResult(BaseDataProvider.java:837)","\tat com.sap.gateway.core.api.provider.data.BaseDataProvider.readExpandedEntitySet(BaseDataProvider.java:915)","\tat com.sap.cloud.sdk.service.prov.v2.data.provider.CXSDataProvider.readExpandedEntitySet(CXSDataProvider.java:724)","\tat com.sap.cloud.sdk.service.prov.v2.rt.data.provider.HybridDataProvider.readExpandedEntitySet(HybridDataProvider.java:298)","\tat com.sap.gateway.core.api.provider.data.GenericODataProcessor.readEntitySet(GenericODataProcessor.java:895)","\tat org.apache.olingo.odata2.core.Dispatcher.dispatch(Dispatcher.java:77)","\tat org.apache.olingo.odata2.core.ODataRequestHandler.handle(ODataRequestHandler.java:131)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.handleRequest(ODataServlet.java:216)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.handle(ODataServlet.java:115)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.service(ODataServlet.java:85)","\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:742)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.lambda$doFilter[=16=](RequestContextServletFilter.java:171)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.call(RequestContextCallable.java:95)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.doFilter(RequestContextServletFilter.java:173)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)","\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)","\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)","\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)","\tat com.sap.xs.java.valves.ErrorReportValve.invoke(ErrorReportValve.java:66)","\tat ch.qos.logback.access.tomcat.LogbackValve.invoke(LogbackValve.java:191)","\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)","\tat com.sap.xs.jdbc.datasource.valve.JDBCValve.invoke(JDBCValve.java:62)","\tat com.sap.xs.security.UserInfoValve.invoke(UserInfoValve.java:19)","\tat com.sap.xs.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:43)","\tat com.sap.xs.logging.catalina.RuntimeInfoValve.invoke(RuntimeInfoValve.java:40)","\tat org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:685)","\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)","\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)","\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)","\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)","\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)","\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)","\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)","\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)","\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)","\tat java.lang.Thread.run(Thread.java:836)"] }

远程实体和本地实体之间的连接不会自动发生。您必须使用关联实体 PartnerStatus 的读取操作手动实现此操作,该关联实体还指定导航 BusinessPartner 的源实体。 此方法如下所示(如您所见,在此示例中我总是返回虚拟数据):

@Read(serviceName = "BusinessPartnerService",
    entity = "PartnerStatus",
    sourceEntity = "BusinessPartner")
public ReadResponse readPartnerStatus(ReadRequest readRequest) throws ServletException {
    return ReadResponse.setSuccess().setData(
            ImmutableMap.of("BusinessPartner", "1003764", "StatusConfirmed", "X"))
        .response();
}

documentation about the Read operation 包含有关此概念的更多详细信息。

您现在需要访问数据库,例如,在处理程序中使用 JPA(我还没有尝试过)。该文档有一节关于 Using JPA in Custom Handlers.

我使用 CDSDataSourceHandler 读取本地实体。

如果本地实体存在,则返回。如果本地实体不存在,则将密钥从源实体传递给实体。

private static Connection getConnection() {
    Connection conn = null;
    Context ctx;
    try {
        ctx = new InitialContext();
        conn = ((DataSource) ctx.lookup("java:comp/env/jdbc/java-hdi-container")).getConnection();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return conn;
}

@Read(serviceName = "BusinessPartnerService", entity = "PartnerStatus", sourceEntity = "BusinessPartner")
public ReadResponse readPartnerStatus(ReadRequest readRequest) {
    CDSDataSourceHandler dsHandler = DataSourceHandlerFactory.getInstance().getCDSHandler(getConnection(), readRequest.getEntityMetadata().getNamespace());

    EntityData ed = null;

    try {
        ed = dsHandler.executeRead(readRequest.getEntityMetadata().getName(), readRequest.getKeys(), readRequest.getEntityMetadata().getElementNames());
    } catch (CDSException e) { }

    if (ed != null) {
        return ReadResponse.setSuccess().setData(ed).response();

    } else {
        Map<String, Object> keys = readRequest.getKeys();
        Object keyObject = keys.get("BusinessPartner");
        String id = (String)keyObject;

        return ReadResponse.setSuccess().setData(ImmutableMap.of("BusinessPartner", id, "StatusConfirmed", "X")).response();
    }
}

OData $expand 对实体和实体集都是成功的。