mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 15:22:31 +01:00
Update wikipage Dealing with references. (Signed-off-by:jocelyn).
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@1460 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -1,150 +1,150 @@
|
||||
[[Property:title|Dealing with references]]
|
||||
[[Property:weight|-6]]
|
||||
[[Property:uuid|ebf8e495-8180-bad2-f063-54fb949298e0]]
|
||||
In ABEL, a basic type is an object of type <e>STRING</e>, <e>BOOLEAN</e>, <e>CHARACTER</e> or any numeric class like <e>REAL</e> or <e>INTEGER</e>.
|
||||
The <e>PERSON</e> 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 <e>CHILD</e>:
|
||||
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
|
||||
This adds in some complexity:
|
||||
Instead of having a single object, ABEL has to insert a <e>CHILD</e>'s mother and father as well, and it has to repeat this procedure if their parent attribute is also attached.
|
||||
The good news are 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 <e>CHILD</e> 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:
|
||||
|
||||
[[Image: Child object graph | center | 700px]]
|
||||
|
||||
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 <e>CHILD</e> 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 <e>CHILD</e> 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 <e>START</e> 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 <e>PERSON</e> objects.
|
||||
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
|
||||
==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.
|
||||
|
||||
[[Property:title|Dealing with references]]
|
||||
[[Property:weight|-6]]
|
||||
[[Property:uuid|1190C592-50E0-4834-9D60-E1E357921335]]
|
||||
In ABEL, a basic type is an object of type <e>STRING</e>, <e>BOOLEAN</e>, <e>CHARACTER</e> or any numeric class like <e>REAL</e> or <e>INTEGER</e>.
|
||||
The <e>PERSON</e> 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 <e>CHILD</e>:
|
||||
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
|
||||
This adds some complexity:
|
||||
Instead of having a single object, ABEL has to insert a <e>CHILD</e>'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 <e>CHILD</e> 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:
|
||||
|
||||
[[Image:Child object graph | center | 700px]]
|
||||
|
||||
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 <e>CHILD</e> 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 <e>CHILD</e> 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 <e>START</e> 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 <e>PERSON</e> objects.
|
||||
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
|
||||
==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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user