Dealing with references
In ABEL, a basic type is an object of type
Inserting objects with references
Let's look at the new class
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
However, there are some additional caveats to take into consideration.
Let's consider a simple example with
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 singleCHILD 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
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.