diff --git a/java/change-tracking.md b/java/change-tracking.md index d55584ecb..0caffcb1d 100644 --- a/java/change-tracking.md +++ b/java/change-tracking.md @@ -129,23 +129,26 @@ For example, if you have an entity that represents the order with a composition you can annotate the elements of both and track the changes made through the order and the items in a deep update. ```cds -entity OrderItems { - key ID: UUID; +entity OrderItems : cuid { + parent : Association to Orders; [...] quantity: Integer @changelog; } -entity Orders { - key ID: UUID; +entity Orders : cuid { customerName: String @changelog; [...] - items: Composition of many OrderItems; + items: Composition of many OrderItems on items.parent = $self; } ``` -### Identifiers for Changes +You must extend the `Orders` with the aspect `changelog.changeTracked` and not the `OrderItems`. With this, all changes in the `Orders`, even deep ones, +are associated with the `Orders`. + +### Identifiers for Entities + +You can store some elements of the entity together with the changes in the change log to produce a user-friendly identifier that annotates changes. -You can store some elements of the entity together with the changes in the change log to produce a user-friendly identifier. You define this identifier by annotating the entity with the `@changelog` annotation and including the elements that you want to store together with the changed value: @@ -156,56 +159,74 @@ annotate Bookshop.Book with @changelog: [ ``` This identifier can contain the elements of the entity or values of to-one associations that are reachable via path. -For example, for a book you can store an author name if you have an association from the book to the author. +For example, for a book you can store an author name if you have an association from the book to the author. The best candidates for identifier are the elements that are insert-only or that don't change often. -When you define the identifier for an entity, keep in mind that the projections of the annotated entity -will inherit the annotation `@changelog`. If you change the structure of the projection, -for example, exclude or rename the elements that are used in the identifier, you must annotate the projection again -to provide updated element names in the identifier. +### Identifiers for Compositions -The best candidates for identifier elements are the elements that are insert-only or that don't change often. - -:::warning Stored as-is -The values of the identifier are stored together with the change log as-is. They are not translated and some data types might -not be formatted per user locale or some requirements, for example, different units of measurement or currencies. -You should consider this when you decide what to include in the identifier. -::: +For compositions, no special annotations are required, the identifiers of the target entity are used instead. -### Identifiers for Associated Entities +For example, given the following model: -When your entity has an association to an other entity, you might want to log the changes in their relationship. - -Given the `Orders` entity with an association to a `Customer` instead of the element with customer name: ```cds -entity Orders { - key ID: UUID; +entity Orders : cuid { + OrderNo : String; customer: Association to Customer; [...] + items: Composition of many OrderItems on items.parent = $self; +} + +entity OrderItems : cuid { + parent : Association to Orders; + supplierName: String; + [...] + quantity : Integer; } ``` -If you annotate such an association with `@changelog`, by default, the change log stores the value of the associated entity key. -If you want, you can store some human-readable identifier instead. You define this by annotating the association with an own identifier: +You can annotate your model as follows to define identifiers for both entities. + +```cds +annotate Orders with @changelog: [OrderNo]; + +annotate OrderItems with @changelog: [ + parent.OrderNo, + supplierName, +]; +``` + +Changes for `Orders` and `OrderItems` will have their own respective target or root identifiers filled. + +### Human-readable values for associations + +For associations, the value of the foreign key is stored in the changelog by default. You can change this and store values of the associated entity instead. +This kind of identifier changes the values stored in the changelog, while [entity identifiers](#identifiers-for-entities) annotate changed values. + +You annotate your entity like this: ```cds annotate Orders { customer @changelog: [ customer.name ] } ``` - -Elements from the `@changelog` annotation value must always be prefixed by the association name. The same caveats as for the identifiers for the entities apply here. - -If you annotate a composition with an identifier, the change log will contain an entry with the identifier's value. Additionally, it will include change log entries for all annotated elements of the composition's target entity. +Elements from the `@changelog` annotation value must always be prefixed by the association name. :::warning Validation required If the target of the association is missing, for example, when an entity is updated with the ID for a customer -that does not exists, the changelog entry will not be created. You need to validate +that does not exist, the changelog entry will not be created. You need to validate such cases in the custom code or use annotations, for example, [`@assert.target`](/guides/providing-services#assert-target). ::: -This feature can also be used for to-many compositions, when you don't need to track the deep changes, but still want to track the additions and removals in the composition. +### Caveats of Identifiers + +Consider the following important points that are relevant for all kinds of identifiers and human-readable values: + +- When you define the identifier for an entity, keep in mind that the projections of the annotated entity +will inherit the annotation `@changelog`. If you change the structure of the projection, +for example, exclude or rename the elements that are used in the identifier, you must annotate the projection again +to provide updated element names in the identifier. This is one additional benefit of annotating the top-most projection for change tracking. -With association identifiers you also must consider the changes in your entities structure along the projections. In case your target entity is exposed using different projections with removed or renamed elements, you also need to adjust the identifier accordingly in the source entity. +- The values of the identifier are stored together with the change log as-is. They are not translated and some data types might +not be formatted per user locale or some requirements, for example, different units of measurement or currencies. ### Displaying Changes @@ -268,10 +289,10 @@ the changes across the complete document and stores them in the change log with For example, given the order and item model from above, if you change values for the tracked elements with the deep update, for example, the customer name in the order and the quantity of the item, the change log contains two entries: one for the order and one for the item. The change log entry for the item will also reflect that -the root of the change is an order. +the root of the change is an order. Both changes will be reachable through the association `changes` of the order entity. :::warning Prefer deep updates for change tracked entities -If you change the values of the `OrderItems` entity directly via an OData request or a CQL statement, the change log contains only one entry for the item and won't be associated with an order. +If you change the values of the `OrderItems` entity directly via an OData request or a CQL statement, the change log contains only one entry for the item and won't be associated with an order and will not be reachable through the `changes` association. While the updates of composition targets directly is possible, the change tracking feature does not attempt to resolve the parent entity by itself, it requires that either OData request or a CQL statement provide the reference to the parent e.g. `Orders`. ::: ## Reacting on Changes