Show/Hide Toolbars

TMS Aurelius Documentation

Navigation: Manipulating Objects

Concurrency Control

Scroll Prev Top Next More

When working with multiple users/clients, it might be possible that two or more users try to change the same entity (records in database). TMS Aurelius provides some mechanisms to avoid problems in those situations.

 

Changed fields

 

When updating objects, Aurelius detects which property have changed since the entity was loaded from the database in the manager, and it only updates those columns in the database. For example, suppose two users load the same TCustomer (with same id) from the database at the same time:

 

// User1
User1Customer := Manager1.Find<TCustomer>(1);
// User2
User2Customer := Manager2.Find<TCustomer>(1);

 

Now first user changes customer's city and update, and second user changes customer's document and update:

 

// User1
User1Customer.City := 'New City';
Manager1.Flush;
// User2
User2Customer.Document := '012345';
Manager2.Flush;

 

Here are the SQL executed by Aurelius for each user (SQL were simplified for better understanding, the actual SQL uses parameters):

 

User1:
Update Customer 

Set City = 'New City' 

Where Id = 1

 
User2:
Update Customer 

Set Document = '012345' 

Where Id = 1

 

Even if TCustomer class has lots of customer, and some properties might be outdated in memory, it doesn't cause any trouble or data loss here, because only changed data will be commited to the database. In the end, the TCustomer object in database will have both the new city and new document correct.

 

This is a basic mechanism that solves concurrency problems in many cases. If it's not enough, you can use entity versioning.

 

Entity Versioning

 

It might be possible that two users change the exactly same property, in this case, one of the users will "lose" their changes, because it will be overwritten by the other user.  Or some other types of operations are performed where all fields are updated (when entity is put in manager without being loaded from database for example, so the manager can't tell which properties were changed).

 

Or maybe you just need to be sure that the object being updated needs to hold the very latest data. A typical case is where you are updating account balance or inventory records, so you increment/decrement values and need to ensure that no other user changed that data since you loaded.

 

In this case, you can use entity versioning. To accomplish this, you just need to create an extra integer property in the class, map it (so it's persisted in database) and add the [Version] attribute to it:

 

[Entity, Automapping]
TCustomer = class
private
  FId: Integer;
  FName: String
  {...}
  [Version]
  FVersion: Integer;
  {...}
end;

 

And that's it. Once you do this, Aurelius will make sure that if you update (or delete) an entity, data it holds is the very latest one. If it's not, because for example another user changed the database record in the meanwhile, an exception will be raised and then you can decide what to do (refresh the object for example).

 

Let's take a look at how it works. First, two users load the same object at the same time:

 

// User1
User1Customer := Manager1.Find<TCustomer>(1);

// User1Customer.Version is 1 

 
// User2
User2Customer := Manager2.Find<TCustomer>(1);

// User1Customer.Version is 1 

 

Then User1 updates customer:

 

User1Customer.City := 'New City';
User1Customer.Flush;
// User1Customer.Version becomes 2 (also in database)

 

This is the SQL executed by Aurelius:

 

Update Customer 
Set City = 'New City', Version = 2 
Where Id = 1 and Version = 1

 

Record is changed successfully because the current version in database is 1, so the actual record is updated.

 

Now, if User2 tries to update the old customer:

 

// User2Customer.Version is still 1!
User2Customer.City := 'Another city';
User2Customer.Flush;

 

Aurelius tries to execute the same SQL:

 

Update Customer 
Set City = 'Another City', Version = 2 
Where Id = 1 and Version = 1

 

However this will fail, because the version in the database is not 1 anymore, but 2. Aurelius will detect that no records were affected, and will raise an EVersionedConcurrencyControl exception.