A note from 2026: This article was published in 2016. The product is now SAP Commerce Cloud rather than hybris, and Import Cockpit/HMC-era tooling has been deprecated in favor of Backoffice and current integration patterns. The ServiceLayer Direct internals shown here were written for hybris 6.0/6.1; validate APIs and cache behavior against your target Commerce Cloud release.

I found a way to speed up data import in hybris. The approach explained below allows you to import data two times faster than regular ImpEx. A video of the PoC, some numbers, and technical details are below.

Hybris importing capabilities overview

There are two main ways in hybris to update data from external sources:

In hybris 6.0, SAP introduced ServiceLayer Direct as a projected replacement for the legacy JALO layer. However, the new engine has not become the default engine in 6.0/6.1 due to the amount of crucial legacy business logic working only with Jalo. I leveraged some of its components to work with objects that meet the new engine's compatibility criteria.

Objects such as StockLevel and PriceRow are common key players in the integration process. If your prices or availability data are region-specific or store-specific, the amount of data could become really massive.

For both methods, when updating data, hybris makes three sequential requests:

SELECT {pk}
FROM {StockLevel}
WHERE ( {StockLevel.warehouse} = 8796194146261
    AND {StockLevel.ProductCode} = 'LProduct_1' )
SELECT *
FROM {StockLevel}
WHERE ( {pk} = 8807037634519 )
UPDATE stocklevels
SET hjmpTS = 1 ,
    modifiedTS='2016-09-23 12:05:47.543',
    p_available=11021
WHERE
    PK = 8807037634519

The first two operations, locating and loading, use cached data if there is any (region cache). However, this does not solve the problem completely: if you have millions of items to change, 2/3 of your operations will take precious time, memory, and CPU resources.

The solution

The idea of the solution is to get rid of the first two requests and use prebuilt in-memory lookup tables instead.

Diagram showing import speedup by skipping lookup and model-loading requests

Will it work? Tests show that it works twice as fast as regular ImpEx import:

Number of items Average creation time Average update time
Regular ImpEx import, 16 threads 50000 19.6 sec 14.8 sec
ServiceLayer Direct Import, 16 threads 50000 17.1 sec 7.5 sec two times faster!

You can see the difference in the JDBC log:

ImpEx Import ServiceLayer Direct
All three components are marked by color: lookup, loading model, and updating.

JDBC log showing lookup, model loading, and update statements for ImpEx import
There is only one component: an UPDATE statement.

JDBC log showing only an update statement for ServiceLayer Direct

Technical details

  1. Multithreading is not implemented in hybris classes. You need to manage it on your own.
  2. There are three objects: UpdateRecord, InsertRecord, and DeleteRecord. The example below is about UpdateRecord; all others are used in a similar way. I used StockLevel as an example, but you can change it to other objects. There are some limitations; see the next section for details.
UpdateRecord updateRecord1 = new UpdateRecord(
        pk,
        "StockLevel",
        version,
        Sets.newHashSet(
             new PropertyHolder("available", availableValue),
             new PropertyHolder("overselling", oversellingValue)
        )
);
changeSet.add(updateRecord1);
DefaultWritePersistenceGateway defaultWritePersistenceGateway =
  Registry.getApplicationContext().getBean("defaultWritePersistenceGateway",
    DefaultWritePersistenceGateway.class)
CacheInvalidator cacheInvalidator =
  Registry.getApplicationContext().getBean("cacheInvalidator",
    CacheInvalidator.class);
final Collection<PersistResult> persistResults =
 defaultWritePersistenceGateway.persist(changeSet);
cacheInvalidator.invalidate(persistResults);

In order to create objects (CreateRecord), you need to generate PKs. The next example shows the details:

TypeInfoMap persistenceInfo = Registry.getCurrentTenant().getPersistenceManager().getPersistenceInfo("StockLevel");
PK pk = PK.createCounterPK(persistenceInfo.getItemTypeCode());

Drawbacks and limitations

Nothing in life is free 😉 The downside of this approach is in the following points:

Video

© Rauf Aliev, September 2016