Dealing with references

In ABEL, a basic type is an object of type STRING, BOOLEAN, CHARACTER or any numeric class like REAL or INTEGER. The PERSON class only has attributes of a basic type. However, an object can contain references to other objects. ABEL is able to handle these references by storing and reconstructing the whole object graph (an object graph is roughly defined as all the objects that can be reached by recursively following all references, starting at some root object).

Inserting objects with references

Let's look at the new class CHILD:

class CHILD create make feature {NONE} -- Initialization make (first, last: STRING) -- Create a new child. require first_exists: not first.is_empty last_exists: not last.is_empty do first_name := first last_name := last age := 0 ensure first_name_set: first_name = first last_name_set: last_name = last default_age: age = 0 end feature -- Access first_name: STRING -- The child's first name. last_name: STRING -- The child's last name. age: INTEGER -- The child's age. father: detachable CHILD -- The child's father. feature -- Element Change celebrate_birthday -- Increase age by 1. do age := age + 1 ensure age_incremented_by_one: age = old age + 1 end set_father (a_father: CHILD) -- Set a father for the child. do father := a_father ensure father_set: father = a_father end invariant age_non_negative: age >= 0 first_name_exists: not first_name.is_empty last_name_exists: not last_name.is_empty end

This adds some complexity: Instead of having a single object, ABEL has to insert a CHILD's mother and father as well, and it has to repeat this procedure if their parent attribute is also attached. The good news is that the examples above will work exactly the same.

However, there are some additional caveats to take into consideration. Let's consider a simple example with CHILD objects Baby Doe, John Doe and Grandpa Doe. From the name of the object instances you can already guess what the object graph looks like:


Now if you insert Baby Doe, ABEL will by default follow all references and insert every single object along the object graph, which means that John Doe and Grandpa Doe will be inserted as well. This is usually the desired behavior, as objects are stored completely that way, but it also has some side effects we need to be aware of:

  • Assume an insert of Baby Doe has happened to an empty database. If you now query the database for CHILD objects, it will return exactly the same object graph as above, but the query result will actually have three items as the object graph consists of three single CHILD objects.
  • The insert of John Doe and Grandpa Doe, after inserting Baby Doe, is internally changed to an update operation because both objects are already in the database. This might result in some undesired overhead which can be avoided if you know the object structure.

In our main tutorial class START we have the following two features that show how to deal with object graphs. You will notice it is very similar to the corresponding routines for the flat PERSON objects.

insert_children -- Populate the repository with some children objects. local c1, c2, c3: CHILD transaction: PS_TRANSACTION do -- Create the object graph. create c1.make ("Baby", "Doe") create c2.make ("John", "Doe") create c3.make ("Grandpa", "Doe") c1.set_father (c2) c2.set_father (c3) print ("Insert 3 children in the database.%N") transaction := repository.new_transaction -- It is sufficient to just insert "Baby Joe", -- as the other CHILD objects are (transitively) -- referenced and thus inserted automatically. if not transaction.has_error then transaction.insert (c1) end if not transaction.has_error then transaction.commit end if transaction.has_error then print ("An error occurred during insert!%N") end end print_children -- Print all children in the repository local query: PS_QUERY[CHILD] do create query.make repository.execute_query (query) -- The result will also contain -- all referenced CHILD objects. across query as person_cursor loop print (person_cursor.item) end query.close end

Going deeper in the Object Graph

ABEL has no limits regarding the depth of an object graph, and it will detect and handle reference cycles correctly. You are welcome to test ABEL's capability with very complex objects, however, please keep in mind that this may impact performance significantly.