A mechanism for storing and retrieving objects between MATISSE and Eiffel

1st Edition - June 1998

	Table of contents

1. Introduction
	1.1 Goals
	1.2 An example schema
2. Connecting to database
3. Retrieving persistent objects
	3.1 Class MT_STORABLE
	3.2 Retrieving objects
	3.3 Accessing objects
	3.4 Class invariants
	3.5 Attribute types
	3.6 Collection classes for successors
	3.7 Caching Objects
4. Modifying persistent objects
	4.1 Transparent modification
	4.2 Removing attribute values
	4.3 Deletion of objects
5. Creation of new persistent objects
	5.1 Persistent Closure principle
	5.2 Explicit promotion

Appendix-A  A sample application schema
Appendix-B  mt_odl
Appendix-C  How to define a new collection class
Appendix-D  Collection class for relationship
Appendix-E  A sample application program
Appendix-F  Class Hierarchies and client relations

Contact: nakao@matisse.com
© 1998 Tokyo Gas Co., Ltd.

1. Introduction
This document describes the mechanism of the MATISSE-Eiffel binding and gives the basic idea of the way to use it. The MATISSE-Eiffel binding provides a persistent system in which you can build an application reading, creating and storing MATISSE objects with the minimum possible effort.
The design of this binding is based on the following ideas,
1) There is a convenience/efficiency trade-off. In one extreme, everything about retrieving and storing persistent objects is done automatically but we have no control over optimizing when and which objects should be retrieved and stored (vice versa in another extreme). We take a moderate approach.
2) When a MATISSE object is read, a replicated Eiffel object (called replica object) is created dynamically. (In the future, we will provide a proxy mechanism also to access MATISSE objects, which does not replicate the MATISSE objects)
3) During a session, the binding program reads the MATISSE meta-schema and the application schema. These schema are used to retrieve and store persistent objects efficiently. The binding program can create replica objects dynamically. (No specific Eiffel type information is stored in the database)
4) The basic mechanism for retrieving and storing persistent objects does not depend on a set of the Eiffel collection classes. The Eiffel environment provides a set of collection classes such as LIST, ARRAY and TABLE, while MATISSE provides "Relationship" to refer to other objects. However, MATISSE does not need a class library corresponding to the Eiffel collection classes for the basic mechanism. Further enhancements are possible for efficient and flexible handling.
1.1 Goals
1. Access to multiple databases.
We implemented an Eiffel class whose instances represents each database session and manages each database schema as well as persistent object caching. This enables not only access to multiple databases from an application program but also these databases can have the same database schema or same class names.
2. Access to one database from multiple applications
This binding uses only standard MATISSE database schema and meta schema to read and write persistent objects. This enables different application programs to access the same database. Even other applications using other language bindings or something else can access the database.
3. High performance for reading and writing persistent objects.
Persistent objects are retrieved only when they are needed by an application, and are cached in the Eiffel environment. Only updates of attributes and relationships are propagated to the database and there is no extra work to be done at transaction commit.
4. Transparent persistence.
You can let a transient Eiffel object participate in a relationship with another persistent object in the Eiffel environment. Then the transient object becomes persistent automatically. This is called "persistence by reachability". All the updates made on persistent objects are automatically propagated to the database. In addition, you can explicitly promote a transient object into a persistent object by using the procedure 'persist'. 
5. Evolutive development cycle.
The guideline for application development is as follows:
(1)	Define persistent Eiffel classes in the MATISSE ODL (Object Definition Language) format.
(2)	Generate the files of Eiffel classes supporting this binding from the ODL file by running ‘mt_odl’ utility.
(3)	Generate the database schema from the ODL file by running ‘mt_odl’ utility.
(4)	Add your application source code into the generated Eiffel classes.
(5)	In the development life cycle, the definition of Eiffel classes can change. You may update the ODL file. The ‘mt_odl’ utility generates new Eiffel classes while preserving the added application source code.
(6)	Restart the procedures from (3).

1.2 An example schema
To illustrate the design of storing and retrieving persistent objects, we use an example schema. The schema represents BOOK, PUBLISHER and AUTHOR. The detailed schema is shown in the Appendix-A.

2. Connecting to database
To retrieve persistent objects from the database, you must establish a database connection and open a transaction or a version access. The following is a typical code to establish a database connection. See the comments of each routine for its purpose.

start_my_appliation is
	local
		appl : MATISSE_APPL
		session : DB_CONTROL
	do
		!!appl.login("hostname","dbname", 0, 0)
			-- Setting a host name of the database server,
			-- database name, accessing priority and wait time.
		appl.set_mode(OPENED_TRANSACTION,Void)
			-- Setting a mode: transaction or version access.
			-- The second argument is used with the version access
			-- mode to specify a specific version (savetime).
		appl.set_base
			-- Initialization
		!!session.make
			-- Creation of an interface objet to control the database.
		session.connect
			-- Connecting to the database

		... -- Your application source code here
			
		session.commit
			-- Committing the transaction
		session.disconnect
			-- Disconnecting from the database
	end

Note that most example programs in the following sections assume that a database connection is already established and a transaction or version access is opened.
3. Retrieving persistent objects
After establishing a database connection and opening a transaction or version access, you can use the indexes, entry-point or extent of a class (or maybe ODBC in the future) to retrieve objects. The retrieved objects allow you to obtain other objects through the relationship navigation. When you retrieve these objects from the database, the Eiffel objects corresponding to the objects are replicated and cached in the Eiffel environment. The replicated object is called a replica object. See the sample codes in the Appendix-D (and the reference manual: coming soon) for the details on how to write a Eiffel program using this binding.
In this section, we show the basic mechanism for retrieving persistent objects.
3.1 Class MT_STORABLE
A new class MT_STORABLE is introduced. This class is a class from which the descendant classes inherit persistent behaviors. In other words, if you want to store objects of your class into the database, it should inherit from class MT_STORABLE.
Since instances of the classes BOOK, PUBLISHER and AUTHOR should be persistent, these classes inherit from class MT_STORABLE. For example, the definition of class BOOK looks like,

class
	BOOK

inherit
	MT_STORABLE

feature
	title: STRING;
	price: REAL;
	written_by: MT_ARRAY [AUTHOR]
	publisher: PUBLISHER

feature
	.....
end -- class BOOK

3.2 Retrieving objects
MATISSE provides several ways to access objects in the database: entry point, index, extent of a class and SQL In this version of MATISSE-Eiffel binding, we provide entry point, index and extent of a class. For the details on these methods, refer to the MATISSE documentation. In the following sections, simple examples using these ways are shown.
3.2.1 Entry point
An entry point enables you to access an instance by means of the value of one of its properties.
To get instances through the entry point, you first create an instance a_mt_ep of class MT_ENTRYPOINT with arguments, an attribute name and a class name. A class name can be void. Then you call the function retrieve_objects on the object a_mt_ep with an argument, entry point value. The function returns an array of objects accessed through the entry point. 
The following sample function returns an array of objects of class BOOK which are accessed through an entry point.

find_books_from_title(book_title: STRING) : ARRAY[BOOK] is
	local
		ep: MT_ENTRYPOINT
	do
		!!ep.make_from_name(“title”, “BOOK”)
		Result ?= ep.retrieve_objects(book_title)
	end

3.2.2 Indexes
Indexes allow you to access an object stream. An index is defined by one up to four criteria, attributes that are attached to the same class.
To scan the index, you first create an instance of class MT_INDEX with an argument, an index name. Then you specify a start value and an end value for each criterion. After specifying these values, you call the function open_stream on the instance, which returns a stream object, an instance of class MT_STREAM. You can scan objects through this stream object.
The following sample procedure prints all the objects of class BOOK whose titles are between a start value and an end value specified by the arguments.

find_and_print_books_using_index(start_v, end_v: STRING) is
	local
		an_index: MT_INDEX
		index_stream: MT_STREAM
		criterion: MT_INDEX_CRITERION
	do
		!!an_index.make("BookTitleIndex")
		criterion := an_index.criteria.item(0)
		criterion.set_start_value(start_v)
		criterion.set_end_value(end_v)
		index_stream := an_index.open_stream
		from
			index_stream.start
		until
			index_stream.exhausted
		loop
			print(index_stream.next_object)
			index_stream.forth
		end
		index_stream.close
end

3.2.3 Extent of class
An extent of a class allows you to get all the instances of the class and its subclasses.
To get the instances, you first create an instance of class MT_CLASS with an argument, a class name. Then you call the function all_instances on the instance, which returns an array containing all the instances of the class. You may call the function open_stream on the instance of class MT_CLASS, which returns a stream to access the instances successively.
The following sample procedure prints all the instances of a class specified by the argument.

print_all_instances_of_class(a_class_name: STRING) is
	local
		a_class: MT_CLASS
		all_instances: ARRAY[MT_STORABLE]
		i: INTEGER
	do
		!!a_class.make_from_name(a_class_name)
		all_instances := a_class.all_instances
		from
			i := all_instances.lower
		until
			i > all_instances.upper
		loop
			print(all_instances.item(i))
			i := i + 1
		end
	end

3.3 Accessing objects
When a replica object is created, it contains neither attribute values nor relationship successors. You need to call routines that retrieve values of attributes or successors of relationships. These routines are categorized into two groups. One group supports “Transparent loading” and another does “Explicit loading”
The following two sections describe these two methods.
3.3.1 Accesses through transparent loading
This accessing method relies on the automatic Eiffel code generation. The MATISSE command-line utility mt_odl reads an ODL (Object Definition Language) definition and generates an Eiffel code which supports this ‘Transparent loading’. For the details on the utility mt_odl, see the Appendix-B
The generated code contains functions to access attribute values or relationship successors. Names of these functions begin with the letters ‘get_’ followed by their attribute name or relationship name. The following is a part of the code generated from the ODL definition shown in the Appendix-A

class
	BOOK
... Something else ...

feature -- Accessing
	get_title : STRING is
		do
			if title = Void then
				title := mt_get_string_by_position(field_position_of_title)
			end
			Result := title
		end
		
	get_price : REAL is
		do
			if price = Real_default_value then
				price := mt_get_real_by_position(field_position_of_price)
			end
			Result := price
		end
	
	get_written_by : ARRAY[AUTHOR] is
		do
			if is_persistent then
				written_by.load_successors
			end
			Result := written_by
		end
	
	get_publisher : PUBLISHER is
		do
			if publisher = Void then
				publisher ?= mt_get_successor_by_position(field_position_of_publisher)
			end
			Result := publisher
		end	
end -- class BOOK

The function to access attribute value, such as get_title or get_price, checks if the value is read from the database. If not, it reads the value from the database and assigns the value to the corresponding field, such as title or price, then returns it. If the object is not a persistent one, the function just returns the value of its field.
In the sample schema defined in the Appendix-A, the maximum cardinality of the relationship written_by is unlimited while that of the relationship publisher is one. When we define the class BOOK in Eiffel which corresponds to the database schema, we usually use one of the collection classes as an Eiffel type for the relationship written_by and use just the class PUBLISHER as an Eiffel type for the relationship publisher. 
Here, we call the relationship whose maximum cardinality is one single-relationship, and call the relationship whose maximum cardinality is more than one multiple-relationship.
The function to access relationship successors, such as get_written_by or get_publisher, checks if the successors are read from the database. If not, it reads the successors from the database and creates replica objects. The function has two slightly different succeeding behaviors according to the relationship’s cardinality
Single-Relationship:	The function to access the single-relationship, like get_publisher, then assigns the replica object to the corresponding field, publisher, and returns it. 
Multiple-Relationship:	The function to access the multiple-relationship, like get_written_by, then assigns the replica objects to the container object, like written_by and returns the container object.
If the object is not persistent, the function just returns the object of its field without accessing the database.
As long as  you use these accessing functions to access attribute values or relationship successors, you don’t have to care about when and how the values or successors are read from the database. They are read from the database only once when they are needed first. The later accesses to the same attribute or relationship do not need to read the database.

<<Note>>
Actually this approach violates the “Command-Query Separation principle”. The legitimate approach is, for example, to prepare a procedure load_title which reads a value from the database and assign it to the field title. So you should access the field title after calling the procedure load_title. If you want to access the field without calling load_title once the field value is loaded, you need to manage whether or not the field value is already loaded from the database. Since this work tends to be very complicated, you usually call load_title every time before you access the field title. This is the reason why we think our approach is practical and useful.
3.3.2 Accesses through explicit loading and accessing
This accessing method does not provide such an automatic loading as defined in the section 3.3.1. You have to call these routines defined in this section by yourself to access attribute values or relationship successors. You also have to be aware of when these values or successors are read from the database. However, you have all controls over when and which values or successors are accessed and loaded. This method does not need the automatic code generation.
Several routines to access attribute values or relationship successors are listed in the following code. Since these routines are defined in class MT_STORABLE, you can call them on an instance of, for example, class BOOK

class 
	MT_STORABLE

feature

	mt_load_all_values
		-- Load values of all attributes of the current object
	mt_load_all_successors
		-- Load successors of all relationships of the current object
	mt_load_all_properties
		-- Load both attributes and relationships
end	

The procedures mt_load_all_values, mt_load_all_successors and mt_load_all_properties read all values or all successors and assign them to each field of the target replica object. With these procedures, you do not have to take care of attribute name or relationship name. Once these procedures are called on an object, you can access directly the Eiffel attributes, such as title, price, written_by and publisher in the class BOOK.
On the other hand, the class MT_STORABLE provides also the Get functions, such as mt_get_integer_by_name etc., which just return a value or successors of a specific property and do not assign them to the field of the target. These functions read values or objects from the database every time they are called. They do not read values or successors that are loaded and cached in the Eiffel environment. Some of them are listed below:
feature
	mt_get_value_by_name (attr_name: STRING): ANY
			-- Return the value of the attribute specified by 'attr_name'
	mt_get_value_by_position (index: INTEGER): ANY
			-- Return the value of the attribute field-positioned at 'index'
	mt_get_integer_by_name (attr_name: STRING): INTEGER
	mt_get_integer_by_position (index: INTEGER): INTEGER
	mt_get_string_by_name (attr_name: STRING): STRING
	mt_get_string_by_position (index: INTEGER): STRING

	mt_get_successor_by_name (rs_name: STRING): MT_STORABLE
			-- Return the first successor object of Current through
			-- the relationship specified by 'rs_name'
			-- This is usually used to get a successor of 'sigle-relationship'
	mt_get_successor_by_position (index: INTEGER): MT_STORABLE
			-- Same as 'mt_get_successor_by_name' except that relationship
			-- is specified by field position
	mt_get_successors_by_name (rs_name: STRING): MT_RS_CONTAINABLE [MT_STORABLE]
			-- Return an array of successor objects of Current through
			-- the relationship specified by 'rs_name'
	mt_get_successors_by_position (index: INTEGER): MT_RS_CONTAINABLE[MT_STORABLE]
			-- Same as 'mt_get_successors_by_name' except that relationship
			-- is specified by field position

	... more functions ...
end

When you use these functions, you need to specify an attribute or relationship by giving its name or field position as an argument. The name of an attribute or relationship should be same as the one used to define the Eiffel class. The field position of an attribute or relationship is an index used to obtain internal object properties by using some functions defined in class INTERNAL. (If a class is generated by the automatic code generation tool, mt_odl, then the functions to get the field position are provided in the class. Their names start from ‘field_position_of’. These are actually used in the code shown in the section 3.3.1)
These Get functions are provided for very specific purposes such as when you need access only one attribute of a replica object without loading all attribute values, or without assigning the returned value to the replica object.
The following sample procedures print all properties of a book using the procedure mt_load_all_properties.

print_properites_of_book(book_title: STRING) is
	local
		a_book: BOOK
		an_ep: MT_ENTRYPOINT
		books: ARRAY[BOOK]
		i: INTEGER
	do
		!!an_ep.make_from_name("title", "BOOK")
		books ?= an_ep.retrieve_objects(book_title)
		from i := books.lower
		until i > books.upper
		loop
			a_book := books.item(i)
			a_book.mt_load_all_properties
			print(a_book.title)
			print(a_book.price)
			print(a_book.publisher)
			print(a_book.written_by)
			i := i + 1
		end
	end

The sample code above can be rewritten by using the Get functions as follows,

print_properites_of_book_using_get_functions(book_title: STRING) is
	local
		a_book: BOOK
		an_ep: MT_ENTRYPOINT
		books: ARRAY[BOOK]
		i: INTEGER
	do
		!!an_ep.make_from_name("title", "BOOK")
		books ?= an_ep.retrieve_objects(book_title)
		from i := books.lower
		until i > books.upper
		loop
			a_book := books.item(i)
			print(a_book.mt_get_string_by_name("title"))
			print(a_book.mt_get_real_by_name("price"))
			print(a_book.mt_get_successor_by_name("publisher"))
			print(a_book.mt_get_successors_by_name("written_by"))
			i := i + 1
		end
	end

3.4 Class invariants
Here we come across a new question. What happens to the invariant clause of class BOOK before attributes or relationships are loaded? Right after a replica object of class BOOK is created, neither attribute values nor relationship successors are loaded. So, what if class BOOK should use attribute 'title' in its invariant clause? It may cause an unnecessary error.
To avoid this situation, we recommend to use implies in invariant clause so that the invariant clause returns true if the object is not persistent, or its properties are not loaded yet. 
When you are using the transparent loading described in the section 3.3.1, you can write a class invariant as shown in the following code.

class BOOK
.....
invariant
	title_not_void: is_persistent implies get_title /= Void
	publisher_not_void: is_persistent implies get_publisher /= Void

In the example, if an object is not persistent, the succeeding expressions, get_title /= Void or get_publisher /= Void, are not evaluated. Note that this approach assumes that the current database session is set properly whenever this invariant clause is evaluated.
When you are using the explicit loading described in the section 3.3.2, you can write a class invariant as shown in the following code.

class BOOK
.....
invariant
	title_not_void: attributes_loaded implies title /= Void
	publisher_not_void: relationships_loaded implies publisher /= Void

Note that the value of attributes_loaded or relationships_loaded becomes true after the procedure mt_load_all_values, mt_load_all_successors or mt_load_all_properties is called.

3.5 Attribute types
The following table shows the MATISSE data types and Eiffel types to which they correspond.

MATISSE data type	Eiffel type
MT_BOOLEAN	BOOLEAN
MT_CHAR, MT_ASCIICHAR	CHARACTER
MT_DATE	DATE
MT_TIMESTAMP	DATE_TIME
MT_TIME_INTERVAL	DATE_TIME_DURATION
MT_U8, MT_S16, MT_U16, MT_S32, MT_U32	INTEGER
MT_FLOAT	REAL
MT_DOUBLE	DOUBLE
MT_STRING, MT_ASCIISTRING	STRING
MT_U8_LIST	LINKED_LIST[CHARACTER]
MT_S16_LIST, MT_U16_LIST	LINKED_LIST[INTEGER]
MT_S32_LIST, MT_U32_LIST	LINKED_LIST[INTEGER]
MT_FLOAT_LIST	LINKED_LIST[REAL]
MT_DOUBLE_LIST	LINKED_LIST[DOUBLE]
MT_STRING_LIST, MT_ASCIISTRING_LIST	LINKED_LIST[STRING]
MT_U8_ARRAY	ARRAY[CHARACTER]
MT_S16_ARRAY, MT_U16_ARRAY	ARRAY[INTEGER]
MT_S32_ARRAY, MT_U32_ARRAY	ARRAY[INTEGER]
MT_FLOAT_ARRAY	ARRAY[REAL]
MT_DOUBLE_ARRAY	ARRAY[DOUBLE]
MT_STRING_ARRAY, MT_ASCIISTRING_ARRAY	ARRAY[STRING]

Note that multiple dimensional array for all array data types, such as  MT_S32_ARRAY, are not supported.

3.6 Collection classes for successors
The ISE Eiffel library provides a very rich set of collection classes such as ARRAY, LIST, TREE etc. MATISSE, however, provides the 'Relationship' mechanism to refer to a collection of objects. It would be possible to build collection classes equivalent to the ones defined in the Eiffel library. This would imply that the basic mechanism of retrieving and storing objects would depend on those collection classes. We prefer a simple mechanism which does not need the existence of those collection classes.
The first reason for this approach is conceptual: a container object itself should not be persistent whenever relationship can be used instead. The second reason is simplicity: we do not want to assume the existence of the equivalent collection class library in MATISSE as a part of the basic binding mechnism. The third reason is performance: if we were to build the equivalent collection class library in the MATISSE database, it would require more database accesses to reach the actual successor object.
3.6.1 Class MT_RS_CONTAINABLE
We introduce a new class MT_RS_CONTAINABLE. This is a deferred class, whose descendants classes can contain successor objects of multiple-relationship. The following shows an example descendant class. 

class
	MT_ARRAY[G]

inherit
	ARRAY[G]
	
	MT_RS_CONTAINABLE[G]

creation
  .....

We already defined three classes that inherit from class MT_RS_CONTAINABLE and each of the three frequently used collection classes: ARRAY, LINKED_LIST and ARRAYED_LIST. The new classes are respectively called MT_ARRAY, MT_LINKED_LIST and MT_ARRAYED_LIST. You need to use these classes when you define Eiffel classes corresponding to MATISSE classes that have ‘multiple-relationship’. The behaviors of these classes are identical to that of each class’s ancestor class respectively except that it can contain successor objects through a relationship.
When a replica object is created, actually an empty collection object is created for each multiple-relationship and assigned to the corresponding field of the replica object. For example, when a replica object of class BOOK is created, an instance of class MT_ARRAY is created and is assigned to the field written_by. But the instance of class MT_ARRAY contains no successor. (This was not mentioned in the previous sections just for simplicity.)
To load all the successor objects from the database, the procedure load_successors is called on the collection object. Once successors are loaded, further calls of load_successors on the same collection object have no effect. The procedure load_successors is used in other routines internally, for example, get_written_by from class BOOK, mt_load_all_successors and mt_get_successors_by_name from class MT_STORABLE. Usually you do not need to use this procedure.
You may use a stream, instead of calling load_successors, to retrieve successors from the database. This stream mechanism offers the possibility to visit successively the successors of an object through a relationship. The successors are not read from the server unless they are applied a read operation. The reading operation on the stream does not put the successors in the collection object. The function open_stream returns a stream, through which you can retrieve the successors successively by using the routines start, forth and item. It is illustrated on the following example.

print_all_authors_of_book(book_title: STRING) is
		-- Using stream
	local
		a_stream: MT_STREAM
		an_author: AUTHOR
		a_book: BOOK
		an_ep: MT_ENTRYPOINT
	do
		!!an_ep.make_from_name("title", "BOOK")
		a_book ?= an_ep.retrieve_first(book_title)
		if a_book /= Void then
			a_stream := a_book.written_by.open_stream
			from
				a_stream.start
			until
				a_stream.exhausted
			loop
				print(a_stream.item)
				a_stream.forth
			end
			a_stream.close
		end
	end

3.6.2 Collection class and application
We have to think about two layers in the application development. One is the "persistent classes layer" and another is the "application layer". The former is a layer for classes whose instances are stored in the database and these classes can be defined in the ODL file. The latter is an application (including GUI and so on) which uses these persistent classes.
This binding uses MT_ARRAY, MT_LINKED_LIST and so on to define the persistent classes. But the interfaces of the “Get” function, such as “get_written_by”, in the persistent classes are defined using the standard classes such as ARRAY or LINKED_LIST, since MT_ARRAY or MT_LINKED_LIST inherit from ARRAY or LINKED_LIST. Hence the application layer does not need to depend on the MT_ARRAY or MT_LINKED_LIST but can depend on the standard collection library. The business model or object model in the application layer can be portable.
3.6.2 Defining new collection class
Now let us look at a part of the definition of the class MT_RS_CONTAINABLE. You may skip this part on first reading

deferred class
	MT_RS_CONTAINABLE[G->MT_STORABLE]

feature
	load_successors is
		do ...
		end
	open_stream : MT_RELATIONSHIP_STREAM is
		do ...
		end
		
feature -- Status
	count : INTEGER is
		deferred
		end
		
feature -- Conversion
	linear_representation: LINEAR[G] is
		deferred
		end

feature {NONE}  -- Implementation
	mt_put_at_loading(new: G; i: INTEGER) is
		deferred
		end

	mt_resize_at_loading(new_size: INTEGER) is
		deferred
		end
		
end -- class MT_RS_CONTAINABLE

You can see that descendant classes of class MT_RS_CONTAINABLE should redefine the routines count, linear_representation, mt_put_at_loading and mt_resize_at_loading. Those four routines are necessary to retrieve and store successor objects. For example, to store all the successor objects in a collection, the function linear_representation is used. The function returns a linear representation of the collection, and then each successor object in it is stored to the database. If you want to define a new descendant class of MT_RS_CONTAINABLE, you need to redefine these routines. See the Appendix-C for the details on how to define the new descendant class. 
NOTE:
In this version of MATISSE-Eiffel binding, the default collection class for relationship is the class MT_LINKED_LIST. If you want to use other class, see the Appendix-D. This specification will be changed in the next version.
3.7 Caching Objects
The objects retrieved from the database are cached in the Eiffel environment to maintain the object identity within a transaction or version access. For example, suppose that object O1 refers to object O3, and object O2 refers to object O3 in the database. You retrieve O1 and then retrieve O3 through relationship navigation from O1, and get replica objects RO1 and RO3 respectively. And you retrieve O2 and then O3 through O2, and get replica objects RO2 and RO3 respectively. Since O3 is already retrieved from the database and RO3 is cached in an object cache, new replica object for O3 is not created this time but just RO3 is returned from the object cache.

Object identity is not maintained across transactions or version accesses. For example, you retrieve an object P1 in a transaction and keep the replicated Eiffel object RP1 after the transaction is closed. Later, you start another transaction and retrieve the same object P1 from the database. The Eiffel replica object for P1, this time, is not identical to RP1.
When a retrieved object is not referred to from any other objects, the object ought to be a candidate for garbage collection. However, it is not so, because the MATISSE-Eiffel binding holds the object in its object cache to maintain object identity. If Eiffel provides a "weak reference" facility, the object cache can be implemented using the weak reference facility so that the object just described can be reclaimed by the garbage collector. We provide the procedure remove_from_cache to remove an object from the object cache and remove the corresponding object from the MATISSE client space as well. The procedure is defined in the class MT_STORABLE. 
The object cache is created per database connection.

4. Modifying persistent objects
Replica objects can be updated only in a transaction. The class MT_STORABLE provides several procedures to store these updates to the database. These procedures are categorized into two groups as well as the accessing functions just described. One group supports ‘Transparent modification’ and another does ‘Explicit modification marking’. In this version of MATISSE-Eiffel binding, we provide only ‘Transparent modification’ method. The following section describes the method.
4.1 Transparent modification
This method relies on the automatic Eiffel code generation. The MATISSE utility mt_odl reads an ODL definition and generates Eiffel classes which support this Transparent modification. See the Appendix-B for the details on the automatic Eiffel code generation.
The generated code includes procedures to update attribute values or single-relationship successor. Modifications of multiple-relationship are described in the next section. The following code is generated from the ODL definition in the Appendix-A by the code generator.

class BOOK
... something else ...

feature -- Modification
	set_price(new_price: REAL) is
		do
			price := new_price
			mt_set_value(field_position_of_price)
		end

	set_title(new_title: STRING) is
		do
			title := clone(new_title)
			mt_set_value(field_position_of_title)
		end

	set_publisher(new_publisher: PUBLISHER) is
		do
			check_persistence(new_publisher)
			publisher := new_publisher
			mt_set_successor(field_position_of_publisher)
		end
end

As long as you use these procedures to update objects, you do not have to do anything else to store the updates to the database. The modifications made through these procedures are immediately stored to the database.
4.1.1 Modification of relationships
The procedures to update a successor of a single-relationship are provided in the code generated by the code generator (for example, set_publisher in the code above). The procedure set_publisher first calls the procedure check_persistence with the argument new_publihser. The procedure check_persistence promotes the argument object and its all dependents into persistent objects if these objects are transient. This is known as “Persistence by reachability”.
On the other hand, no procedures are generated to update successors of multiple-relationships. Because the collection classes such as LINKED_LIST, ARRAYED_LIST or ARRAY provide a lot of procedures to change its elements and these procedures are suited to add or remove successor objects. The classes MT_LINKED_LIST, MT_ARRAYED_LIST and MT_ARRAY redefine these procedures so that the updates can be stored to the database and the “Persistence by reachability” is applied to the added successor objects.
The following code is an example procedure defined in class PUBLISHER which supports adding successors.

add_books(books: ARRAY[BOOK]) is
	local
		i: INTEGER
	do
		from i := books.lower
		until i > books.upper
		loop
			published_books.extend(books.item(i))
				-- Adding successor object.
				-- If books.item(i) is transient, it is promoted into persistent object.
			i := i + 1
		end
	end

4.2 Removing attribute values
The procedure mt_remove_value_by_name or mt_remove_value_by_position defined in class MT_STORABLE allows you to remove an attribute value of a replica object. The procedure mt_remove_value_by_name takes an argument attribute_name of type STRING to specify the attribute by its name, while mt_remove_value_by_position takes an argument position of type INTEGER to specify the attribute by its field position. Once the procedure is called on an object, the specified attribute value of the object becomes unspecified in the database. However, the procedure does not change the corresponding field of the replica object.
mt_remove_value_by_name(attribute_name: STRING)
mt_remove_value_by_position(position: INTEGER)

The following procedure is defined in class BOOK. It removes the value of the attribute price.

remove_price is
	do
		price := 0
		mt_remove_value_by_position(field_position_of_price)
	end

4.3 Deletion of objects
The procedure mt_remove defined in class MT_STORABLE allows you to delete an object from the database. The replica object is removed also from the object cache maintained by the MATISSE-Eiffel binding. Note that the replica object still remains in the Eiffel environment until it is collected by the garbage collector. If an object is not persistent, the procedure mt_remove does nothing. This procedure must be called inside a transaction. 
The following code is a procedure using mt_remove.

delete_book(book_title: STRING) is
	local
		ep: MT_ENTRYPOINT
		a_book: BOOK
	do
		!!ep.make_from_name("title", "BOOK")
		a_book ?= ep.retrieve_first(book_title)
		if a_book = Void then
			print("The book is not found%N")
		else
			a_book.mt_remove
		end
	end

5. Creation of new persistent objects
There are two ways to promote a transient object into a persistent one. One is the way based on the "Persistence Closure principle". This is also called "Persistence by reachability". The other is an explicit promotion of a transient object into a persistent one.
5.1 Persistence Closure principle
When an object O1 is referenced from another object that is already persistent, the object O1 will be promoted into a persistent object. Here is an example,

create_new_book(new_book_title: STRING; a_publisher: PUBLISHER)
	require
		a_publisher_is_persistent: a_publisher.is_persistent
	local
		new_book: BOOK
	do
		!!new_book.make(new_book_title)
			-- Some more settings on new_book.
			-- The object ‘new_book’ is transient.
		a_publisher.get_published_books.extend(new_book)
			-- Now, the object ‘new_book’ is promoted  into a persistent object.
	end

5.2 Explicit promotion
The procedure persist allows you to explicitly promote a transient object into a persistent one. This procedure is defined in class MATISSE. Here is an example code using persist.

create_new_author(name: STRING) : AUTHOR
	do
		!!Result.make(clone(name))
		.... -- Some more operations on Result
		current_db.persist(Result)
			-- The function 'current_db' returns an instance of class MATISSE which represents
			-- currently selected database session.
			-- It is defined in class MT_CONSTANTS
	end

Appendix-A Sample Application Schema

This section describes the sample application schema that is mostly used throughout the example programs in the previous chapters. The schema is described in the MATISSE ODL format. See the document “MATISSE OOS ODL Programming Guide” for the details on the MATISSE ODL.

interface BOOK : persistent {
	attribute String title
		mt_make_entry “make-entry”;
	attribute Float price;
	relationship List<AUTHOR> written_by 
		inverse AUTHOR::books;
	relationship PUBLISHER publisher
		inverse PUBLISHER::published_books;
	mt_index BookTitleIndex
		criteria {BOOK::title MT_ASCEND 32};
};

interface PERSON : persistent {
	attribute String name
		mt_make_entry “make-entry”;
	attribute Long birth_year;
};

interface AUTHOR : PERSON : persistent {
	relationship List<BOOK> books
		inverse BOOK::written_by;
};

interface PUBLISHER : persistent {
	attribute String name
		mt_make_entry “make-entry”;
	relationship List<BOOK> published_books
		inverse BOOK::publisher;
};

Appendix-B mt_odl
Syntax	mt_odl -eiffel odl_file
Arguments	odl_file	The file name of the ODL definition
Purpose	This utility reads an ODL file odl_file and generates Eiffel class files..
Description	For example, this utility generates four files, book.e, person.e, author.e and publisher.e from the ODL definition in the Appendix-A. These files are generated in the directory where the utility is executed.
	You can add your own codes in these generated files. Your codes should be located outside the two tags given by this utility. A generated files has two tags, ‘BEGIN’ and ‘END’ as shown below:
class BOOK
-- BEGIN generation of accessors by mt_odl
...
feature
	title: STRING
	...
-- END of mt_odl generation of accessors

-- Your application source code here

end -- class BOOK

	When you modify your ODL file and regenerate the Eiffel class files, the utility mt_odl reads both the ODL file and the existing Eiffel class files. If your own source code is located correctly in each class, your source code will be preserved in the regenerated Eiffel files.

Appendix-C How to define a new collection class

In this section, we show the method to define a new collection class which can contain successor objects. To show the method, let us define a new class which inherits from both class MT_RS_CONTAINABLE and class LINKED_STACK. The followings are the procedures to define it.
1) Define the creation procedure make which takes no argument.
This procedure initializes a collection object with a default size. We can use the creation procedure make which is inherited from the class LINKED_LIST.
2) Define the procedure mt_put_at_loading. 
This procedure is used to put a successor object in a collection object when the procedure load_successors is called on the collection object.
The procedure mt_put_at_loading takes two arguments, v and i. The first one v is a new successor object to be added and the second one i is an integer representing the positional index of the successor object in the relationship. The procedure is called for all the successors in the relationship.
We think it is normal to push the successor objects onto the stack in the same order as that of the successors list. That means first pushing the object at the first position in the successors list, then pushing the object at the second position and so on. Hence, the procedure mt_put_at_loading just call the procedure extend with the argument v.
3) Define the procedure mt_resize_at_loading.
This procedure is used to resize a collection object when the procedure load_successors is called on the collection object.
The procedure mt_resize_at_loading is called from the procedure load_successors with an argument, new_size of type INTEGER. This new_size is a number of the successors in the relationship in question. The collection object should be resized (if necessary) so that it can hold all the successors.
Since the procedure extend of class LINKED_STACK extends the stack itself, the procedure mt_resize_at_loading of class MT_LINKED_STACK does not have to do anything.
4) Define the function linear_representation.
This function is used to get all the successor objects in a collection object in a linear order when the successor objects need to be stored to the database.
The function linear_representation is defined in one of the ancestor classes, LINKED_STACK. However, the order of the list returned by this function is reverse of the original order of insertion. We need to redefine the function so that the order would be the same as the original insertion order.
5) Define the function count
 This function is inherited from the class LINKED_LIST. We do not have to redefine it.

The resulted class definition is shown as follows:

class
	MT_LINKED_STACK[G->MT_STORABLE]

inherit
	LINKED_STACK[G]
		redefine
			linear_representation
		end
		
	MT_RS_CONTAINABLE[G]
	
creation
	make

feature
	mt_put_at_loading(v: G; i: INTEGER) is
		do
			extend(v)
		end

	mt_resize_at_loading(new_size: INTEGER) is
		-- Do nothing
		do
		end

	linear_representation: ARRAYED_LIST [G] is
			-- Representation as a linear structure
			-- (order is the same of original order of insertion)
		local
			old_cursor: CURSOR
		do
			old_cursor := cursor;
			from
				!! Result.make (count);
				start
			until
				after
			loop
				Result.put_front (ll_item);
				forth
			end;
			go_to (old_cursor)
		end

end -- class MT_LINKED_STACK

Appendix-D Collection class for relationship
In this version of MATISSE-Eiffel binding, the Eiffel class for a MATISSE multiple-relationship is defined as a constant in class MT_CONTAINER_TYPES. The default Eiffel class for a MATISSE multiple-relationship is class MT_LINKED_LIST. If you want to use other class, then you need to add a line in the function Container_class_names. The following code is an example of class MT_CONTAINER_TYPES.
class
	MT_CONTAINER_TYPES

feature {NONE} -- constants
	
	Container_class_names: HASH_TABLE[STRING, STRING] is
		-- Value is a container class name in upper case
		-- Key is a MATISSE relationship name.
		once
			!!Result.make(20)	
			Result.put("MT_ARRAY", "BOOK__written_by")
			Result.put("MT_ARRAYED_LIST", "PUBLISHER__published_books")
		end
end -- class MT_CONTAINER_TYPES

The function returns a constant hash table whose key is a MATISSE relationship name and whose value is Eiffel class name. For example, if you want to use class MT_ARRAY for the relationship “AUTHOR__books”, you should add the following line in the function.
Result.put("MT_ARRAY", "AUTHOR__books")

Appendix-E A sample application

----------------------------------------------------------
class 	PERSON
----------------------------------------------------------

inherit
-- BEGIN generation of inheritance by mt_odl
	MT_STORABLE
-- END of mt_odl generation of inheritance

-- BEGIN generation of accessors by mt_odl
feature
	name: STRING
	birth_year: INTEGER

feature -- Access
	get_name: STRING is
		do
			if name = Void then
				name := mt_get_string_by_position(field_position_of_name)
			end
			Result := name
		end

	get_birth_year: INTEGER is
		do
			if birth_year = Integer_default_value then
				birth_year := mt_get_integer_by_position(field_position_of_birth_year)
			end
			Result := birth_year
		end

feature -- Modification
	set_name(new_name: STRING) is
		do
			name := clone(new_name)
			mt_set_string(field_position_of_name)
		end

	set_birth_year(new_birth_year: INTEGER) is
		do
			birth_year := new_birth_year
			mt_set_integer(field_position_of_birth_year)
		end

feature {NONE} -- Implementation
	field_position_of_name: INTEGER is
		once
			Result := field_position_of("name")
		end

	field_position_of_birth_year: INTEGER is
		once
			Result := field_position_of("birth_year")
		end

-- END of mt_odl generation of accessors

--
-- You may add your own code here...
--
end -- class PERSON

----------------------------------------------------------
class 	AUTHOR
----------------------------------------------------------

inherit
-- BEGIN generation of inheritance by mt_odl
	PERSON
		redefine
			field_position_of_name,
			field_position_of_birth_year
		end
-- END of mt_odl generation of inheritance

-- BEGIN generation of accessors by mt_odl
feature
	books: MT_LINKED_LIST[BOOK]

feature -- Access
	get_books: LINKED_LIST[BOOK] is
		do
			if is_persistent then
				books.load_successors
			end
			Result := books
		end

feature {NONE} -- Implementation
	field_position_of_name: INTEGER is
		once
			Result := field_position_of("name")
		end

	field_position_of_birth_year: INTEGER is
		once
			Result := field_position_of("birth_year")
		end

-- END of mt_odl generation of accessors

--
-- You may add your own code here...
--
end -- class AUTHOR

----------------------------------------------------------
class 	BOOK
----------------------------------------------------------

inherit
-- BEGIN generation of inheritance by mt_odl
	MT_STORABLE
-- END of mt_odl generation of inheritance

-- BEGIN generation of accessors by mt_odl
feature
	title: STRING
	price: REAL
	written_by: MT_ARRAY[AUTHOR]
	publisher: PUBLISHER

feature -- Access
	get_title: STRING is
		do
			if title = Void then
				title := mt_get_string_by_position(field_position_of_title)
			end
			Result := title
		end

	get_price: REAL is
		do
			if price = Real_default_value then
				price := mt_get_real_by_position(field_position_of_price)
			end
			Result := price
		end

	get_written_by: ARRAY[AUTHOR] is
		do
			if is_persistent then
				written_by.load_successors
			end
			Result := written_by
		end

	get_publisher: PUBLISHER is
		do
			if publisher = Void then
				publisher ?= mt_get_successor_by_position(field_position_of_publisher)
			end
			Result := publisher
		end

feature -- Modification
	set_title(new_title: STRING) is
		do
			title := clone(new_title)
			mt_set_string(field_position_of_title)
		end

	set_price(new_price: REAL) is
		do
			price := new_price
			mt_set_real(field_position_of_price)
		end

	set_publisher(new_publisher: PUBLISHER) is
		do
			check_persistence(new_publisher)
			publisher := new_publisher
			mt_set_successor(field_position_of_publisher)
		end

feature {NONE} -- Implementation
	field_position_of_title: INTEGER is
		once
			Result := field_position_of("title")
		end

	field_position_of_price: INTEGER is
		once
			Result := field_position_of("price")
		end

	field_position_of_publisher: INTEGER is
		once
			Result := field_position_of("publisher")
		end

-- END of mt_odl generation of accessors

end -- class BOOK

----------------------------------------------------------
class	PUBLISHER
----------------------------------------------------------

inherit
-- BEGIN generation of inheritance by mt_odl
	MT_STORABLE
-- END of mt_odl generation of inheritance

-- BEGIN generation of accessors by mt_odl
feature
	name: STRING
	published_books: MT_ARRAYED_LIST[BOOK]

feature -- Access
	get_name: STRING is
		do
			if name = Void then
				name := mt_get_string_by_position(field_position_of_name)
			end
			Result := name
		end

	get_published_books: ARRAYED_LIST[BOOK] is
		do
			if is_persistent then
				published_books.load_successors
			end
			Result := published_books
		end

feature -- Modification
	set_name(new_name: STRING) is
		do
			name := clone(new_name)
			mt_set_string(field_position_of_name)
		end

feature {NONE} -- Implementation
	field_position_of_name: INTEGER is
		once
			Result := field_position_of("name")
		end

-- END of mt_odl generation of accessors

end -- class PUBLISHER

----------------------------------------------------------
class	EXAMPLE_APP
----------------------------------------------------------

inherit
	MT_CONSTANTS
	
	INTERNAL

	ARGUMENTS
	
creation
	make

feature -- Initialization
	make is
		do
			if arg_number /= 3 then
				print_usage
			else
			!!appl.set_login(argument(1), argument(2), 0, 0)
				-- Setting a host name of the database server,
				-- database name, accessing priority and wait time.
			appl.set_base
				-- Initialization
			appl.connect
				-- Connecting to the database
			appl.start_transaction
			
			-- Data creation --
			
			read_file_and_store_objects
				-- Read a text file in the example project directory.
				-- Then create new objects of the class BOOK, AUTHOR
				-- and PUBLISHER, and store these objects in the database.
						
		-- The followings are the examples for this document --
			
			find_and_print_books_from_title("Eiffel The Language")
				-- An example of entry point
								
			find_and_print_books_using_index("M", "N")
				-- An example of index
				
			print_all_instances_of_class("PUBLISHER")
				-- An example of class of extent
				
			print_all_instances_by_stream("PUBLISHER")
				-- Another example of class of extent
				-- This uses stream mechanism.

			print_properites_of_book("Design Patterns") 
				-- This uses 'mt_load_all_properties'.
				
			print_properites_of_book_using_mt_get_functions("Design Patterns")
				-- This uses 'mt_get_string_by_name' etc.

			print_all_authors_of_book_transparent("Design Patterns")
				-- Get successors using transparent loading mechanism.

			print_all_authors_of_book_explicit("Design Patterns")
				-- Get successors using explicit loading mechanism.

			print_all_authors_of_book_using_stream("Design Patterns")
				-- Get successors using stream.

			remove_price_of_book("Wireless Information Networks")
				-- Remove an attribute value
			
			delete_book("Distributed Algorithms")
				-- Delete an object
			
			print_publisher_and_authors_of_book_explicit("Design Patterns")
			print_publisher_and_authors_of_book_transparent("Design Patterns")
				-- These two illustrate the difference between the transparent
				-- loading feature and the explicit loading feature.
									
			appl.commit
				-- Committing the transaction
			appl.disconnect
				-- Disconnecting from the database
			end
		end

	print_usage is
		do
			print("Usage:%N")
			print("	Specify arguments <hostname> and <database_name>%N")
			print("	Right-click on the Run button and type your host name and database name in this order%N")
		end

feature

	read_file_and_store_objects is
		-- Read text file which is created by read_from_bookst_and_file_out.
		-- Create instances of PUBLISHER, AUTHOR
		-- and BOOK. Then store them in a database
		-- Section 5.1 and 5.2
		local 
			pubs: HASH_TABLE[PUBLISHER, STRING]
			authors: HASH_TABLE[AUTHOR, STRING]
			books: ARRAY[BOOK]
			a_pub: PUBLISHER
			a_book: BOOK
			an_author: AUTHOR
			i, j, n, count: INTEGER
			a_file: PLAIN_TEXT_FILE
			author_name, pub_name: STRING
			message: STRING
		do
			message := "This program is going to update the database "
			message.append(appl.database_name)
			message.append(" in host ")
			message.append(appl.host_name)
			print(message)
			print("%NIs it OK? (yes or no) ")
			io.readline
			if io.laststring.is_equal("yes") then
			!!a_file.make_open_read("..\..\books.dat")
			
			-- Creating PUBLISHER object
			a_file.read_integer
			count := a_file.last_integer
			!!pubs.make(10)
			from  i := 1
			until  i > count
			loop
				a_file.readline
				!!a_pub
				a_pub.set_name(a_file.laststring)
				pubs.put(a_pub, a_pub.name)
				i := i + 1
			end

			-- Creating AUTHOR object
			a_file.read_integer
			count := a_file.last_integer
			!!authors.make(10)
			from  i := 1
			until  i > count
			loop
				a_file.readline
				!!an_author
				an_author.set_name(a_file.laststring)
				a_file.read_integer
				an_author.set_birth_year(a_file.lastint)
				authors.put(an_author, an_author.name)
				i := i + 1
			end

			-- Creating BOOK object
			a_file.read_integer
			count := a_file.last_integer
			from  i := 1
			until  i > count
			loop
				a_file.readline
				!!a_book
				a_book.set_title(a_file.laststring)
				current_db.persist(a_book)
					-- Explicit promotion of transient object into persistent one
				
				a_file.read_integer
				a_book.set_price(a_file.last_integer)
				
				a_file.readline
				pub_name := a_file.laststring
				a_book.set_publisher(pubs.item(pub_name))
					-- "Persistence by reachability"
				
				a_file.read_integer
				n := a_file.last_integer
				from  j := 1
				until  j > n
				loop
					a_file.readline
					author_name := a_file.laststring
					a_book.written_by.force(authors.item(author_name), j)
						-- "Persistence by reachability"
					j :=  j + 1
				end
				i := i + 1
			end
			
			a_file.close
			end
		end

feature -- Programs in the document

	find_and_print_books_from_title(a_title: STRING) is
		-- Use entry-point
		-- section 3.2.1
		local
			ep: MT_ENTRYPOINT
			an_array: ARRAY[BOOK]
			i: INTEGER
		do
			print("%N%Nfind_and_print_books_from_title%N")
			!!ep.make_from_name("title", "BOOK")
			an_array ?=  ep.retrieve_objects(a_title)
			from  i := an_array.lower
			until  i > an_array.upper
			loop
				print(an_array.item(i))
				i := i + 1
			end
		end

	find_and_print_books_using_index(start_v, end_v: STRING) is
		-- Section 3.2.2
		local
			an_index: MT_INDEX
			index_stream: MT_STREAM
			criterion: MT_INDEX_CRITERION
		do
			!!an_index.make("BookTitleIndex")
			criterion := an_index.criteria.item(0)
			criterion.set_start_value(start_v)
			criterion.set_end_value(end_v)
			index_stream := an_index.open_stream
			from  index_stream.start
			until  index_stream.exhausted
			loop
				index_stream.item.mt_load_all_values
				print(index_stream.item)
				index_stream.forth
			end
			index_stream.close
	end

	print_all_instances_of_class(a_class_name: STRING) is
		-- Section 3.2.3
		local
			a_class: MT_CLASS
			all_instances: ARRAY[MT_STORABLE]
			i: INTEGER
		do
			print("%N%Nprint_all_instances%N")
			!!a_class.make_from_name(a_class_name)
			all_instances := a_class.all_instances
			from  i := all_instances.lower
			until  i > all_instances.upper
			loop
				print(all_instances.item(i))
				i := i + 1
			end
		end
		
	print_all_instances_by_stream(a_class_name: STRING) is
		-- Use stream to print all instances of a class
		-- Section 3.2.3
		local
			a_class: MT_CLASS
			a_stream: MT_CLASS_STREAM
			i: INTEGER
		do
			print("%N%Nprint_all_instances_by_stream%N")
			!!a_class.make_from_name(a_class_name)
			a_stream := a_class.open_stream
			from  a_stream.start
			until  a_stream.exhausted
			loop
				print(a_stream.item)
				a_stream.forth
			end
			a_stream.close
		end
		
	print_properites_of_book(book_title: STRING) is
		-- Example of mt_load_all_values
		local
			a_book: BOOK
			an_ep: MT_ENTRYPOINT
			books: ARRAY[BOOK]
			i: INTEGER
		do
			print("%N%Nprint_properites_of_book%N")
			!!an_ep.make_from_name("title", "BOOK")
			books ?= an_ep.retrieve_objects(book_title)
			from i := books.lower
			until	i > books.upper
			loop
				a_book := books.item(i)
				a_book.mt_load_all_properties
				print(a_book.title)
				print(a_book.price)
				print(a_book.publisher)
				print(a_book.written_by)
				i := i + 1
			end
		end
	
	print_properites_of_book_using_mt_get_functions(book_title: STRING) is
		-- Example of mt_get_* functions
		local
			a_book: BOOK
			an_ep: MT_ENTRYPOINT
			books: ARRAY[BOOK]
			i: INTEGER
		do
			print("%N%Nprint_properites_of_book_using_mt_get_functions%N")
			!!an_ep.make_from_name("title", "BOOK")
			books ?= an_ep.retrieve_objects(book_title)
			from i := books.lower
			until i > books.upper
			loop
				a_book := books.item(i)
				print(a_book.mt_get_string_by_name("title"))
				print(a_book.mt_get_real_by_name("price"))
				print(a_book.mt_get_successor_by_name("publisher"))
				print(a_book.mt_get_successors_by_name("written_by"))
				i := i + 1
			end
		end		

	print_all_authors_of_book_transparent(book_title: STRING) is
		-- This verson uses get_written_by instead of stream.
		-- Section 3.6
		local
			authors: ARRAY[AUTHOR]
			a_book: BOOK
			an_ep: MT_ENTRYPOINT
			i: INTEGER
		do
			print("%N%Nprint_all_authors_of_book: Using get_written_by%N")
			!!an_ep.make_from_name("title", "BOOK")
			a_book ?= an_ep.retrieve_objects(book_title).item(1)
			authors := a_book.get_written_by
			from  i := authors.lower
			until  i > authors.upper
			loop
				print(authors.item(i))
				i := i + 1
			end
		end
		
	print_all_authors_of_book_explicit(book_title: STRING) is
		-- This verson uses load_successors instead of stream
		-- Section 3.6
		local
			authors: MT_ARRAY[AUTHOR]
			a_book: BOOK
			an_ep: MT_ENTRYPOINT
			i: INTEGER
		do
			print("%N%Nprint_all_authors_of_book_3%N")
			!!an_ep.make_from_name("title", "BOOK")
			a_book ?= an_ep.retrieve_first(book_title)
			authors ?= a_book.written_by
			authors.load_successors
			from  i := authors.lower
			until  i > authors.upper
			loop
				print(authors.item(i))
				i := i + 1
			end
		end
	
	print_all_authors_of_book_using_stream(book_title: STRING) is
		-- Section 3.6
		local
			a_stream: MT_STREAM
			an_author: AUTHOR
			a_book: BOOK
			an_ep: MT_ENTRYPOINT
		do
			print("%N%Nprint_all_authors_of_book: Using stream%N")
			!!an_ep.make_from_name("title", "BOOK")
			a_book ?= an_ep.retrieve_first(book_title)
			if a_book /= Void then
				a_stream := a_book.written_by.open_stream
				from  a_stream.start
				until  a_stream.exhausted
				loop
					print(a_stream.item)
					a_stream.forth
				end
				a_stream.close
			end
		end

	remove_price_of_book(book_title: STRING) is
		-- Remove the value of attribute 'price'
		-- Section 4.2
		local
			ep: MT_ENTRYPOINT
			a_book: BOOK
		do
			print("%N%NRemove value%N")
			!!ep.make_from_name("title", "BOOK")
			a_book ?= ep.retrieve_first(book_title)
			if a_book /= Void then
				a_book.mt_remove_value_by_name("price")
			end
		end
	
	delete_book(book_title: STRING) is
		-- Section 4.3
		local
			ep: MT_ENTRYPOINT
			a_book: BOOK
		do
			print("%N%Ndelete book: ")
			print(book_title) io.new_line
			!!ep.make_from_name("title", "BOOK")
			a_book ?= ep.retrieve_first(book_title)
			if a_book = Void then
				print("The book is not found%N")
			else
				a_book.mt_remove
			end
		end

feature
	print_publisher_and_authors_of_book_explicit(book_title: STRING) is
		-- Use 'mt_load_all_successors' and 'mt_load_all_values'
		-- This is using explicit loading.
		local
			a_book: BOOK
			a_publisher: PUBLISHER
			authors: MT_ARRAY[AUTHOR]
			an_ep: MT_ENTRYPOINT
			i: INTEGER
		do
			print("%N%Nprint_publisher_and_authors_of_book%N")
			!!an_ep.make_from_name("title", "BOOK")
			a_book ?= an_ep.retrieve_first(book_title)
			if a_book /= Void then
				a_book.mt_load_all_successors
				a_publisher := a_book.publisher
				a_publisher.mt_load_all_values
				print(a_publisher.name)
				authors := a_book.written_by
					-- authors is still empty
				from
					authors.load_successors
					i := authors.lower
				until
					i > authors.upper
				loop
					authors.item(i).mt_load_all_values
					print(authors.item(i).name)
					i := i + 1
				end
			end
		end
	
	print_publisher_and_authors_of_book_transparent(book_title: STRING) is
		-- Use get_* functions, that is transparent loading
		local
			a_book: BOOK
			a_publisher: PUBLISHER
			authors: ARRAY[AUTHOR]
			an_ep: MT_ENTRYPOINT
			i: INTEGER
		do
			print("%N%Nprint_publisher_and_authors_of_book_2%N")
			!!an_ep.make_from_name("title", "BOOK")
			a_book ?= an_ep.retrieve_objects(book_title).item(1)
			if a_book /= Void then
				a_publisher := a_book.get_publisher
				print(a_publisher.get_name)
				authors := a_book.get_written_by
				from  i := authors.lower
				until  i > authors.upper
				loop
					print(authors.item(i).get_name)
					i := i + 1
				end
			end
		end

feature {NONE}
	appl : MATISSE_APPL

end -- class EXAMPLE_APP

----------------------------------------------------------
class	MT_CONTAINER_TYPES
----------------------------------------------------------

feature {NONE}
	container_class_for_relationship (rel_name: STRING): STRING is
		-- Find the class name for a MATISSE relationship 'rel_name'
		-- Default class is MT_LINKED_LIST.
		do
			container_class_names.search (rel_name)
			if container_class_names.found then
				Result := container_class_names.found_item
			else
				Result := "MT_LINKED_LIST"
			end
		end

	Container_class_names: HASH_TABLE[STRING, STRING] is
		-- Value is a container class name in upper case.
		-- Key is a MATISSE relationship name.
		once
			!!Result.make(20)
			Result.put("MT_ARRAY", "BOOK__written_by")
			Result.put("MT_ARRAYED_LIST", "PUBLISHER__published_books")
		end

end -- class MT_CONTAINER_TYPES

Appendix-F Class Hierarchies and Client Relations