Updating entity instances in query results

Entities that you iterate across in a query result are read-only by default. The query builder APIs load iterated entities into a read-only bundle. A bundle is a collection of entities loaded from the database into server memory that represents a transactional unit of information. The read-only bundles that contain iterated query results are separate from the active read-write bundles of running code. You cannot update the properties of query result entities while they remain in the read-only bundle of the result. In many uses of query results, you need to write changed records to the database. For example, you use custom batch processing to flag contacts that your users need to call the next day.

Moving entities from query results to writable bundles

To change the properties of entities in query results, you must move the entities from the query result to a writable bundle. To move an entity to a writable bundle, call the add method on the writable bundle and save the result of the add method in a variable. Whenever you pass an entity from a read-only bundle to a writable bundle, the add method returns a clone of the entity instance that you passed to the method.

If you move an entity from a query result to a writable bundler, store the entity reference that the add method returns in a variable. Then, modify the properties on the saved entity reference. Do not modify properties on the original entity reference that remains in the result set or its iterator. Avoid keeping any references to the original entity instance whenever possible.

Moving entities from query results to the current bundle

Most programming contexts have a writable bundle that the application prepares and manages. For example, all rule execution contexts and PCF code has a current bundle. When a current bundle exists, get it by using the getCurrent method.

Typically, whenever you want to update an entity in a query result, you move it to the current bundle. While you iterate the result, add each entity to the current bundle and make changes to the version of it that the add method returns. After you finish iterating the query result and the execution context finishes, the application commits all the changes that you made to the database.

Important: Entities must not exist in more than one writable bundle at a time.

For example:

// Get the current bundle from the programming context. 
var bundle = gw.transaction.Transaction.getCurrent()

var query = gw.api.database.Query.make(Address)
query.compare(Address#State, Equals, typekey.State.TC_IL)
var result = query.select().orderBy(QuerySelectColumns.path(Paths.make(Address#City)))

for (address in result) {
  switch (address.City) {
    case "Schaumburg":
    case "Melrose Park":
    case "Norridge":
      break
    default: {
      // Add the address to the current bundle to make it writable and
      // discard the read-only reference obtained from the query result.
      address = bundle.add(address)
      // Change properties on the writable address.
      address.Description =
        // Use a Gosu string template to concatenate the current description and the new text.
        "${address.Description}; This city is not Schaumburg, Melrose Park, or Norridge."
    }
  }
}

At the time the execution context for the preceding code finishes, the application commits all changes made to addresses in the current bundle to the database.

The Gosu Scratchpad does not have a current bundle, so you must create a new bundle. To run the preceding code in the Gosu Scratchpad, use the method Transaction.runWithNewBundle instead of Transaction.getCurrent. Enclose the remaining code in a code block:

gw.transaction.Transaction.runWithNewBundle( \ bundle -> {
  var query = gw.api.database.Query.make(Address)
...
}, "su")

To see the effects of the preceding code, run the following Gosu code.

// Query the database for addresses in Illinois.
var query = gw.api.database.Query.make(Address)
query.compare(Address#State, Equals, typekey.State.TC_IL)

// Configure the result to be ordered by City. 
var result = query.select()
result.orderBy(QuerySelectColumns.path(Paths.make(Address#City)))

// Iterate and print the result set. 
for (address in result) {
  print(address.City + ", " + address.Description)
}

See also