Object Types in Oracle - Oracle PL/SQL by Example, Fifth Edition (2015)

Oracle PL/SQL by Example, Fifth Edition (2015)

Chapter 23. Object Types in Oracle


In this chapter, you will learn about

Image Object Types

Image Object Type Methods


In Oracle, object types are the main ingredient of object-oriented programming. They are used to model real-world tangible entities, such as students, instructors, and bank accounts, as well as abstract entities, such as ZIP codes, geometrical shapes, and chemical reactions.

In this chapter, you will learn how to create object types, and how to nest object types within collection types. In addition, you will learn about different kinds of object type methods and their usage.

This chapter is introductory in nature and does not cover more advanced topics such as object type inheritance and evolution, REF modifiers, and object type tables (not to be confused with collections). These topics, along with many others, are covered in Oracle’s documentation—specifically, Oracle’s Database Object-Relational Developer’s Guide.

Lab 23.1: Object Types


After this lab, you will be able to

Image Create Object Types

Image Use Object Types with Collections


Object types generally consist of two parts: attributes (data) and methods (functions and procedures). Attributes are essential characteristics that describe the object type. For example, some attributes of the student object type may be first and last names, contact information, and enrollment information. Methods are functions and procedures defined in an object type; they are optional. They represent actions that are likely to be performed on the object attributes. For example, methods of the student object type might update the student contact information, get the student’s name, or display the student’s information.

By combining attributes and methods, object types facilitate encapsulation of data with the operations that may be performed on that data. As an example, Figure 23.1 shows the object type Student. Some of the attributes of the Student object type are Student ID, First Name,Zip, and Employer, and some of the methods are Update Contact Info, Get Student ID, and Get Student Name. Figure 23.1 also shows two instances of the object type, Student 1 and Student 2. An object instance is a value of an object type. In other words, the instances Student 1 and Student 2 of the Student object type contain actual student data so that the Get Student ID method returns student ID 102 for instance Student 1 and 103 for instance Student 2.

Image

Figure 23.1 Object Type Student


Did You Know?

An object instance is often referred to simply as an object.


In Oracle, an object type is created with the CREATE OR REPLACE TYPE clause and is stored in the database schema. As a consequence, object types cannot be created within a PL/SQL block or stored subprogram. Once an object type has been created and stored in the database schema, a PL/SQL block or subprogram may reference that object type.

Creating Object Types

The general syntax for creating an object type is shown in Listing 23.1 (the reserved words and phrases surrounded by brackets are optional):

Listing 23.1 Create Object Type

CREATE [OR REPLACE] TYPE type_name AS OBJECT
(attribute_name1 attribute_type,
attribute_name2 attribute_type,
...
attribute_nameN attribute_type,
[method1 specification],
[method2 specification],
...
[methodN specification]);
[CREATE [OR REPLACE] TYPE BODY type_name AS
method1 body;
method2 body;
...
methodN body;]
END;

Notice that the creation of an object type includes two parts: the object type specification and the object type body. The object type specification contains declarations of attributes as well as any methods that may be used with that object. The attribute_type may be a built-in PL/SQL type such as NUMBER or VARCHAR2, or it may be a complex user-defined type such as a collection, record, or other object type. The method specification consists of the method type, its name, and any input and output parameters the method needs.

Object specification is required when creating an object type. Any attributes and methods defined in the object type specification are visible to the outside world (such as a PL/SQL block, subprogram, or Java application). The object type specification is also called a public interface, and the methods defined in it are called public methods. As mentioned earlier, methods are optional when creating object types. However, if an object type has a method specification, it requires an object type body.

The object type body is optional when creating an object type. This part of the script contains the bodies (executable statements) of the methods defined in the object type specification. In addition, the object type body may contain methods that have not been defined in the object type specification. These methods are private—that is, they are not visible to the outside world. Some types of methods that might be specified as private are constructor, member, and static methods. Different method types, their usage, and restrictions are discussed in detail in Lab 23.2.

Note that the concepts explained to this point are very similar to those you learned about in Chapter 21, dealing with packages. Thus, rules that apply to the package specification and body mostly apply to the object type specification and body as well. For example, the header of the method defined in the object type specification must match the method header in the object type body.

Consider the following example of the zipcode_obj_type object type specification.

For Example ch23_1a.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
(zip VARCHAR2(5)
,city VARCHAR2(25)
,state VARCHAR2(2)
,created_by VARCHAR2(30)
,created_date DATE
,modified_by VARCHAR2(30)
,modified_date DATE);

This object type does not have any methods associated with it, and its syntax is somewhat similar to the CREATE TABLE syntax.

Once this object type has been created, it can be used as demonstrated in the following example:

For Example ch23_2a.sql

DECLARE
zip_obj zipcode_obj_type;
BEGIN
SELECT zipcode_obj_type(zip, city, state, null, null, null, null)
INTO zip_obj
FROM zipcode
WHERE zip = '06883';

DBMS_OUTPUT.PUT_LINE ('Zip: '||zip_obj.zip);
DBMS_OUTPUT.PUT_LINE ('City: '||zip_obj.city);
DBMS_OUTPUT.PUT_LINE ('State: '||zip_obj.state);
END;

This script defines an instance zip_obj of the object type zip_code_obj_type. It then initializes some of the object attributes and displays those values on the screen.

The object attributes are initialized via the SELECT INTO statement. Note how the SELECT clause uses an object type constructor. Recall that you learned about constructors for nested table types in Chapter 15. Default constructors for object types are similar in that they are system-defined functions that have the same name as the corresponding object type. In Lab 23.2, you will learn how to define your own constructor functions.

When run, the preceding script produces this output:

Zip: 06883
City: Weston
State: CT

When an object instance is defined, its value is null. This means that not only its individual attributes are null, but the object itself is also null. The object remains null until its constructor method is called, as illustrated in the next example.

For Example ch23_3a.sql

DECLARE
zip_obj zipcode_obj_type;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Object instance has not been initialized');

IF zip_obj IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj instance is null');
ELSE
DBMS_OUTPUT.PUT_LINE ('zip_obj instance is not null');
END IF;

IF zip_obj.zip IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj.zip is null');
END IF;

-- Initialize zip_obj_instance
zip_obj := zipcode_obj_type(null, null, null, null, null, null, null);

DBMS_OUTPUT.PUT_LINE ('Object instance has been initialized');

IF zip_obj IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj instance is null');
ELSE
DBMS_OUTPUT.PUT_LINE ('zip_obj instance is not null');
END IF;

IF zip_obj.zip IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj.zip is null');
END IF;
END;

When run, this script produces the following output:

Object instance has not been initialized
zip_obj instance is null
zip_obj.zip is null
Object instance has been initialized
zip_obj instance is not null
zip_obj.zip is null

As you can see, both the object instance and its attributes are null prior to the initialization. Once the object instance has been initialized with the help of its default constructor, it is no longer null, even though its individual attributes remain null.


Watch Out!

Referencing individual attributes of an uninitialized object instance causes an ORA-06530 exception, “Reference to uninitialized composite error”:

DECLARE
zip_obj zipcode_obj_type;
BEGIN
zip_obj.zip := '12345';
END;

ORA-06530: Reference to uninitialized composite
ORA-06512: at line 4

It is a good practice to always initialize any newly created object type instance.


Using Object Types with Collections

As mentioned previously, object types and collection types may be nested inside one another. Consider the following example, which includes a collection of ZIP code objects.

For Example ch23_4a.sql

DECLARE
TYPE zip_type IS TABLE OF zipcode_obj_type INDEX BY PLS_INTEGER;
zip_tab zip_type;
BEGIN
SELECT zipcode_obj_type(zip, city, state, null, null, null, null)
BULK COLLECT INTO zip_tab
FROM zipcode
WHERE rownum <= 5;

IF zip_tab.COUNT > 0
THEN
FOR i in 1..zip_tab.count
LOOP
DBMS_OUTPUT.PUT_LINE ('Zip: '||zip_tab(i).zip);
DBMS_OUTPUT.PUT_LINE ('City: '||zip_tab(i).city);
DBMS_OUTPUT.PUT_LINE ('State: '||zip_tab(i).state);
DBMS_OUTPUT.PUT_LINE ('-----------------------');
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE ('Collection of objects is empty');
END IF;
END;

This example declares an associative array type zip_type of object type zipcode_obj_type. Next, it declares a collection variable zip_tab based on the newly created associative array type. It then populates this collection of objects via a BULK SELECT statement. Finally, it checks whether the collection has been populated via the IF statement and displays its data on the screen.

Note how individual object type attributes are referenced by the DBMS_OUTPUT.PUT_LINE statement. Each attribute is prefixed by the collection name and row subscript without any reference to the object type itself.

When run, this example produces the following output:

Zip: 00914
City: Santurce
State: PR
-----------------------
Zip: 01247
City: North Adams
State: MA
-----------------------
Zip: 02124
City: Dorchester
State: MA
-----------------------
Zip: 02155
City: Tufts Univ. Bedford
State: MA
-----------------------
Zip: 02189
City: Weymouth
State: MA
-----------------------

In this example, you saw how to populate an associative array of objects with data. PL/SQL also supports selecting the data from collection of objects. In such a case, the collection type should be a nested table or varray type that is created and stored in the database schema just like its corresponding object type. This usage is illustrated by the following example.

For Example ch23_5a.sql

CREATE OR REPLACE TYPE zip_tab_type AS TABLE OF zipcode_obj_type;
/
DECLARE
zip_tab zip_tab_type := zip_tab_type();
v_zip VARCHAR2(5);
v_city VARCHAR2(20);
v_state VARCHAR2(2);
BEGIN
SELECT zipcode_obj_type(zip, city, state, null, null, null, null)
BULK COLLECT INTO zip_tab
FROM zipcode
WHERE rownum <= 5;

SELECT zip, city, state
INTO v_zip, v_city, v_state
FROM TABLE(zip_tab)
WHERE rownum < 2;

DBMS_OUTPUT.PUT_LINE ('Zip: '||v_zip);
DBMS_OUTPUT.PUT_LINE ('City: '||v_city);
DBMS_OUTPUT.PUT_LINE ('State: '||v_state);
END;

First, this script creates a nested table type, zip_tab_type, in the STUDENT schema. This table type is then used by the PL/SQL block. Creating and storing a nested table type in the STUDENT schema enables you to use the TABLE function in the SELECT INTO statement to select data from the collection of objects into the v_zip, v_city, and v_state variables. Recall that the TABLE function enables you to query a collection as if it were a physical database table.

When run, this example produces the following output:

Zip: 00914
City: Santurce
State: PR

So far, you have seen various examples of collections of objects. PL/SQL also supports nesting a collection type within an object type. Similarly to the previous examples, both collection and object data types should be created and stored in the database schema. Consider an example of a state object type that has two collection attributes, cities, and ZIP codes.

For Example ch23_6a.sql

CREATE OR REPLACE TYPE city_tab_type AS TABLE OF VARCHAR2(25);
/
CREATE OR REPLACE TYPE zip_tab_type AS TABLE OF VARCHAR2(5);
/
CREATE OR REPLACE TYPE state_obj_type AS OBJECT
(state VARCHAR2(2)
,city city_tab_type
,zip zip_tab_type);
/

This script creates two nested table types, city_tab_type and zip_tab_type, in the STUDENT schema. Next, it creates an object type, state_obj_type, that has three attributes. Note that the city and zip attributes are based on the nested table types created earlier.

Next consider an example that employs the newly created collection and object types.

For Example ch23_7a.sql

DECLARE
city_tab city_tab_type;
zip_tab zip_tab_type;

state_obj state_obj_type := state_obj_type(null, city_tab_type(), zip_tab_type());

BEGIN
SELECT city, zip
BULK COLLECT INTO city_tab, zip_tab
FROM zipcode
WHERE state = 'NY'
AND rownum <= 5;

state_obj := state_obj_type ('NY', city_tab, zip_tab);

DBMS_OUTPUT.PUT_LINE ('State: '||state_obj.state);
DBMS_OUTPUT.PUT_LINE ('------------------------');

IF state_obj.city.COUNT > 0
THEN
FOR i in state_obj.city.FIRST..state_obj.city.LAST
LOOP
DBMS_OUTPUT.PUT_LINE ('City: '||state_obj.city(i));
DBMS_OUTPUT.PUT_LINE ('Zip: '||state_obj.zip(i));
END LOOP;
END IF;
END;

In the declaration portion of the script, there are definitions of the two nested table variables, city_tab and zip_tab, based on the city_tab_type and the zip_tab_type, respectively. Also, there is a declaration and initialization of the object instance state_obj based on thestate_obj_type. Because the city and zip attributes of state_obj are nested tables, they are initialized via their default constructor methods, as highlighted here:

state_obj state_obj_type := state_obj_type(null, city_tab_type(), zip_tab_type());

In the executable portion of the example, the two nested tables, city_tab and zip_tab, are populated via a SELECT statement with a BULK COLLECT INTO clause. Recall that when nested tables are populated in such manner, there is no need to initialize them by invoking their default constructor methods. Next, the state_obj instance is populated by invoking its default constructor method:

state_obj := state_obj_type ('NY', city_tab, zip_tab);

In this case, there is no need to employ default constructor methods for the two nested table attributes, city and zip. Instead, the city_tab and zip_tab nested tables are simply passed into the constructor method state_obj_type.

When run, this script produces the following output:

State: NY
------------------------
City: Irvington
Zip: 07111
City: Franklin Lakes
Zip: 07417
City: Alpine
Zip: 07620
City: Oradell
Zip: 07649
City: New York
Zip: 10004

Lab 23.2: Object Type Methods


After this lab, you will be able to

Image Use Constructor Methods

Image Use Member Methods

Image Use Static Methods

Image Compare Objects Via Map and Order Methods


In Lab 23.1, you learned that object type methods are functions and procedures that specify actions that may be performed on the object type attributes and are defined in the object type specification. Also, you saw how to use the default system-defined constructor methods. A constructor is only one of the method types supported by PL/SQL. Some other method types are member, static, map, and order. The method type is typically determined by the actions that a particular method performs. For example, constructor methods are used to initialize object instances, whereas map and order methods are used for comparing and sorting object instances, respectively.

Oftentimes object type methods use a built-in parameter called SELF. This parameter represents a particular instance of the object type. As such, it is available to the methods that are invoked on that object type instance. You will see various examples of the SELF parameter in the discussions that follow.

Constructor Methods

A constructor method is a default method that is implicitly created by the system whenever a new object type is created. This function has the same name as its object type. Its input parameters have the same names and data types as the object type attributes and are listed in the same order as the object type attributes. A constructor method returns a new instance of the object type. In other words, it initializes the new object instance and assigns values to the object attributes.

Listing 23.2 illustrates calls to the default constructor method for the zipcode_obj_type created in Lab 23.1.

Listing 23.2 Default Constructor Method for Zipcode_Obj_Type

zip_obj1 := ZIPCODE_OBJ_TYPE('00914', 'Santurce', 'PR', USER, SYSDATE, USER, SYSDATE);

or

zip_obj2 := ZIPCODE_OBJ_TYPE(NULL, NULL, NULL, NULL, NULL, NULL NULL);

The first call to the constructor method returns a new instance, zip_obj1, of the zipcode_obj_type with attributes initialized to non-null values. The second call creates a new instance, zip_obj2, with NULL attribute values. Note that both calls produce non-null instances of thezipcode_obj_type. The difference between them lies in the values assigned to the individual attributes.

In Listing 23.2, both calls to the default constructor method use positional notation. Recall that positional notation associates values with corresponding parameters based on their positions in the header of the function, procedure, or (in this case) constructor. Next, consider the call to the default constructor method that uses named notation. In this case, the order of parameters does not correspond to the order of attributes in the zipcode_obj_type, as they are referenced by their names as shown in Listing 23.3.

Listing 23.3 Using Positional Notation with the Default Constructor Method for Zipcode_Obj_Type

zip_obj3 := ZIPCODE_OBJ_TYPE(created_by => USER
,created_date => SYSDATE
,modified_by => USER
,modified_date => SYSDATE
,zip =>'00914'
,city => 'Santurce'
,state => 'PR');

As noted earlier, PL/SQL also provides you with the ability to create your own (user-defined) constructors. User-defined constructors offer flexibility that the default constructors lack. For example, you might want to define a constructor on the zipcode_obj_type that initializes only some of the attributes of the newly created object instance. In this case, any attributes for which you do not specify values will be initialized to NULL by the system. In addition, you can control the number and type of parameters that your constructor may require.

Consider the following example of the user-defined constructors for the zipcode_obj_type.

For Example ch23_8a.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
(zip VARCHAR2(5)
,city VARCHAR2(25)
,state VARCHAR2(2)
,created_by VARCHAR2(30)
,created_date DATE
,modified_by VARCHAR2(30)
,modified_date DATE

,CONSTRUCTOR FUNCTION zipcode_obj_type (SELF IN OUT NOCOPY zipcode_obj_type
,zip VARCHAR2)
RETURN SELF AS RESULT

,CONSTRUCTOR FUNCTION zipcode_obj_type (SELF IN OUT NOCOPY zipcode_obj_type
,zip VARCHAR2
,city VARCHAR2
,state VARCHAR2)
RETURN SELF AS RESULT);
/

CREATE OR REPLACE TYPE BODY zipcode_obj_type AS

CONSTRUCTOR FUNCTION zipcode_obj_type (SELF IN OUT NOCOPY zipcode_obj_type
,zip VARCHAR2)
RETURN SELF AS RESULT
IS
BEGIN
SELF.zip := zip;
SELECT city, state
INTO SELF.city, SELF.state
FROM zipcode
WHERE zip = SELF.zip;

RETURN;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END;

CONSTRUCTOR FUNCTION zipcode_obj_type (SELF IN OUT NOCOPY zipcode_obj_type
,zip VARCHAR2
,city VARCHAR2
,state VARCHAR2)
RETURN SELF AS RESULT
IS
BEGIN
SELF.zip := zip;
SELF.city := city;
SELF.state := state;

RETURN;
END;
END;
/

This script expands the definition of the zipcode_obj_type by providing two versions of the default constructor method. In programming terms, such approach is called overloading. Essentially, overloading allows two methods or subprograms to use the same name as long as their parameters differ in terms of either their data types or their number. In the preceding example, the first constructor method expects two parameters, and the second constructor method expects four parameters.

Both constructors use the default parameter SELF as an IN OUT parameter and as a return data type in the RETURN clause. As stated previously, SELF references a particular object type instance. Note the use of the NOCOPY compiler hint. This hint is typically used with OUT and IN OUT parameters. By default, OUT and IN OUT parameters are passed by value. As a consequence, the values of these parameters are copied prior to the execution of the subprogram or method. Then, during the execution, temporary variables are used to hold values of the OUT parameters. For parameters that represent complex data types such as collections, records, and object type instances, this copying step can add significant processing overhead. By adding the NOCOPY hint, you instruct PL/SQL compiler to pass OUT and IN OUT parameters by reference and eliminate the copying step altogether.

In the type body, both constructor methods populate the city, state, and zip attributes. The first constructor method accomplishes this work via a SELECT INTO statement, and the second constructor method assigns incoming values to the object attributes. Notice how the attributes are referenced via the SELF parameter in the constructor methods.

Member Methods

Member methods provide access to the object instance data. As such, a member method should be defined for each action that an object type must perform. For example, you may need to return city, state, and ZIP code values associated with an object instance to the calling application, as shown in the next example. Note that this example shows only the newly added member method.

For Example ch23_8b.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
...

,MEMBER PROCEDURE get_zipcode_info (out_zip OUT VARCHAR2
,out_city OUT VARCHAR2
,out_state OUT VARCHAR2));
/

CREATE OR REPLACE TYPE BODY zipcode_obj_type AS

...

MEMBER PROCEDURE get_zipcode_info (out_zip OUT VARCHAR2
,out_city OUT VARCHAR2
,out_state OUT VARCHAR2)
IS
BEGIN
out_zip := SELF.zip;
out_city := SELF.city;
out_state := SELF.state;
END;
END;
/

This version of the object type definition contains a new member procedure that returns the ZIP code, city, and state values associated with a particular instance of the zipcode_obj_type object type. The reference to the SELF parameter in this procedure is optional, however, so the assignment statements can be modified as follows:

out_zip := zip;
out_city := city;
out_state := state;

These statements initialize OUT parameters associated with individual attributes of a particular object instance, just like the statements that include the reference to the SELF parameter.

Static Methods

Static methods are created for actions that do not need to access data associated with a particular object instance. As such, these methods are created for the object type itself and describe actions that are global to that object type. Because static methods do not have access to the data associated with a particular object type instance, they may not reference the default parameter SELF.

Consider the following example of a static method that displays ZIP code information. Note that this example shows only the newly added static method.

For Example ch23_8c.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
...

,STATIC PROCEDURE display_zipcode_info (in_zip_obj IN zipcode_obj_type));
/

CREATE OR REPLACE TYPE BODY zipcode_obj_type AS

...

STATIC PROCEDURE display_zipcode_info (in_zip_obj IN zipcode_obj_type)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE ('Zip: ' ||in_zip_obj.zip);
DBMS_OUTPUT.PUT_LINE ('City: ' ||in_zip_obj.city);
DBMS_OUTPUT.PUT_LINE ('State: '||in_zip_obj.state);
END;
END;
/

In this version of the script, the static method display_zipcode_info displays the values of the individual attributes for the ZIP code object on the screen. Even though this method references data associated with some object instance, the object instance is created elsewhere (i.e., another PL/SQL script, function, or procedure) and then passed into this method as an input parameter.

Comparing Objects

In PL/SQL, element data types such VARCHAR2, NUMBER, or DATE have a predefined order that enables them to be compared to each other or sorted. For example, the comparison operator (>) determines which variable contains a greater value and the IF-THEN-ELSE statement evaluates toTRUE, FALSE, or NULL accordingly:

IF v_num1 > v_num2 THEN
-- Do something
ELSE
-- Do something else
END IF;

In contrast, an object type may contain multiple attributes of different data types and, as a result, does not have a predefined order. Thus, to be able to compare and sort object instances of the same object type, you must specify how these object instances should be compared and ordered. This can be accomplished via two types of optional member methods, map and order.

Map Methods

Map methods compare and order object instances essentially by mapping an object instance to an element (scalar) data type such as DATE, NUMBER, or VARCHAR2. This mapping is then used to position object instance on the axis (DATE, NUMBER, or VARCHAR2) used for the comparison.

A map method is a member function that does not accept any parameters and returns an element data type, as demonstrated in the next example. Note that this example shows only the newly added map method.

For Example ch23_8d.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
...

,MAP MEMBER FUNCTION zipcode RETURN VARCHAR2);
/

CREATE OR REPLACE TYPE BODY zipcode_obj_type AS

...

MAP MEMBER FUNCTION zipcode RETURN VARCHAR2
IS
BEGIN
RETURN (zip);
END;
END;
/

In this version of the script, the map member function returns the value of the zip attribute that has been defined as VARCHAR2.

Once a map method is added to the object type, object type instances may be compared or ordered similarly to the element data types. For example, if zip_obj1 and zip_obj2 are two instances of the zipcode_obj_type, they can be compared as follows:

zip_obj1 > zip_obj2

or

zip_obj1.zipcode() > zip_obj2.zipcode()

The second statement uses dot notation to reference the map function.

The next example demonstrates how the various object type methods created so far may be used.

For Example ch23_9a.sql

DECLARE
zip_obj1 zipcode_obj_type;
zip_obj2 zipcode_obj_type;
BEGIN
-- Initialize object instances with user-defined constructor methods
zip_obj1 := zipcode_obj_type (zip => '12345'
,city => 'Some City'
,state => 'AB');

zip_obj2 := zipcode_obj_type (zip => '48104');

-- Compare object instances via map methods
IF zip_obj1 > zip_obj2
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj1 is greater than zip_obj2');
ELSE
DBMS_OUTPUT.PUT_LINE
('zip_obj1 is not greater than zip_obj2');
END IF;
END;

When the user-defined constructors are invoked, there is no reference to the SELF default parameter in the call statements.

When run, this script produces the following output:

v_zip_obj1 is not greater than v_zip_obj2

Order Methods

Order methods use a different technique for comparing and ordering object instances. They do not map object instances to an external axis such as NUMBER or DATE. Instead, an order method compares the current object instance with another object instance of the same object type based on some criteria specified in the method.

An order method is a member function with a single IN parameter of the same object type that returns INTEGER as its return type. Furthermore, the method must return a negative number, zero, or a positive number, which indicates that the object instance referenced by the SELFparameter is less than, equal to, or greater than the object instance referenced by the IN parameter, respectively.


Watch Out!

The following restrictions apply to the map and order methods:

Image An object type may contain either an order method or a map method. If it has both, the following error is raised at the time of its creation:

PLS-00154: An object type may have only 1 MAP or 1 ORDER method.

Image An object type derived from another object type may not define an order method.


Consider the following example of an order method for the zipcode_obj_type. Similarly to the previous examples, this version shows only the newly added order method.

For Example ch23_8e.sql

CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
...

,ORDER MEMBER FUNCTION zipcode (zip_obj zipcode_obj_type) RETURN INTEGER);
/

CREATE OR REPLACE TYPE BODY zipcode_obj_type AS

...

ORDER MEMBER FUNCTION zipcode (zip_obj zipcode_obj_type) RETURN INTEGER
IS
BEGIN
IF zip < zip_obj.zip THEN RETURN -1;
ELSIF zip = zip_obj.zip THEN RETURN 0;
ELSIF zip > zip_obj.zip THEN RETURN 1;
END IF;
END;
END;
/

In this version of the script, the map member function is replaced by the order member function. Much like the map method, the order method uses the zip attribute as the basis of comparison for the two object type instances.

The following example demonstrates how an order method may be used.

For Example ch23_10a.sql

DECLARE
zip_obj1 zipcode_obj_type;
zip_obj2 zipcode_obj_type;

v_result INTEGER;
BEGIN
-- Initialize object instances with user-defined constructor methods
zip_obj1 := zipcode_obj_type ('12345', 'Some City', 'AB');
zip_obj2 := zipcode_obj_type ('48104');

-- Compare objects instances via ORDER method
v_result := zip_obj1.zipcode(zip_obj2);
DBMS_OUTPUT.PUT_LINE ('The result of comparison is '||v_result);

IF v_result = 1
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj1 is greater than zip_obj2');

ELSIF v_result = 0
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj1 is equal to zip_obj2');

ELSIF v_result = -1
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj1 is less than zip_obj2');
END IF;
END;

In this script, the result of the order method is assigned to the v_result variable, which is defined as an INTEGER. Note how the order method is invoked:

v_result := zip_obj1.zipcode(zip_obj2);

The order method associated with the instance zip_obj1 accepts the instance zip_obj2 as its input parameter.

When run, this script produces the following output:

The result of comparison is -1
zip_obj1 is less than zip_obj2

It is important to recognize which object instance is being used to invoke the order method, as different instances may yield different results. For example, consider what happens if we transpose the object instances when calling the order method (the affected statements are shown in bold):

For Example ch23_10b.sql

DECLARE
zip_obj1 zipcode_obj_type;
zip_obj2 zipcode_obj_type;

v_result INTEGER;
BEGIN
-- Initialize object instances with user-defined constructor methods
zip_obj1 := zipcode_obj_type ('12345', 'Some City', 'AB');
zip_obj2 := zipcode_obj_type ('48104');

-- Compare objects instances via ORDER method
v_result := zip_obj2.zipcode(zip_obj1);
DBMS_OUTPUT.PUT_LINE ('The result of comparison is '||v_result);
IF v_result = 1
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj2 is greater than zip_obj1');

ELSIF v_result = 0
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj2 is equal to zip_obj1');

ELSIF v_result = -1
THEN
DBMS_OUTPUT.PUT_LINE ('zip_obj2 is less than zip_obj1');
END IF;
END;

The text displayed by the DBMS_OUTPUT.PUT_LINE statements has been altered in this script to produce the correct output. While this is a very simple example, it is able to demonstrate that changing how the order method is invoked (via a change in the object instance) affects how its return value is evaluated.

This version of the script produces different output as shown:

The result of comparison is 1
zip_obj2 is greater than zip_obj1

Summary

In this chapter, you learned how to define and use objects in Oracle. Overall, object types in Oracle are similar to classes created in Java. They consist of attributes and methods, where attributes represent different data elements of an object and methods are used to perform various actions on these data elements. You also learned how to implement and use different types of methods to initialize, compare and sort objects. In addition, you discovered how to use objects with collections.


By the Way

The companion website provides additional exercises and suggested answers for this chapter, with discussion related to how those answers resulted. The main purpose of these exercises is to help you test the depth of your understanding by utilizing all of the skills that you have acquired throughout this chapter.