Search

Service Provisioning API

The Service Provisioning APIs can be used to register event handlers on services. In CAP everything that happens at runtime is an event that is sent to a service. With event handlers the processing of these events can be extended or overridden. Event handlers can be used to handle CRUD events, implement actions and functions and to handle asynchronous events from a messaging service.

Content

Introduction to Event Handlers

CAP allows you to register event handlers for events on the services defined in your CDS model. Event handlers enable you to add custom business logic to your application by either extending the processing of an event, or by completely overriding its default implementation. Typical events that happen at runtime are CRUD events (CREATE, READ, UPDATE, UPSERT, and DELETE). These events are sent by HTTP-based protocol adapters (for example an OData V4 protocol adapter) to the respective CDS service. The CAP Java stack provides default event handlers (also known as Generic Providers) that handle CRUD operations out-of-the-box and implement the handling of many CDS annotations. Therefore, it’s most common that event handlers for CRUD events extend the event processing (Before and After phase).

Actions and functions can be used to add custom synchronous events to your services. For such custom events CAP doesn’t provide default event handlers. For these events, it’s therefore common to register event handlers that define the default implementation (On phase).

Events usually have parameters and a return value. The CRUD events in CAP are mapped to CQN statements. If you have a READ event, a CQN Select statement is provided as a parameter to the event handlers. The return value is a list of entity objects, that were selected by the query. Similarly, a CREATE event gets a CQN Insert statement as a parameter. This CQN Insert statement also contains the entity data to be inserted. The list of entity objects that were inserted is provided as a return value. The remaining CRUD events UPDATE, UPSERT, and DELETE also have a respective CQN statement type.

For actions and functions, the parameter types and return type are directly modeled in the CDS definition. The CAP Java stack provides a way to access parameters and return values of CRUD events and custom events in a type-safe way.

Event Phases

Events are processed in three phases that are executed consecutively: Before, On, and After. When registering an event handler the phase in which the event handler should be called needs to be specified. In Java, an annotation exists for each event phase (@Before, @On, and @After) to let the framework know which phase of the event processing is to be handled.

It’s possible to register multiple event handlers for each event phase. Handlers within the same event phase are executed one at a time and therefore never concurrently. In case concurrency is desired, it needs to be explicitly implemented within an event handler. There’s also no guaranteed order in which the handlers of the same phase are called. It’s therefore recommended to put code that requires ordering of certain steps into a single event handler.

The following subchapters describe the semantics of the three phases in more detail:

Before

The Before phase is the first phase of the event processing. This phase is useful for filtering, validation, and other types of preprocessing of the incoming parameters of an event. There can be an arbitrary number of Before handlers per event. The processing of the Before phase is completed when one of the following conditions applies:

On

The On phase is started after the Before phase, as long as no return value is yet provided and no exception occurred. It’s meant to implement the core processing of the event. There can be an arbitrary number of On handlers per event, although as soon as the first On handler successfully completes the event processing, all remaining On handlers are skipped.

The On phase is completed when one of the following conditions applies:

If after the On phase, no handler completed the event processing, it’s considered an error and the event processing is aborted with an exception.

After

The After phase is only started after the On phase is completed successfully. Handlers are therefore guaranteed to have access to the result of the event processing. This phase is useful for post-processing of the return value of the event or triggering side-effects. A handler in this phase can also still abort the event processing by throwing an exception. No further handlers of the After phase are called in this case.

Event Contexts

In CAP Java, the EventContext is the central interface, that provides information about the current event to the event handler. It provides general information, like the currently processed event, the target entity of the event and the service that the event was sent to. Additional information, the EventContext provides access to:

The event context also stores all parameters of the event and the return value. From the EventContext interface the parameters and return value can be accessed and modified:

EventContext context = EventContext.create("myEvent", null);

// set parameters
context.put("parameter1", "MyParameter1");
context.put("parameter2", 2);

srv.emit(context); // process event

// access return value
Object result = context.get("result");

Both of these methods aren’t aware of the type of the parameter or return value, in context of the specific event. They also require a key, which is used to store and retrieve the parameter or return value from a map. As this is cumbersome and error-prone, the CAP Java stack provides the ability to overlay an event context with a specialized event context interface. This interface is aware of the parameter types and the return type of a certain event.

For each event that the CAP Java stack provides out-of-the-box (for example the CRUD events) a specialized event context is provided. This specialized event context interface can be used to access the parameters and the return value of a certain event through normal getter and setter methods. These methods are also aware of the concrete type of the parameter or return value.

An example of such an event context is the CdsReadEventContext interface. The READ event has a CQN Select as parameter and returns a list of entity data, represented as a Result object. The following example shows how such an interface can be used with an existing event context:

CdsReadEventContext context = genericContext.as(CdsReadEventContext.class);
CqnSelect select = context.getCqn();
context.setResult(Collections.emptyList());
Result result = context.getResult();

It’s important to note, that these getter and setter methods, still operate on the simple get/put API shown in the previous example. They just provide a type-safe layer on top of it. The interfaces also don’t need to be implemented. Within the as method, Java proxies are used to enable this. This flexibility enables the ability to create such interfaces also for custom actions and functions, defined in the CDS model.

Recommendation: Use these specialized type-safe EventContext interfaces whenever possible.

Completing the Event Processing

The EventContext interface also provides the API to complete the event processing. Indicating the completion of the event, is important to successfully finish the On phase of an event. If the event doesn’t have any return value, the completion can be indicated by triggering the context.setCompleted() method.

For events that specify a return value, the context.setResult(...) method of the specialized event context interface can be used to set the return value. Calling this method automatically sets the event to completed.

If the generic event context API is used, setting the result and setting the event to completed are two dedicated steps. Please note, that by convention the return value is expected under the key result in the generic event context:

Object myResult = ...
context.put("result", myResult);
context.setCompleted();

Defining Custom EventContext Interfaces

When implementing actions or functions, it can be useful to define custom EventContext interfaces to allow type-safe access to the parameters and the returned value of the action or function.

The following example shows a custom EventContext interface.

@EventName("myAction")
public interface MyActionEventContext extends EventContext {
       
        CqnSelect getCqn();
       
        String getInputParam();

        @CdsName("param2");
        String getOtherInputParam();
       
        void setResult(int result);
}

The @EventName annotation ensures that the custom EventContext interface (here MyActionEventContext) is only used on EventContext instances, that correspond to the specified event. Make sure, that the getter and setter methods are named after the name of the parameter in the CDS model. You can also specify the name of the parameter using the @CdsName annotation.

Registering Event Handlers

In Java event handlers are implemented by methods in event handler classes. Event handler methods need to be annotated with a @Before, @On, or @After annotation and can have a flexible method signature.

Event Handler Classes

Event handler classes can contain event handler methods. You can use them to group event handlers for a specific service or even a specific entity. The class can also define arbitrary methods, which aren’t event handler methods, to provide functionality reused by multiple handler methods.

When running in Spring Boot, event handler classes are defined as a Spring bean. Therefore, you can use the full flexibility of Spring beans, such as Dependency Injection or Scopes within these classes.

The following example defines an event handler class in a Spring context:

import org.springframework.stereotype.Component;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.ServiceName;

@Component
@ServiceName("AdminService")
public class AdminServiceHandler implements EventHandler {
     
}

Every event handler class needs to implement the EventHandler marker interface. Implementing this interface is required, in order to identify the class as an event handler class.

The @ServiceName annotation can be used to specify the default service, which the event handlers are registered on. In a CDS service, the fully qualified name, including the namespace, has to be specified. When using the static model, generated by the CDS4J Maven Plugin, String constants for these names are available. The service defined by this annotation can be overridden through the annotations on the event handler methods.

Event Handler Methods

Event handler methods are methods within an event handler class, that are annotated with one of the following annotations: @Before, @On, or @After. The annotation defines, during which phase of the event processing the event handler is called.

Each of these annotations can define the following three attributes:

The Java Service interfaces (for example the CdsService interface) define String constants for the built-in events they can process. For the service and entity attribute the fully qualified names, including the namespace, need to be used. When using the static model, generated by the CDS4J Maven Plugin, String constants for these names are available. It’s recommended to use these constants in annotation attributes, to prevent unexpected behaviour caused by typos.

Here are some examples of different event handler methods:

@Before(event = { "CREATE", "UPSERT" }, entity = "AdminService.Books")
public void beforeCreateBooks(EventContext context) {
    // shows how an event handler can be registered on multiple events
}

@On(service = "CatalogService", event = "READ")
public void readAnyEntityInCatalogService(EventContext context) {
    // shows how the default service on class-level can be overriden
    // shows how to register on any entity
}

@After(event = CdsService.EVENT_READ, entity = Books_.CDS_NAME)
public void afterReadBooks(EventContext context) {
    // use the available string constants for events and entities!
}

The basic signature of an event handler method is void process(EventContext context). However, handler methods registered via annotations can have a more flexible handler signature. Handler methods don’t necessarily have to be public methods. They can also be methods with protected, private, or package visibility.

Parameters and Return Types

Registering event handler methods via Java annotations provides the ability to have flexible handler signatures. The basic signature of an event handler method is void process(EventContext context). Everything can be achieved using this signature. However, it doesn’t provide the highest level of comfort. Event handler signatures can vary on three levels:

All types of arguments can be combined as desired. It’s also valid for event handler methods to have no arguments at all.

Event Context Arguments

Every handler method can get access to the EventContext. The EventContext interface is a general interface, but CAP also provides the ability to use (custom) event-specific interfaces. You can directly refer to these event-specific interfaces in your method arguments. In that case, the general interface is overlaid with the event-specific one. This behaviour works for events defined by CAP (for example, CRUD events), as well as for custom events, for example defined through actions or functions.

During startup of the application, the event context arguments are automatically validated against the events the handlers are registered on. If an event doesn’t match the requested event context interface, the startup of the application fails. This failure handling ensures, that such errors are found early in the development process.

If an event-specific event context argument is used, the event definition in the annotation can be omitted. The event handler is automatically registered on the event, that corresponds to the event context interface. The mapping between an event context interface and its event, is done through the @EventName annotation on the event context interface.

The following examples showcase the usage of different types of event context arguments:

@After(event = CdsService.EVENT_READ, entity = Books_.CDS_NAME)
public void afterReadBooks(CdsReadEventContext context) {
    // use the event context specific to READ events
}

@Before(entity = Books_.CDS_NAME) 
public void beforeCreateBooks(CdsCreateEventContext context) {
    // CREATE event can be omitted from the annotation
    // it is inferred from the CdsCreateEventContext argument
}

@Before(event = { CdsService.EVENT_UPDATE, CdsService.EVENT_UPSERT }, entity = Books_.CDS_NAME)
public void wrongBeforeUpdateBooks(CdsUpdateEventContext context) {
    // WRONG! This will throw an error during startup
    // the CdsUpdateEventContext does not match to the UPSERT event
    // in this case only the generic EventContext argument can be used
    // Hint: You can use the EventContext.as(Class) method as alternative
}

POJO-Based Arguments

Most of the time, event handler methods are interested in the entity data, that was provided by the client or that is provided to the client as a result of the event. The CDS4J code generator generates a POJO interface for every entity in the CDS model to enable typed access to entity data. These POJO interfaces can be directly used in the event handler method signature.

Getting access to the entity data directly from the method arguments is only supported for the basic CRUD events and their draft-specific counterparts. Depending on the phase of the event processing, either the entity data provided by the client (Before and On), or the entity provided to the client (After) is provided as argument. For events that don’t have entity data in their incoming parameters (for example READ and DELETE) null is provided for POJO-based arguments on handlers of the Before or On phase. By default a DELETE event doesn’t provide the deleted entity data in its return value. Therefore, POJO-based arguments are also set to null on handlers of the After phase for this event.

The entity data for POJO-based arguments during Before and On phases is obtained from what is available in the CQN statement. During the After phase, the entity data is obtained from the Result object.

As usually a collection of entities can be present in CQN statements or in Result objects, the POJO-based arguments can be either List<Entity>, Stream<Entity> or just Entity (Entity being the POJO interface). If no collection type is used an error occurs at runtime, if multiple entities are provided in the CQN or Result. It’s therefore preferred to use the collection-based arguments.

Similarly, as for event context arguments, a validation of the POJO-based arguments is performed during startup of the application. If an entity doesn’t match the entity type of the POJO-based argument, the startup of the application fails.

If a POJO-based argument is used, the entity definition in the annotation can be omitted. The event handler is automatically registered on the entity, that corresponds to the POJO interface. The mapping between a POJO interface and its entity, is done through the @CdsName annotation on the POJO interface.

The following examples showcase the usage of different POJO-based arguments:

@After(event = CdsService.EVENT_READ, entity = Books_.CDS_NAME)
public void afterReadBooks(List<Books> books) {
    // directly get access to read books
}

@Before(event = CdsService.EVENT_CREATE) 
public void beforeCreateBooks(Stream<Books> books) {
    // get access to client data to be inserted
    // entity can be omitted from the annotation
    // it is inferred from the Books POJO interface
}

Flexible Return Types

Event handlers that are registered on the Before or On phase might want to set the return value of the event to complete the event processing. This is possible programmatically on the EventContext interface. For the basic CRUD events and their draft-specific counterparts, the event handler method can directly return any object that implements Iterable<? extends Map<String, Object>>. The Result object type used by CDS4J meets these requirements.

When returning such an object, it’s automatically set as result on the processed event context and the event context is set to completed.

The following examples showcase this:

@On(event = CdsService.EVENT_READ, entity = Books_.CDS_NAME)
public Result readBooks(CdsReadEventContext context) {
    // return Result objects
    Result result = db.run(context.getCqn());
    return result;
}

@Before(event = CdsService.EVENT_READ, entity = Authors_.CDS_NAME)
public List<Authors> readAuthors(EventContext context) {
    // return lists of POJO objects
    Authors author = Struct.create(Authors.class);
    return Arrays.asList(author);
}

@On(event = CdsService.EVENT_READ, entity = Orders_.CDS_NAME) 
public List<Map<String, Object>> readOrders(EventContext context) {
    // return any Iterable<Map<String, Object>> type
    Map<String, Object> ordersMap = new HashMap<>();
    return Arrays.asList(ordersMap);
}

Implementing Event Handlers

When implementing event handlers, it’s important to understand certain concepts of CAP applications. In the following chapter, some of these concepts are explained. It’s important to keep in mind, that when running on Spring Boot, event handlers have the full flexibility of Spring beans. If the CAP Java stack doesn’t provide the required functionality or abstraction, it’s often already available within Spring Boot.

Event Flow

It’s important to understand how CAP triggers events on services at runtime, to register the correct event handlers when extending or overriding the processing of an event. The CAP Java stack defines events for the standard CRUD operations:

Static constants exist for these event names on the CdsService interface, which also defines the Consumption API of these events.

These events are heavily used to implement HTTP-based protocol adapters, like the one for OData V4. OData is built upon the HTTP verbs. It’s therefore interesting to know, how these HTTP verbs are mapped to CAP’s CRUD events:

CAP supports creating or updating deeply structured documents with the CREATE and the UPSERT event. If there are such documents the corresponding event is only triggered on the root entity of the document. No events are automatically triggered on the composition entities contained within the document. When implementing validation logic, events needs to be triggered by the application developer, like in the following example:

@Before(event = CdsService.EVENT_CREATE, entity = Orders_.CDS_NAME)
public void validateOrders(List<Orders> orders) {
    for(Orders order : orders) {
        if (order.getItems() != null) {
            validateItems(order.getItems());
        }
    }
}

@Before(event = CdsService.EVENT_CREATE, entity = OrderItems_.CDS_NAME)
public void validateItems(List<OrderItems> items) {
    for(OrderItems item : items) {
        if (item.getAmount() <= 0) {
            throw new ServiceException(ErrorStatuses.BAD_REQUEST, "Invalid amount");
        }
    }
}

In the example, the OrderItems entity exists as a composition within the Items element of the Orders entity. When creating an order a deeply structured document can be passed, which contains order items. For this reason, the event handler method to validate order items (validateItems) is called as part of the order validation (validateOrders). In case an order item is directly created (for example through a containment navigation in OData V4) only the event handler for validation of the order items is triggered.

Draft Event Flow

When working with draft enabled entities, specific draft events are used to interact with entities in draft mode. The following additional events are available:

Static constants exist for these event names on the DraftService interface, which also defines the Consumption API of these events.

The events draftEdit and draftActivate are defined as an action in the CDS model. In OData V4, they’re therefore triggered through an action call. The other events are again mapped to HTTP verbs and override the default mapping specified for the CRUD events:

During activation of a draft entity either a single CREATE or UPSERT event is triggered to create or respectively update the active entity with all of its compositions through a deeply structured document. Using these additional events it’s possible to trigger validations only during creation or update of the active entity. Other types of custom logic can already be run, while the entity is in draft mode.

When deleting active entities, the DRAFT_CANCEL event is also triggered to delete respective draft entities, corresponding to the active entities. The READ event combines reading drafts and active entities.

Actions and Functions

Actions and functions define custom events. They can therefore be implemented by simply registering an event handler for these custom events. If an action or function is bound to an entity, the entity also needs to be specified while registering the event handler. In case the action is unbound, the entity can simply be omitted. It’s best practice that the On phase is used to register the event handler. The implementation of the event handler needs to take care of properly completing the event processing.

When implementing a bound action or function a CqnSelect statement is available through the event context, which can be used to select the entity, that was targeted by the action or function. All input parameters and the return value can be accessed and set through the event context. It’s recommended to create a custom event context interface for the action or function to enable type-safe access to the input parameters and the return value.

The following example shows how an action event handler can be implemented:

CDS Model:

service CatalogService {
    entity Books {
        key ID: UUID;
        title: String;
    } actions {
      action review(stars: Integer) returns Reviews;
    };

    entity Reviews {
        book : Association to Books;
        stars: Integer;
    }
}

Custom Event Context:

@EventName("review")
public interface ReviewEventContext extends EventContext {

    // CqnSelect that points to the entity the action was called on
    CqnSelect getCqn();
    void setCqn(CqnSelect select);

    // The 'stars' input parameter
    Integer getStars();
    void setStars(Integer stars);

    // The return value
    void setResult(Reviews review);
    Reviews getResult();

}

Event Handler:

@Component
@ServiceName(CatalogService_.CDS_NAME)
public class CatalogServiceHandler implements EventHandler {

    @On(event = "review", entity = Books_.CDS_NAME)
    public void reviewAction(ReviewEventContext context) {
        CqnSelect selectBook = context.getCqn();
        Integer stars = context.getStars();
        Reviews review = [...] // create the review
        context.setResult(review);
    }

}

Hint: The unused methods setCqn(CqnSelect), setStars(Integer) and getResult() are useful when triggering the event through the service consumption API.

Indicating Errors

Errors, that should abort the event processing and the transaction are indicated by throwing an exception. The CAP Java stack provides a generic unchecked exception class, called ServiceException. Use this class in event handlers, when throwing an exception. Using this exception class it’s also possible to indicate an internal error code and a mapping to an HTTP status code through the ErrorStatus interface. An enum ErrorStatuses exists, which lists many useful error codes already. If no error status is set when creating the ServiceException, an internal server error (HTTP status code 500) is assumed. The message, that is passed to the ServiceException, can be formatted using SLF4J’s messaging formatting style.

The following examples show how to use the ServiceException to indicate different errors:

// static text with exception cause
throw new ServiceException("An error occurred", originalException);
// message with placeholders
throw new ServiceException(ErrorStatuses.CONFLICT, "Can't order {} books: Not enough on stock", orderAmount)
// last argument can always be an exception
throw new ServiceException(ErrorStatuses.BAD_REQUEST, "Invalid number: '{}'", wrongNumber, originalException);

Consuming Services

Most of the time, the implementation of the event handler needs to use other services. Not only CDS services defined in your model are considered services. In CAP also, for example, the database is consumed through a service interface, which understands CQN. More about services is described in the Service Consumption API chapter.

Access to the local API representation of these services can be gained through the ServiceCatalog interface, which is available through the event context. CDS services can be obtained by specifying the fully qualified name as defined in the CDS model. Technical services usually have a default name, that is defined as part of the service’s interface. An example for the name of a technical service is the DEFAULT_NAME constant defined on the PersistenceService interface.

@Before(event = CdsService.EVENT_CREATE, entity = Books_.CDS_NAME)
public void beforeCreateBooks(EventContext context) {
    ServiceCatalog catalog = context.getServiceCatalog();
    PersistenceService db = catalog.getService(PersistenceService.class, PersistenceService.DEFAULT_NAME);
    CdsService adminService = catalog.getService(CdsService.class, AdminService_.CDS_NAME);
    // use these services
}

When running in Spring, all services are available as Spring beans. Dependency injection can therefore be used to get access to the service objects:

@Component
public class EventHandlerClass implements EventHandler {

    @Autowired
    private PersistenceService db;

    @Autowired
    @Qualifier(AdminService_.CDS_NAME)
    private CdsService adminService;

    @Before(event = CdsService.EVENT_CREATE, entity = Books_.CDS_NAME)
    public void beforeCreateBooks(EventContext context) {
        // use these services
    }

}

Accessing the CDS Model

CAP offers a Model Reflection API to introspect the CDS model of an application and retrieve details on the services, types, entities, and their elements. The model is available through the event context:

@Before(event = CdsService.EVENT_CREATE, entity = Books_.CDS_NAME)
public void beforeCreateBooks(EventContext context) {
    CdsModel model = context.getModel();
    // use the model
}

When running in Spring, the model is available as a Spring bean. It can therefore be accessed through dependency injection:

@Component
public class EventHandlerClass implements EventHandler {

    @Autowired
    private CdsModel model;

    @Before(event = CdsService.EVENT_CREATE, entity = Books_.CDS_NAME)
    public void beforeCreateBooks(EventContext context) {
        // use the model
    }

}

Transaction Handling

By default all processing of an event happens in a transaction. When implementing an event handler, it’s therefore guaranteed that the code is running as part of an existing transaction. If the application doesn’t have any special requirements, no transaction management needs to be performed at all. Multiple calls to insert data to the database through the persistence service are combined into the active transaction automatically. If not otherwise controlled, the transaction is closed as soon as the outermost event is finished processing.

The CAP Java stack provides an abstraction around transactions, called changeset context.

The changeset context can be used to get more fine-grained control over how events are combined in transactions.

The currently active ChangeSetContext is available through the event context. In case an event handler needs to perform some action shortly before the transaction will be committed or after the transaction was committed or rolled-back, it can register a listener on the changeset context. The ChangeSetListener interface can be used for this case. It allows to register a listener, which is executed shortly before the changeset is closed (beforeClose()) or one, that is executed after the changeset was closed (afterClose(boolean)). The afterClose method has a boolean parameter, which indicates if the changeset was completed successfully (true) or failed and rolled-back (false).

ChangeSetContext changeSet = context.getChangeSetContext();
changeSet.register(new ChangeSetListener() {

    @Override
    public void beforeClose() {
        // do something before changeset is closed
    }

    @Override
    public void afterClose(boolean completed) {
        // do something after changeset is closed
    }

});

The changeset context can also be used to cancel a changeset without throwing an exception. All events in the changeset are processed in that case, but the transaction is rolled back at the end. A changeset can still be canceled from the beforeClose() listener method.

ChangeSetContext changeSet = context.getChangeSetContext();
// cancel changeset without throwing an exception
changeSet.markForCancel();

Database transactions in CAP are always started and initialized lazily, during the first interaction with the persistence service. When running in Spring Boot, CAP Java completely integrates with Spring’s transaction management. As a result you can use Spring’s @Transactional annotations or the TransactionTemplate to control transactional boundaries. This transaction management also comes in handy, in case you need to perform plain JDBC connections in your event handlers. It might be necessary, when calling HANA procedures or selecting from tables not covered by CDS and the persistence service. When annotating an event handler with @Transactional, Spring ensures that a transaction is initialized. CAP in that case ensures, that this transaction is managed as part of an existing changeset context, for which the transaction wasn’t yet initialized. If no such changeset context exists, a new changeset context is created. In case the transaction propagation is specified as REQUIRES_NEW, Spring, and CAP ensure that a new transaction and changeset context are initialized. This mechanism suspends existing transactions and changeset contexts, until the newly created one is closed.

Spring’s transaction management can therefore be used to control transactional boundaries and to initialize transactions more eagerly than CAP. This can be combined with Spring’s standard capabilities to get access to a plain JDBC connection:

@Autowired
private JdbcTemplate jdbc;

@Autowired
private DataSource ds;

@Before(event = CdsService.EVENT_CREATE, entity = Books_.CDS_NAME)
@Transactional // ensure transaction is initialized
public void beforeCreateBooks(List<Books> books) {
    // JDBC template
    jdbc.queryForList("SELECT 1 FROM DUMMY");

    // Connection object
    Connection conn = DataSourceUtils.getConnection(ds);
    conn.prepareCall("SELECT 1 FROM DUMMY").executeQuery();
}