Interface ModelDataService

All Superinterfaces:
Service

public interface ModelDataService
extends Service
Provides APIs for reading and writing Models.
  • Method Details

    • write

      <M extends Model> ModelList<M> write​(ModelList<M> modelList, PlatformUserContext platformContext)
      Create, update or delete models using Actions defined in the MPT or SPT. Given a ModelList containing the Models to be written to the database, executes the write action specified, then returns a list containing any Models whose write operation failed.

      For example, let's assume we want to insert a new Enterprise into the database. (Enterprise is a common Platform model, defined in com.onenetwork.platform.common.)

       Enterprise enterprise = new Enterprise();
       enterprise.setEntName("NewEnterprise");
       
       ModelList<Enterprise> modelList = new ModelList<Enterprise>(EnterpriseActions.INSERT_OR_UPDATE, enterprise);
       ModelList<Enterprise> results = modelService.write(modelList, userContext);
       if (results.getErrors().size() > 0) {
         throw new Exception("Write failed: " + results.getErrors());
       }
       
      In the example above, we:
      1. Instantiate a new model object in memory (Enterprise)
      2. Set all the properties of the model which we want updated in the database (in this case, we simply set the enterprise name)
      3. Create a new ModelList. For each of its arguments, we provide:
          action - EnterpriseActions.INSERT_OR_UPDATE. This is the name of the action we will invoke in the write operation. Calls to write() are not simple DB updates; they are specific business actions which dictate their own validations, workflows, state transitions, etc. Examples of action names could be things like PLACE_ORDER, CANCEL, etc. EnterpriseActions is a generated class, containing all actions defined in the platform core - likewise, you will have similar generated classes in your module for all actions you define in your MPT.
          model enterprise. This is the actual model(s) to be written. ModelList takes a varargs so you can pass in multiple. You can also call getModels().add(...) after construction to add more.
      4. Write the list. The general contract of the write API is to return a new ModelList containing only those Models whose write operation failed (due to some business validation failure or other constraint). Thus the client should call results.getErrors() after the write to check for errors & respond appropriately.

      Surrogate and Natural Keys
      Models can have ModelLink fields which refer to other Models in the system. For example, Organization has a ModelLink to its parent Enterprise. In the Organization object, this is represented with the following methods:

      • get/setEnterpriseName() - the Natural Key of the referenced Enterprise
      • get/setSysEnterpriseId() - the Surrogate Key of the referenced Enterprise

      When reading a Model from the database, these values are always in sync; the surrogate id will correspond to the Enterprise with matching natural key in the database.

      When writing models, you have a choice as to how you wish to populate these fields; you can pass either natural key, or surrogate id, or both, and the write API will handle it properly. For example, with a new Organization, you can write:

       Organization org = new Organization();
       org.setName("MyOrg");
       org.setEnterpriseName("MyEnt");
       modelDataService.write(new ModelList<Organization>("PLT.InsertOrUpdate", org), userContext);
       
      This will take the given natural key (MyEnt), lookup the corresponding surrogate id, and write both to the database. Alternately, you can write:
       Organization org = new Organization();
       org.setName("MyOrg");
       org.setSysEnterpriseId(10000L, true);
       modelDataService.write(new ModelList<Organization>("PLT.InsertOrUpdate", org), userContext);
       
      This will do the opposite - given the surrogate id (10000), it will lookup the corresponding natural key and write both to the database.

      You will note that there is a boolean parameter in the surrogate id setter. This value specifes whether to nullify Natural Keys when setting the value. In order to ensure that the Natural and Surrogate ids don't get out of synch accidentally, any change to the Natural Key nullifies the Surrogate Id, and any change to the Surrogate Id requires a decision on the client's part as to whether or not to nullify the natural key. This supports both scenarios like the one above, where as a client we are trying to change the surrogate id and we don't know the natural key and don't want the responsibility of setting it, as well as the scenario where we might have both surrogate id and natural key in hand and want to set the NK and then SK, without losing the NK information.

      Changing Natural Keys
      By default, no attempt will be made to change Natural Key fields when updating an existing model. (The surrogate id of the model is ignored for purposes of write.) If you want to change the Natural Key of the model, you should:

      1. set the surrogate id of the model (setSysId()) to match the existing database value
      2. set the natural keys of the model to the new desired values
      3. use the write(ModelWriteRequest, PlatformUserContext) API, setting changeNaturalKeys to true on the model write request

      Additional Notes
      When creating a list of models, the surrogate ids are written to each model instance, so when the write() call returns, you have access to them on the original instances.

      To get insight into the generated SQL and parameter bindings for calls to write(), you can set the following internal package to DEBUG in the logger:

      com.transcendsys.platform.server.trans.write.db
    • write

      <M extends Model> ModelList<M> write​(ModelWriteRequest<M> modelWriteRequest, PlatformUserContext platformContext)
      Create, update or delete models using Actions defined in the MPT or SPT. Similar to write(ModelList, PlatformUserContext), but takes a ModelWriteRequest, which allows additional customization of the overall write operation, including whether to rollback all changes if one or more errors occurred, whether to return processed records in addition to error records, etc.

      To get insight into the generated SQL and parameter bindings for calls to write(), you can set the following internal package to DEBUG in the logger:

      com.transcendsys.platform.server.trans.write.db
      Parameters:
      modelWriteRequest - model write request which qualifies the write operation
      platformContext - callers' context
      Returns:
      error results (or possibly all processed records, depending on the ModelWriteRequest)
    • readByIds

      <T extends Model> java.util.Map<java.lang.Long,​T> readByIds​(java.lang.Class<T> topLevelClass, java.util.Collection<java.lang.Long> sysIds, PlatformUserContext platformContext, ModelRetrieval... retrievals)
      Convenience method for fetching Models by surrogate id. Map returned is keyed by surrogate id, with Model object as value. All model fields are fetched, and all children (recursively). Provide explicit retrievals to fetch only the selected children(s).

      To get insight into the generated SQL and parameter bindings for calls to readByIds(), you can set the following internal package to DEBUG in the logger:

      com.transcendsys.platform.server.trans.read.sql
      Parameters:
      topLevelClass - implementation class for the Model being read (for example, Enterprise)
      sysIds - collection of surrogate ids
      platformContext - caller's context
      Returns:
      Map of surrogate id to Model
    • readByIds

      <T extends Model> java.util.Map<java.lang.Long,​T> readByIds​(java.lang.Class<T> topLevelClass, java.util.Collection<java.lang.Long> sysIds, java.util.Map<java.lang.Class<? extends Model>,​java.util.Collection<java.lang.Long>> includeChildSysIdsByModelLevelType, PlatformUserContext platformUserContext)
      Convenience method for fetching Models by ids and selecting which child model instances to fetch.
      Parameters:
      topLevelClass - implementation class for the Model being read (for example, Enterprise)
      sysIds - collection of surrogate keys of top level model
      includeChildSysIdsByModelLevelType - when provided, this is used as a filter to reduce the children returned; only children with these ids will be included. Please note that this means that a 3rd-level child's parent must also have its id included in order for that 3rd-level child to be returned. The Map should be keyed by ModelLevelType, with value of Collection of surrogate ids.
      platformContext - caller's context
      Returns:
      Map of surrogate id to Model
    • readById

      <T extends Model> T readById​(java.lang.Class<T> topLevelClass, java.lang.Long sysId, PlatformUserContext platformContext, ModelRetrieval... retrievals)
      Convenience method for fetching a single Model by its surrogate id. Model (including all children) is returned, or null if it isn't found. Provide explicit retrievals to fetch only the selected children(s).
      Parameters:
      topLevelClass - implementation class for the Model being read (for example, Enterprise
      sysIds - surrogate id
      platformContext - caller's context
      retrievals - when provided, used as a filter to reduce the children returned;
      Returns:
      Model, or null
    • read

      <T extends Model> java.util.List<T> read​(java.lang.Class<T> topLevelClass, PlatformUserContext context, SqlParams params, ModelQueryComponent... queryComponents)
      Reads Model objects from the database using user-provided filter, sort and retrieval criteria.



      Assume one has a three-level Model, with levels House, Room and Furnishing. To retrieve all Houses from the database, one would use the following:

       List<House> houses = modelDataService.read(House.class, context, null);
       
      Behind the scenes, the query would be formed as follows:
      • Since no explicit filter was given, all Houses in the database would be returned except Houses which the calling user doesn't have permission to (as dictated by the MPT and/or SPT).
      • Since no explicit retrieval was given, all levels of House (House/Room/Furnishing) would be fetched. Thus if each House contains Rooms in the DB, those Rooms would be included in the getRooms() property of House. And if the Rooms contain Furnishings, those would be included in the getFurnishings() property of Room.
      • Since no explicit sort was given, the results would be unsorted and could vary from call to call.



      To retrieve only certain levels of the overall House model, one can provide explicit retrieval:

       import com.onenetwork.platform.data.model.ModelQuery;
       
       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.retrieve(Room.class));
       
      In this case, only the Room level will be retrieved. Furnishings would be completely omitted, even if present in the database. Only key fields from House will be retrieved since it was not explicitly specified; all other fields will be left unset.

      To fetch both Room and Furnishing information (but without pulling all the details of the parent House) you would use the following:

       import com.onenetwork.platform.data.model.ModelQuery;
       
       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.retrieve(Room.class, Furnishing.class));
       



      To filter the data using arbitrary SQL, you can use the ModelQuery.sqlFilter method. The following query will fetch only Houses built in the last year:

       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.sqlFilter("ZHSE_HOUSE.HOUSE_BUILT_DATE > systimestamp - 365"));
       
      Additionally, you may want to provide bound parameters for portions of the queries. To achieve this, SqlParam is used, and the rules of variable substition match those described in that class. For example:
       SqlParams params = new SqlParams();
       params.setStringValue("BUILDER", "A%");
       List<House> houses = modelDataService.read(House.class, context, params, ModelQuery.sqlFilter("ZHSE_HOUSE.BUILDER_NAME LIKE $BUILDER$"));
       
      This would return all Houses whose builder name started with "A".

      You can combine multiple sqlFilters if desired ... these will be automatically AND'ed. Thus the previous example could also be written as:

       SqlParams params = new SqlParams();
       params.setStringValue("BUILDER", "A%");
       List<House> houses = modelDataService.read(House.class, context, params, 
         ModelQuery.sqlFilter("ZHSE_HOUSE.HOUSE_BUILT_DATE > systimestamp - 365"),
         ModelQuery.sqlFilter("ZHSE_HOUSE.BUILDER_NAME LIKE $BUILDER$"));
       

      If you want to combine the filters in ways other than AND, you should simply create one sqlFilter with the entire desired fragment. For example:

       SqlParams params = new SqlParams();
       params.setStringValue("BUILDER", "A%");
       List<House> houses = modelDataService.read(House.class, context, params, 
         ModelQuery.sqlFilter("(ZHSE_HOUSE.HOUSE_BUILT_DATE > systimestamp - 365 and ZHSE_HOUSE.BUILDER_NAME LIKE $BUILDER$) or ZHSE_HOUSE.IS_DEMO_HOUSE = 1"));
       



      To sort data, you can use the sqlSort method. The following query will order the houses by the date they were built, with more recent returned first:

       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.sqlSort("ZHSE_HOUSE.HOUSE_BUILT_DATE DESC"));
       

      This can also include bound parameters, just like filters. For example, this will sort by the built date descending, but if that date is null, it will use "now" as the built date.

       SqlParams params = new SqlParams();
       params.setTimestampValue("DEFAULT_BUILT_DATE", new java.sql.Timestamp(System.currentTimeMillis()));
       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.sqlSort("NVL(ZHSE_HOUSE.HOUSE_BUILT_DATE, $DEFAULT_BUILT_DATE$) DESC"));
       

      You can also provide multiple sorts; these will be implicitly added in the order given. For example, the following will order by built date, followed by builder name as a secondary sort criteria.

       List<House> houses = modelDataService.read(House.class, context, null,
         ModelQuery.sqlSort("ZHSE_HOUSE.HOUSE_BUILT_DATE DESC"),
         ModelQuery.sqlSort("ZHSE_HOUSE.BUILDER_NAME"));
       



      You can combine multiple sort, filter and retrieval as required, in any order. For example:

       SqlParams params = new SqlParams();
       params.setStringValue("BUILDER", "A%");
       params.setTimestampValue("DEFAULT_BUILT_DATE", new java.sql.Timestamp(System.currentTimeMillis()));
       List<House> houses = modelDataService.read(House.class, context, params, 
         ModelQuery.sqlFilter("ZHSE_HOUSE.HOUSE_BUILT_DATE > systimestamp - 365"),
         ModelQuery.sqlFilter("ZHSE_HOUSE.BUILDER_NAME LIKE $BUILDER$"),
         ModelQuery.sqlSort("NVL(ZHSE_HOUSE.HOUSE_BUILT_DATE, $DEFAULT_BUILT_DATE$) DESC"),
         ModelQuery.retrieve(House.class, Room.class));
       
      This query will retrieve Houses built in the last year with builder name starting with "A", order them by built date descending (using "now" as the default when the built date is null), and will return only Houses and Rooms (no Furnishings). Notice that the SqlParam bindings span across all filters/sort.



      To set concurrencyVersion on retrieved model(s) and its children, you can use ModelQuery.concurrencyRetrieval() method.

       import com.onenetwork.platform.data.model.ModelQuery;
       
       List<House> houses = modelDataService.read(House.class, context, null, ModelQuery.concurrencyRetrieval());
       
      In this case, a transient field named ConcurrencyVersion will be set for the retrieved houses and their retrieved child models.

      To get insight into the generated SQL and parameter bindings for calls to read(), you can set the following internal package to DEBUG in the logger:

      com.transcendsys.platform.server.model.read
      Type Parameters:
      T - type of the Model class being read
      Parameters:
      topLevelClass - implementation class for the Model being read (for example, Enterprise
      context - caller's context (the models returned will be constrained by this user's permissions)
      params - if the query requires bound params, this should contain those values - if it does not require params, pass null
      queryComponents - query components, including filter, sort, retrieval, etc - acquired through ModelQuery methods
    • count

      <T extends Model> long count​(java.lang.Class<T> clazz, PlatformUserContext context, SqlParams params, ModelFilter... filters)
      This method is similar to read(Class, PlatformUserContext, SqlParams, ModelQueryComponent...) with one key difference - it returns total count of models.
      Type Parameters:
      T - type of the Model class being read
      Parameters:
      clazz - implementation class for the Model being read (for example, Enterprise
      context -
      params - if the query requires bound params, this should contain those values - if it does not require params, pass null
      filters -
      Returns:
      total count of models in DB
    • readByView

      <T extends Model> java.util.List<T> readByView​(ViewRequest<T> viewReadRequest, PlatformUserContext context)
      Allows a client to programmatically invoke a view which was defined in studio.

      For example, below we read Books using a view "ZBKS.BooksByISBN", which takes a single String filter for the ISBN field:

       ViewRequest<Book> viewRequest = new ViewRequest<Book>(Book.class, "ZBKS.BooksByISBN");
       viewRequest.addFilter(Book.class, "ISBN", "9880*");
       List<Book> books = modelService.readByView(viewRequest, userContext);
       
      Type Parameters:
      T - type of the Model class being read
      Parameters:
      viewReadRequest - describes the view being invoked, any filter values, etc.
      context - caller's context (the models returned will be constrained by this user's permissions)
      Returns:
      Models which are found by the given view invocation
    • readAudit

      <T extends Model> java.util.List<ModelAudit<T>> readAudit​(java.lang.Class<T> modelClass, PlatformUserContext ctx, SqlParams params, ModelQueryComponent... queryComponents)
      Reads audit records for Models from the database using user-provided filter and sort criteria.

      Similar to read(Class, PlatformUserContext, SqlParams, ModelQueryComponent...) in many respects, but with some key differences:

      Supports only "single level" queries.
      You may provide either a root model level or a child model level for the modelClass param. Only the given model level will be queried. ModelRetrieval are not supported in the queryComponents param. If you query a child level, a ModelAudit wrapping the child level will be returned, and the child level model itself will have references to a parent Model object which is sparsely populated with only its NK and sys id. When querying a parent level, none of its children will ever be populated; you must make separate calls to readAudit to read the children.
      Queries _ADT table
      The query will be executed against the _ADT table (e.g. ZBKS_BOOK_ADT instead of ZKBS_BOOK). Therefore, any filter or sort criteria you provide must reference that explicitly, e.g. sqlFilter("ZBKS_BOOK_ADT.CREATION_DATE > $CREATION_DATE$")
      Unpermissioned
      This is an unpermissioned read. It is up to the caller to ensure that audit records are not shared with parties who should not see them (for example, by ensuring the model to which the audits are attached is currently available via readById(Class, Long, PlatformUserContext, ModelRetrieval...). This API returns all matching audits, regardless of permissions.
      Parameters:
      modelClass - model level to be queries. See description above to understand how parent/child relationships are handled
      ctx - caller's context
      params - if the query requires bound params, this should contain those values - if it does not require params, pass null
      queryComponents - query components, including filter, sort, etc - acquired through ModelQuery methods. Does NOT support ModelRetrieval.
      Returns:
      one ModelAudit object per _ADT record matching the given filter criteria. if no sort order provided, will default sort by sysId desc, nvl(ptxDate, lastModifiedDate) desc, ptxVersion desc (i.e. grouped by sys id, with most recent changes first)
    • getViewFilterFields

      <T extends Model> java.util.List<Field> getViewFilterFields​(ViewRequest<T> viewRequest, PlatformUserContext context)
      Returns a collection of Field objects describing the required and optional filter fields available for the given view.
      Parameters:
      viewRequest - describes the view
      context - caller's context
      Returns:
      available filter fields
    • getAvailableActions

      java.util.Map<Model,​java.util.Set<java.lang.String>> getAvailableActions​(java.util.Collection<? extends Model> models, PlatformUserContext context)
      Given a collection of fully-populated Models (i.e. do not give stubs with sysId only), returns a Map of each Model to a Set containing the names of all Actions which are available to be executed on that model at this time by the calling context. The collection returned includes keys for all child models.
      Parameters:
      models - Models for which to fetch actions
      context - caller's context
      Returns:
      Map of each Model to a Set containing the names of all Actions which are available to be executed on that model at this time by the calling context. The collection returned includes keys for all child models.
    • validateWritePermission

      <M extends Model> void validateWritePermission​(ModelWriteRequest<M> modelWriteRequest, PlatformUserContext platformContext)
      Validates write permission for the given models without actually writing models in the DB. If the model doesn't have write permission, error will be set on model.
    • validateReadPermission

      <T extends Model> java.util.Set<java.lang.Long> validateReadPermission​(java.lang.Class<T> topLevelClass, java.util.Set<java.lang.Long> sysIds, PlatformUserContext platformContext)
      Given a Set of top-level model sys ids, executes a read query to check if the given context has access to those records. Returns a Set containing only those ids to which the given context has permissions.