Edit this page

# Transactions

Strolch Transactions play a central role in a Strolch agent. A transaction is opened for a realm, and grants access to the model of the agent. Transactions are implemented as a Java `try-with-resources` by implementing the `AutoCloseable` interface. This makes it trivial to understand the scope of a transaction.

Transactions handle the following:

- Opening and closing database connections
- Releasing locks to strolch elements, if `tx.lock(StrolchRootElement)` or `tx.lock(Locator)` was called
- Performing Commands by executing them in the added order, and validating them first.
- Exception handling
- Auditing
- Updating observers

When a transaction is opened, it is by default read-only, i.e. does not perform any commands when it is closed. Should the TX perform commands, then it is important to call `tx.commitOnClose()`, but only at the end of the work, so that exception handling can properly work if something goes wrong.

`StrolchTransaction` offers a myriad of methods:

- find element by its `Locator`
- get methods for elements by type and id, or using a `StringParameter` or `StringListParameter` references
- methods to add, update or remove elements
- assert privilege access
- get a new element by its template
- check if an element exists by type and id
- get streams for elements
- add commands for execution

Transactions are opened by accessing the realm, but there are convenience methods depending on the use-case:

- In Services: by calling one of the `openTx()`-methods
- In Commands: Transactions are already open, use method `tx()` to get instance.
- REST API: `RestfulStrolchComponent.openTx()`

Note: don’t open a new TX inside a TX for the same realm!

Important is to always open the transaction as a `try-with-resource` block and to define if the TX should commit, or not:

    try (StrolchTransaction tx = openTx(...)) {

      // read lock our object
      Locator ferrariLoc = Resource.locatorFor("Car", "ferrari");
      tx.lock(ferrariLoc);

      // find a car by locator
      Resource ferrari = tx.findElement(ferrariLoc);

      // get a car by ID
      Resource opel = tx.getResourceBy("Car", "opel", true);

      // modify ball
      opel.setName("Opel Corsa");
      tx.update(opel);

      // get by string reference
      StringParameter ownerP = ferrari.getParameter("relations", "owner", true);
      Resource owner = tx.getResourceBy(ownerP, true);

      // get by string list reference
      StringListParameter previousOwnersP = opel.getParameter("relations", "previousOwners", true);
      List<Resource> previousOwners = tx.getResourcesBy(previousOwnersP, true);

      // check resource exists
      if (tx.hasResource("Car", "audi")) {
        Resource audi = tx.getResourceBy("Car", "audi", true);

        // assert has privilege to remove a car
        tx.assertHasPrivilege(Operation.REMOVE, audi);

        // remove the car
        tx.remove(audi);
      }

      // iterate all cars
      tx.streamResources("Car").forEach(car -> {
      	logger.info("Car: " + car.getId());
      });

      // commit if TX was changed
      if (tx.needsCommit())
        tx.commitOnClose();
    }