Recently I was using a table in HANA with IDENTITY column that was just added in HANA in SP08. Lars Breddemann has a great blog post on that here. My table had a PK ID that was IDENTITY field which HANA would create automatically incremented on INSERT (ie. PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY).

The SQL for the CREATE looked like this:

CREATE COLUMN TABLE SENSOR_READING (

   SENSOR_READING_ID INTEGER   PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,

   SENSOR_ID         INTEGER   NOT NULL,

   READING           DOUBLE    NOT NULL,

   READ_DATE         TIMESTAMP NOT NULL

);

I initially used a very basic mapping in my sensor_reading.xsodata file:

[sensor_reading.xsodata]

service namespace “workshop.workshop_i826714″{

  “I826714″.”SENSOR_READING” as “sensorReading”;

}

This normally works great and the OData Service definition is simple and short. Initially I tried to do POST/CREATE to the OData service with values for only 3 of 4 columns (SENSOR_ID, READING, READ_DATE) as I assumed the PK (SENSOR_READING_ID) would be auto-generated.

Unfortunately, when testing this out I was receiving “400 Bad Request” errors. The full response is below:

<?xml version=”1.0″ encoding=”utf-8″ standalone=”yes”?>

<error

    xmlns=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata“>

    <code/>

    <message xml:lang=”en-US”>The serialized resource has an missing value for member 'SENSOR_READING_ID'.</message>

</error>

This happens because I was sending the JSON (3x columns only):

{

    “SENSOR_ID”: 1,

    “READING”: “10”,

    “READ_DATE”: “/Date(1425487218000)/”

}

What the oData service wanted was this JSON (all 4x columns) regardless if one of the columns in the table was marked as IDENTITY.

{

   “SENSOR_READING_ID” : 1,

    “SENSOR_ID”: 1,

    “READING”: “10”,

    “READ_DATE”: “/Date(1425487218000)/”

}

*** NOTE: I also tried sending a <NULL> thinking that maybe the service would replace it on CREATE, but that didn’t work either. Besides having the column/value enumerated in the JSON, since the column was marked as PRIMARY KEY, it also couldn’t be <NULL>. It had to be not null value. ***

After some searching for some ideas from other HANA/OData experts what I was resorted to was using Modification Exit with XS JavaScript via the OSDL in my *.xsodata file. In this manner I was able to send the IDENTITY column (SENSOR_READING_ID) with a ‘dummy value’ and then in *.xsodata file call out to JavaScript function to do SQL INSERT locally, ignoring the ‘dummy value’.

The final *.xsodata file looked like this with the OSDL Syntax for “using” keyword. (The documentation I believe says it can be used for ‘create’, ‘update’ and ‘delete’. I only tried ‘update’).

[sensor_reading.xsodata]

service namespace “workshop.workshop_i826714″{

  “I826714″.”SENSOR_READING” as “sensorReading” create using “workshop.workshop_i826714a:sensor_reading_create.xsjslib::sensor_reading_create”;

}

The OSDL syntax to bind XS JS to specific entity modification is:

<Package.Path>:<file>.<suffix>::<XSJS_FunctionName>

So a sample would look like this:

service { “sample.odata::table” as “Table” update using “sap.test:jsexit.xsjslib::update_instead”;}

In the *.xsjslib file, the XS JS function gets the single row table in the param and then builds a prepared statement to INSERT into the table. The param has a propery “afterTableName” which we use to columns values after temporary table name.

[sensor_reading_create.xsjslib]

function sensor_reading_create(param) {

    let after = param.afterTableName;

    let pStmt = param.connection.prepareStatement(‘select * from “‘+after+'”‘);
    pStmt.executeQuery();
    var rs = pStmt.executeQuery();

    if (rs.next()) {

        pStmt = param.connection.prepareStatement(‘insert into “I826714”.”SENSOR_READING”(“SENSOR_ID”, “READING”, “READ_DATE”) values(?, ?, ?)’);

        pStmt.setInteger(1, rs.getInteger(2));         
        pStmt.setDouble(2, rs.getDouble(3));           
        pStmt.setTimestamp(3, rs.getTimestamp(4));     

        pStmt.executeUpdate();
        pStmt.close();

        }

    rs.close();

}

Really this just allows me to call SQL INSERT in XS JS instead of using the automatic *.xsodata service mapping.

The ADVANTAGES:

  • Code works with IDENTITY columns. It gets by the OData column/entity requirement issues.
  • Developers can also add in more custom logic and SQL injections

The DISADVANTAGES:

  • PK column dummy value must be sent to server. Extra overhead/resources wasted over the wire.
  • Extra overhead @ HANA to pause and call-out XS JS function. Not sure exactly how much, but I assume it’s going to be slower.
  • A lot more code/work if you want crate OData Service Definition for tables with IDENTITY column

In summary, this will work, but not sure it’s ideal. I’m wondering if anyone knows of a better solution, such as a OSDL keyword that would allow columns to be tagged as not required on the POST/CREATE or POST/UPDATE for OData service. This would save a lot of hassle of creating your own XS JS function. If you know of solution, please reply in the comments!

If anyone has any corrections, suggestions or other feedback, please post as well.

Thanks,

w.

You can find more about Modification Exists with XS JavaScript and OSDL in general in Section 7.1.6 in the SAP HANA Developer Guide.

Special thanks to David Fishburn and Marcus Pridham who also helped me debug this.

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Lars Breddemann

    Good work Wayne!

    Looks like you really have a knack for blog writing – so keep doing that and post more of such quality pieces. ­čÖé

    Thanks for this one anyway.

    – Lars

    (0) 
  2. Vasant Shankarling

    Wayne/Lars,

    Do you know if there is a way to send an array of values to HANA OData service as follows?

    {

        “MIM_SURVEY”: [

            {

                “APP_INSTANCE”: “100041”,

                “QUESTION_ID”: 1,

                “ANSWER_ID”: “A”

            },

            {

                “APP_INSTANCE”: “100041”,

                “QUESTION_ID”: 2,

                “ANSWER_ID”: “B”

            }

        ]

    }

    I tried posting the above in POSTMAN tool, but I keep getting the following error.

    “The serialized resource has an missing value for member ‘APP_INSTANCE’.” APP_INSTANCE is the first field in the table that I have exposed in OData Service. I tried to first do a “GET” to see how the OData Service data returns in json format based on the following blog post.

    JSON POST & PUT (single & batch) on NetWeaver Gateway

    I guess this is more for NetWeaver as opposed to native HANA?

    So, tried the following but to no avail. Same error.

    {

        “d”: [

            {

                “APP_INSTANCE”: “100041”,

                “QUESTION_ID”: 1,

                “ANSWER_ID”: “B”,

                “GEO_ZIP_CD”: “80031”,

                “GEO_CITY”: “PIT”,

                “GEO_STATE”: “PA”,

                “GEO_COUNTRY”: “US”,

                “GEO_LATITUDE”: “10.1”,

                “GEO_LONGITUDE”: “11.1”,

                “LST_UPD_DT”: “/Date(1427203339205)/”

            }

        ]

    }

    Any ideas?

    (0) 
  3. Karthik Hariharan

    Thanks Wayne. This solved my purpose. And is there a way to get back the inserted row in XSJSLIB? This is required for getting back the primary key of the row inserted

    (0) 
  4. Nicholas Charbonneau

    It has been a while now but thank you for confirming that I was not the only one experiencing this issue.

    Any new solution in the last years to handle IDENTITY more efficiently using oData?

    (0) 

Leave a Reply