From c3d749fdeec0edef6a208d5564176f10c53ba41c Mon Sep 17 00:00:00 2001 From: Marc Becker Date: Tue, 5 Aug 2025 17:04:58 +0200 Subject: [PATCH 1/2] Docs for typed result APIs --- cds/cdl.md | 2 +- java/cds-data.md | 18 ++--- java/cqn-services/application-services.md | 2 +- java/developing-applications/testing.md | 2 +- java/event-handlers/index.md | 4 +- java/event-handlers/request-contexts.md | 2 +- java/fiori-drafts.md | 2 +- java/reflection-api.md | 2 +- java/working-with-cql/query-api.md | 6 +- java/working-with-cql/query-execution.md | 92 ++++++++++++++--------- 10 files changed, 75 insertions(+), 57 deletions(-) diff --git a/cds/cdl.md b/cds/cdl.md index 4aeaf672e..f771f9c76 100644 --- a/cds/cdl.md +++ b/cds/cdl.md @@ -849,7 +849,7 @@ SELECT.from({ ref: [{ id: 'UsingView', args: { bar: { val: true }}} ]} ) ``` ```Java [Java] var params = Map.of("bar", true); -Result result = service.run(Select.from("UsingView"), params); +CdsResult result = service.run(Select.from("UsingView"), params); ``` ::: diff --git a/java/cds-data.md b/java/cds-data.md index 14f6d7b83..13d2621fd 100644 --- a/java/cds-data.md +++ b/java/cds-data.md @@ -219,7 +219,7 @@ In CAP Java data is represented in maps. To simplify data access in custom code, ![This graphic is explained in the accompanying text.](./assets/accessor.drawio.svg) -The `Row`s of a [query result](./working-with-cql/query-execution#result) as well as the [generated accessor interfaces](#generated-accessor-interfaces) already extend `CdsData`. Using the helper class [Struct](#struct) you can extend any `Map` with the CdsData `interface`: +The rows of a [query result](./working-with-cql/query-execution#result) as well as the [generated accessor interfaces](#generated-accessor-interfaces) already extend `CdsData`. Using the helper class [Struct](#struct) you can extend any `Map` with the CdsData `interface`: ```java Map map = new HashMap<>(); @@ -307,7 +307,7 @@ You can use the functions, `CQL.cosineSimilarity` or `CQL.l2Distance` (Euclidean ```Java CqnVector v = CQL.vector(embedding); -Result similarBooks = service.run(Select.from(BOOKS).where(b -> +CdsResult similarBooks = service.run(Select.from(BOOKS).where(b -> CQL.cosineSimilarity(b.embedding(), v).gt(0.9)) ); ``` @@ -322,7 +322,7 @@ CqnSelect query = Select.from(BOOKS) .where(b -> b.ID().ne(bookId).and(similarity.gt(0.9))) .orderBy(b -> b.get("similarity").desc()); -Result similarBooks = db.run(select, CdsVector.of(embedding)); +CdsResult similarBooks = db.run(select, CdsVector.of(embedding)); ``` In CDS QL queries, elements of type `cds.Vector` are not included in select _all_ queries. They must be explicitly added to the select list: @@ -397,7 +397,7 @@ To select the mapping elements of a managed association, simply add the [associa CqnSelect select = Select.from(BOOKS).byId(123) .columns(b -> b.author()); -Row row = persistence.run(select).single(); +CdsData row = persistence.run(select).single(); Integer authorId = row.getPath("author.ID"); ``` @@ -414,7 +414,7 @@ Map order = new HashMap<>(); order.put("header.status", "canceled"); CqnSelect select = Select.from("bookshop.Orders").matching(order); -Result canceledOrders = persistence.run(select); +CdsResult canceledOrders = persistence.run(select); ``` ## Typed Access @@ -795,7 +795,7 @@ The process method can also be used on CDS.ql results that have a row type: ```java CqnSelect query; // some query -Result result = service.run(query); +CdsResult result = service.run(query); processor.process(result); ``` @@ -909,8 +909,8 @@ diff.process(newImage, oldImage, type); ``` ```java -Result newImage = service.run(Select.from(...)); -Result oldImage = service.run(Select.from(...)); +CdsResult newImage = service.run(Select.from(...)); +CdsResult oldImage = service.run(Select.from(...)); diff.process(newImage, oldImage, newImage.rowType()); ``` @@ -1373,7 +1373,7 @@ Using a custom `On` handler makes sense if you want to prevent that the default ```java @On(event = CqnService.EVENT_UPDATE) -public Result processCoverImage(CdsUpdateEventContext context, List books) { +public CdsResult processCoverImage(CdsUpdateEventContext context, List books) { books.forEach(book -> { book.setCoverImage(new CoverImagePreProcessor(book.getCoverImage())); }); diff --git a/java/cqn-services/application-services.md b/java/cqn-services/application-services.md index a1e84f99a..7d0a33ffe 100644 --- a/java/cqn-services/application-services.md +++ b/java/cqn-services/application-services.md @@ -137,7 +137,7 @@ If an event handler for an `UPDATE` or `DELETE` event does not specify a result Event handlers for `INSERT` and `UPSERT` events can return a result representing the data that was inserted/upserted. -A failed insert is indicated by throwing an exception, for example, a `UniqueConstraintException` or a `CdsServiceException` with error status `CONFLICT`. +A failed insert is indicated by throwing an exception, for example, a `UniqueConstraintException` or a `ServiceException` with error status `CONFLICT`. ### Result Builder { #result-builder} diff --git a/java/developing-applications/testing.md b/java/developing-applications/testing.md index b3effe384..83c936cc9 100644 --- a/java/developing-applications/testing.md +++ b/java/developing-applications/testing.md @@ -154,7 +154,7 @@ public class CatalogServiceTest { @Test public void discountApplied() { - Result result = catalogService.run(Select.from(Books_.class).byId("51061ce3-ddde-4d70-a2dc-6314afbcc73e")); + CdsResult result = catalogService.run(Select.from(Books_.class).byId("51061ce3-ddde-4d70-a2dc-6314afbcc73e")); // book with title "The Raven" and a stock quantity of > 111 Books book = result.single(Books.class); diff --git a/java/event-handlers/index.md b/java/event-handlers/index.md index 046582a4c..507e04c13 100644 --- a/java/event-handlers/index.md +++ b/java/event-handlers/index.md @@ -447,7 +447,7 @@ The return value of an event can be set by returning a value in an event handler ```java @On(entity = Books_.CDS_NAME) -public Result readBooks(CdsReadEventContext context) { +public CdsResult readBooks(CdsReadEventContext context) { return db.run(context.getCqn()); } ``` @@ -455,7 +455,7 @@ public Result readBooks(CdsReadEventContext context) { In case an event handler method of the `Before` or `On` phase has a return value it automatically [completes the event processing](#eventcompletion), once it's executed. Event handler methods of the `After` phase that have a return value, replace the return value of the event. -For [CRUD events](../cqn-services/application-services#crudevents) and [draft-specific CRUD events](../fiori-drafts#draftevents), return values that extend `Iterable>` are supported. The `Result` object or a list of entity data (for example `List`) fulfill this requirement. +For [CRUD events](../cqn-services/application-services#crudevents) and [draft-specific CRUD events](../fiori-drafts#draftevents), return values that extend `Iterable>` are supported. The `Result` and `CdsResult`) fulfill this requirement. ```java @On(entity = Books_.CDS_NAME) diff --git a/java/event-handlers/request-contexts.md b/java/event-handlers/request-contexts.md index be98b0e97..8acb34657 100644 --- a/java/event-handlers/request-contexts.md +++ b/java/event-handlers/request-contexts.md @@ -300,7 +300,7 @@ To propagate the parent context, create an instance of `RequestContextRunner` in ```java RequestContextRunner runner = runtime.requestContext(); -Future result = Executors.newSingleThreadExecutor().submit(() -> { +Future> result = Executors.newSingleThreadExecutor().submit(() -> { return runner.run(threadContext -> { return persistenceService.run(Select.from(Books_.class)); }); diff --git a/java/fiori-drafts.md b/java/fiori-drafts.md index b6abf6a54..1202010cc 100644 --- a/java/fiori-drafts.md +++ b/java/fiori-drafts.md @@ -49,7 +49,7 @@ You can then delegate reading of active entities, for example to a remote S/4 sy ```java @On(entity = MyRemoteDraftEnabledEntity_.CDS_NAME) -public Result delegateToS4(ActiveReadEventContext context) { +public CdsResult delegateToS4(ActiveReadEventContext context) { return remoteS4.run(context.getCqn()); } ``` diff --git a/java/reflection-api.md b/java/reflection-api.md index 12a5b263e..142aa376c 100644 --- a/java/reflection-api.md +++ b/java/reflection-api.md @@ -297,7 +297,7 @@ FeatureTogglesInfo isbn = FeatureTogglesInfo.create(Collections.singletonMap("is ... -Future result = Executors.newSingleThreadExecutor().submit(() -> { +Future> result = Executors.newSingleThreadExecutor().submit(() -> { return runtime.requestContext().featureToggles(isbn).run(rc -> { return db.run(Select.from(Books_.CDS_NAME)); }); diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 264a0347e..f9906adf5 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -528,7 +528,7 @@ import static bookshop.Bookshop_.BOOKS; CqnSelect q = Select.from(BOOKS) .columns(b -> b.author()); -Row book = dataStore.execute(q).single(); +CdsData book = dataStore.execute(q).single(); Object authorId = book.get("author.Id"); // path access ``` @@ -1152,7 +1152,7 @@ Map paramSet2 = new HashMap<>(); paramSet2.put("author.name", "Emily Brontë"); paramSet2.put("title", "Wuthering Heights"); -Result result = service.run(update, asList(paramSet1, paramSet2)); +CdsResult result = service.run(update, asList(paramSet1, paramSet2)); ``` ## Delete @@ -1623,7 +1623,7 @@ BETWEEN #### `IN` Predicate -The `IN` predicate tests if a value is equal to any value in a given list. +The `IN` predicate tests if a value is equal to any value in a given list. The following example, filters for books written by Poe or Hemingway: diff --git a/java/working-with-cql/query-execution.md b/java/working-with-cql/query-execution.md index 699ab183d..c4c6a1adf 100644 --- a/java/working-with-cql/query-execution.md +++ b/java/working-with-cql/query-execution.md @@ -23,7 +23,7 @@ CqnService service = ... CqnSelect query = Select.from("bookshop.Books") .columns("title", "price"); -Result result = service.run(query); +CdsResult result = service.run(query); ``` @@ -46,7 +46,7 @@ Map paramValues = new HashMap<>(); paramValues.put("id1", 101); paramValues.put("id2", 102); -Result result = service.run(delete, paramValues); +CdsResult result = service.run(delete, paramValues); ``` ::: warning The parameter value map **must** be of type `Map`, otherwise the map is interpreted as a single positional/indexed parameter value, which results in an error. @@ -62,7 +62,7 @@ import static com.sap.cds.ql.CQL.param; CqnDelete delete = Delete.from("bookshop.Books") .where(b -> b.get("ID").in(param(0), param(1))); -Result result = service.run(delete, 101, 102); +CdsResult result = service.run(delete, 101, 102); ``` Before the execution of the statement the values 101 and 102 are bound to the defined parameters. @@ -81,12 +81,12 @@ CqnDelete delete = Delete.from("bookshop.Books").byParams("ID"); Map paramSet1 = singletonMap("ID", 101); Map paramSet1 = singletonMap("ID", 102); -Result result = service.run(query, asList(paramSet1, paramSet2)); +CdsResult result = service.run(query, asList(paramSet1, paramSet2)); long deletedRows = result.rowCount(); ``` -From the result of a batch update/delete the total number of updated/deleted rows can be determined by [rowCount()](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/Result.html#rowCount--), and [rowCount(batchIndex)](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/Result.html#rowCount-int-) returns the number of updated/deleted rows for a specific parameter set of the batch. -The number of batches can be retrieved via the [batchCount()](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/Result.html#batchCount--) method. Batch updates also return the update data. +From the result of a batch update/delete the total number of updated/deleted rows can be determined by [rowCount()](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/CdsResult.html#rowCount--), and [rowCount(batchIndex)](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/CdsResult.html#rowCount-int-) returns the number of updated/deleted rows for a specific parameter set of the batch. +The number of batches can be retrieved via the [batchCount()](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/CdsResult.html#batchCount--) method. Batch updates also return the update data. The maximum batch size for update and delete can be configured via `cds.sql.max-batch-size` and has a default of 1000. @@ -114,7 +114,7 @@ To query `BooksView` in Java, run a select statement and provide values for all CqnSelect query = Select.from("BooksView"); var params = Map.of("minStock", 100); -Result result = service.run(query, params); +CdsResult result = service.run(query, params); ``` ### Query Hints { #hana-hints} @@ -140,12 +140,12 @@ The [update](./query-api) operation can be executed as follows: Map book = Map.of("title", "CAP"); CqnUpdate update = Update.entity("bookshop.Books").data(book).byId(101); -Result updateResult = service.run(update); +CdsResult updateResult = service.run(update); ``` -The update `Result` contains the data that is written by the statement execution. Additionally to the given data, it may contain values generated for [managed data](../../guides/domain-modeling#managed-data) and foreign key values. +The update `CdsResult` contains the data that is written by the statement execution. Additionally to the given data, it may contain values generated for [managed data](../../guides/domain-modeling#managed-data) and foreign key values. -The [row count](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/Result.html#rowCount()) of the update `Result` indicates how many rows where updated during the statement execution: +The [row count](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/CdsResult.html#rowCount()) of the update `CdsResult` indicates how many rows where updated during the statement execution: ```java @@ -204,10 +204,10 @@ entity Author { Iterable> books; CqnInsert insert = Insert.into("bookshop.Books").entries(books); -Result result = service.run(insert); +CdsResult result = service.run(insert); CqnUpsert upsert = Upsert.into("bookshop.Books").entries(books); -Result result = service.run(upsert); +CdsResult result = service.run(upsert); ``` @@ -321,7 +321,7 @@ To add or update CDS views without redeploying the database schema, annotate the Runtime views must be simple [projections](../../cds/cdl#as-projection-on), not using *aggregations*, *join*, *union* or *subqueries* in the *from* clause, but may have a *where* condition if they are only used to read. On write, the restrictions for [write through views](#updatable-views) apply in the same way as for standard CDS views. However, if a runtime view cannot be resolved, a fallback to database views is not possible, and the statement fails with an error. -CAP Java provides two modes for resolving runtime views during read operations: [cte](#rtview-cte) and [resolve](#rtview-resolve). +CAP Java provides two modes for resolving runtime views during read operations: [cte](#rtview-cte) and [resolve](#rtview-resolve). ::: details Changing the runtime view mode To globally set the runtime view mode, use the property `cds.sql.runtimeView.mode` with value `cte` (the default) or `resolve` in the *application.yml*. To set the mode for a specific runtime view, annotate it with `@cds.java.runtimeView.mode: cte|resolve`. @@ -394,7 +394,7 @@ On PostgreSQL, some [pessimistic locking](#pessimistic-locking) queries on runti ### Draft Queries on Views { #draft-views } -When draft-enabling a CDS view, the CDS Compiler creates a corresponding draft persistence table for this view. [Draft activate](../fiori-drafts#editing-drafts) updates the active entity via the view. +When draft-enabling a CDS view, the CDS Compiler creates a corresponding draft persistence table for this view. [Draft activate](../fiori-drafts#editing-drafts) updates the active entity via the view. That means:
@@ -414,7 +414,7 @@ Draft-enabling runtime views is only supported in [*CTE*](#rtview-cte) mode and ### Views on Remote Services -When delegating queries between Application Services and Remote Services, statements are resolved to the targeted service's entity definition by the CAP Java runtime. +When delegating queries between Application Services and Remote Services, statements are resolved to the targeted service's entity definition by the CAP Java runtime. For read, the CDS views are resolved similar to the runtime view [resolve](#rtview-resolve) mode. For write operations, views targeting *remote OData* services must fulfill the following: @@ -461,7 +461,7 @@ CqnUpdate update = Update.entity(ORDER).entry(newData) .where(o -> o.id().eq(85).and( o.eTag(expectedLastModification))); -Result rs = db.execute(update); +CdsResult rs = db.execute(update); if (rs.rowCount() == 0) { // order 85 does not exist or was modified concurrently @@ -471,7 +471,7 @@ if (rs.rowCount() == 0) { In the previous example, an `Order` is updated. The update is protected with a specified ETag value (the expected last modification timestamp). The update is executed only if the expectation is met. ::: warning Application has to check the result -No exception is thrown if an ETag validation does not match. Instead, the execution of the update (or delete) succeeds but doesn't apply any changes. Ensure that the application checks the `rowCount` of the `Result` and implement your error handling. If the value of `rowCount` is 0, that indicates that no row was updated (or deleted). +No exception is thrown if an ETag validation does not match. Instead, the execution of the update (or delete) succeeds but doesn't apply any changes. Ensure that the application checks the `rowCount` of the `CdsResult` and implement your error handling. If the value of `rowCount` is 0, that indicates that no row was updated (or deleted). ::: @@ -510,7 +510,7 @@ Order order = db.run(select).single(Order.class); order.setAmount(5000); CqnUpdate update = Update.entity(ORDER).entry(order); -Result rs = db.execute(update); +CdsResult rs = db.execute(update); if (rs.rowCount() == 0) { // order 85 does not exist or was modified concurrently @@ -527,7 +527,7 @@ List orders = db.run(select).listOf(Order.class); orders.forEach(o -> o.setStatus("cancelled")); -Result rs = db.execute(Update.entity(ORDER).entries(orders)); +CdsResult rs = db.execute(Update.entity(ORDER).entries(orders)); for(int i = 0; i < orders.size(); i++) if (rs.rowCount(i) == 0) { // order does not exist or was modified concurrently @@ -589,7 +589,7 @@ Java snippet for creating element `coverImage` from file `IMAGE.PNG` using `java ```java // Transaction started -Result result; +CdsResult result; try (InputStream resource = getResource("IMAGE.PNG")) { Map book = new HashMap<>(); book.put("title", "My Fancy Book"); @@ -609,14 +609,15 @@ CAP Java doesn't have a dedicated API to execute native SQL Statements. However, ## Query Result Processing { #result} -The result of a query is abstracted by the `Result` interface, which is an iterable of `Row`. A `Row` is a `Map` with additional convenience methods and extends [CdsData](../cds-data#cds-data). +The result of a query is abstracted by the `CdsResult` interface, which is an iterable of [CdsData](../cds-data#cds-data) or its more specific [generated accessor interfaces](../cds-data#typed-access). +`CdsData` itself is a `Map` with additional convenience methods. -You can iterate over a `Result`: +You can iterate over a `CdsResult`: ```java -Result result = ... +CdsResult result = ... -for (Row row : result) { +for (CdsData row : result) { System.out.println(row.get("title")); } ``` @@ -624,7 +625,7 @@ for (Row row : result) { Or process it with the [Stream API](https://docs.oracle.com/javase/8/docs/api/?java/util/stream/Stream.html): ```java -Result result = ... +CdsResult result = ... result.forEach(r -> System.out.println(r.get("title"))); @@ -634,26 +635,30 @@ result.stream().map(r -> r.get("title")).forEach(System.out::println); If your query is expected to return exactly one row, you can access it with the `single` method: ```java -Result result = ... +CdsResult result = ... -Row row = result.single(); +CdsData row = result.single(); ``` -If it returns a result, like a `find by id` would, you can obtain it using `first`: +::: tip +Set application property `cds.errors.preferServiceException` to ensure `single()` throws an appropriate 404 exception in case no row is found. +::: + +You can also obtain the first row using `first`: ```java -Result result = ... +CdsResult result = ... -Optional row = result.first(); +Optional row = result.first(); row.ifPresent(r -> System.out.println(r.get("title"))); ``` -The `Row`'s `getPath` method supports paths to simplify extracting values from nested maps. This also simplifies extracting values from results with to-one expands using the generic accessor. Paths with collection-valued segments and infix filters are not supported. +The `CdsData`'s `getPath` method supports paths to simplify extracting values from nested maps. This also simplifies extracting values from results with to-one expands using the generic accessor. Paths with collection-valued segments and infix filters are not supported. ```java CqnSelect select = Select.from(BOOKS).columns( b -> b.title(), b -> b.author().expand()).byId(101); -Row book = dataStore.execute(select).single(); +CdsData book = dataStore.execute(select).single(); String author = book.getPath("author.name"); ``` @@ -690,8 +695,8 @@ interface Book { Integer getStock(); } -Row row = ... -Book book = row.as(Book.class); +CdsResult result = service.run(query); +Book book = result.single(Book.class); String title = book.getTitle(); Integer stock = book.getStock(); @@ -700,7 +705,7 @@ Integer stock = book.getStock(); Interfaces can also be used to get a typed list or stream over the result: ```java -Result result = ... +CdsResult result = ... List books = result.listOf(Book.class); @@ -710,6 +715,19 @@ Map titleToDescription = For the entities defined in the data model, CAP Java SDK can generate interfaces for you through [a Maven plugin](../cqn-services/persistence-services#staticmodel). +When setting `linkedInterfaces` to `true` in the CDS Maven Plugin `generate` goal, [query builder interfaces](../java/working-with-cql/query-api#concepts) and [data accessor interfaces](../java/cds-data#typed-access) are linked. This enables automatically typed results when executing a `Select` or `Update` statement, avoiding the need to explicitly pass the data accessor interface class to methods like `single()`, `listOf()` or `streamOf()`. + +```java +import static cds.gen.catalogservice.CatalogService_.BOOKS; + +var select = Select.from(BOOKS).byId(4711); // use var or Select +CdsResult result = service.run(select); +Books book = result.single(); +``` + +::: tip +Avoid using `CqnSelect` or `CqnUpdate` for typed query declarations, but prefer `var` to allow the Java compiler to retain the entity query type, linking to the data accessor interface. +::: ### Entity References {#entity-refs} @@ -757,13 +775,13 @@ CqnDelete d = Delete.from(joyce.address()) ### Introspecting the Row Type -The `rowType` method allows to introspect the element names and types of a query's `Result`. It returns a `CdsStructuredType` describing the result in terms of the [Reflection API](../reflection-api): +The `rowType` method allows to introspect the element names and types of a query's `CdsResult`. It returns a `CdsStructuredType` describing the result in terms of the [Reflection API](../reflection-api): ```java CqnSelect query = Select.from(AUTHOR) .columns(a -> a.name().as("authorName"), a -> a.age()); -Result result = service.run(query); +CdsResult result = service.run(query); CdsStructuredType rowType = result.rowType(); rowType.elements(); // "authorName", "age" From f875bf04d9ee79f64002594e9a9f6cf7b50c670d Mon Sep 17 00:00:00 2001 From: Marc Becker Date: Tue, 5 Aug 2025 17:15:21 +0200 Subject: [PATCH 2/2] Fix links --- java/working-with-cql/query-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/working-with-cql/query-execution.md b/java/working-with-cql/query-execution.md index c4c6a1adf..5870dd8a1 100644 --- a/java/working-with-cql/query-execution.md +++ b/java/working-with-cql/query-execution.md @@ -715,7 +715,7 @@ Map titleToDescription = For the entities defined in the data model, CAP Java SDK can generate interfaces for you through [a Maven plugin](../cqn-services/persistence-services#staticmodel). -When setting `linkedInterfaces` to `true` in the CDS Maven Plugin `generate` goal, [query builder interfaces](../java/working-with-cql/query-api#concepts) and [data accessor interfaces](../java/cds-data#typed-access) are linked. This enables automatically typed results when executing a `Select` or `Update` statement, avoiding the need to explicitly pass the data accessor interface class to methods like `single()`, `listOf()` or `streamOf()`. +When setting `linkedInterfaces` to `true` in the CDS Maven Plugin `generate` goal, [query builder interfaces](../working-with-cql/query-api#concepts) and [data accessor interfaces](../cds-data#typed-access) are linked. This enables automatically typed results when executing a `Select` or `Update` statement, avoiding the need to explicitly pass the data accessor interface class to methods like `single()`, `listOf()` or `streamOf()`. ```java import static cds.gen.catalogservice.CatalogService_.BOOKS;