 |
|
Object SQL,
Pointers and Encapsulation
Oracle Database Tips by Donald Burleson
|
One of the basic constructs of
object-oriented programming is encapsulation. Encapsulation is defined
as the ability to access objects only via their behaviors. This is
contradictory to a basic principle the relational database model, data
independence, which says that any data may be accessed in an ad-hoc,
independent fashion.
At first glance, it seems that these two
concepts cannot be reconciled, because it would be impossible to have data
tables which are independent of the application, while at the same time
supporting encapsulation, which tightly-couples the objects and their
behaviors. However, these concepts are not contradictory. Because the
behaviors are stored in the database, they are not external, and will not
jeopardize the independence of applications from data.
For example, one could only access the
CREDIT_RATING field in the CUSTOMER table by invoking the PLACE_ORDER behavior.
The SQL language, of course, would allow access and update to any data items
which are allowed within the system security tables. Any authorized user could
view the credit rating of a customer without ever invoking an object-oriented
method.
Another conceptual limitation of SQL which has
legitimate ramifications for object-oriented databases is the inability of SQL
to associate a behavior with a data item. The properties of an object and its
operational semantics must be coded within an external entity (the application
program), and SQL has no built-in method for incorporating behaviors into
tables. However, there is a solution to this problem, and many of the
object/relational database vendors have created "methods" for database objects
that have a one-for-one correspondence with the SQL operators. For example, a
database might automatically create a method called insert_customer, which would
invoke the appropriate SQL statement to insert a row into a customer table. A
database might automatically create methods to insert, update and delete rows
from the target table, much as C++ allows for constructors and destructors for
objects. While this works fine for a simple operation, there is still a problem
for more sophisticated methods that access and alter more than one data column.
Many of the built-in functions of SQL also
violate the encapsulation rule. For example, instead of writing a methods to
compute the gross pay for an employee, we could directly use SQL to perform this
operation, thereby bypassing the method:
SELECT
hours_worked*payrate
FROM
timesheet, payrates
WHERE
emp_id = 123
AND
week = '03/98';
The same is true when using the SUM, AVG and any
one of the dozens of other SQL functions that are offered by the major
relational database vendors.
SQL and Pointers
One of the greatest mismatches with SQL and
objects lies in the arena of pointers. The introduction of pointers into the
relational model has led to a situation where the declarative nature of SQL is
being radically changed.
For example, the use of the DEREF operator in
the new object/relational SQL, allows an SQL statement to de-reference a row
pointer, essentially navigating from one table to the next table. Rather than
relying on the SQL optimizer to take care of the database access, the developer
now has the option of embedding SQL statements into their programs that will
allow them to navigate through the database, visiting tables that have been
linked together with pointers. This is a very foreign idea for most SQL
developers but it is now a reality.
For example, the following SQL could be used to
navigate from a customer to the order rows for the customer:
SELECT
DEREF(order_list)
FROM
CUSTOMER
WHERE
customer_id = "JONES";
This would be the equivalent to the traditional
SQL:
SELECT
order_stuff
FROM
CUSTOMER, ORDER
WHERE
customer_id = "JONES"
AND
customer_ID = order_ID;
The SQL becomes even more confounding when we
start dealing with the more abstract uses of pointers in a relational/object
model. As we recall from earlier chapters, these pointer constructs use
object-Ids (OID) and include some very abstract data structures:
-
Pointers to individual rows in other tables.
-
Repeating groups of pointers to rows.
-
Pointers to arrays of pointers to rows.
-
Pointers to whole tables.
-
Multidimensional arrays of pointers.
Many of the relational vendors have extended
their SQL syntax to provide the following constructs to deal with pointers:
-
DEREF - This SQL operator accepts an OID and
returns the contents of the row that the OID points to:
SELECT
DEREF(order_oid)
FROM
CUSTOMER;
-
CAST & MULTISET - These SQL operators casts a
multiple input data stream into the appropriate data types for the SQL
operation:
INSERT INTO
COURSE (STUDENT_LIST)
(CAST
(MULTISET
(SELECT
student_name,
student_address,
grade
FROM
GRADE, STUDENT
WHERE
GRADE.course_name =
'CS101'
AND
GRADE.student_name = STUDENT.student_name
)
)
);
With all of these new pointer constructs and
extensions to SQL, it will be several years before the mainstream programming
community arrives at a general agreement about the use of pointer-based
navigation within SQL programming.
SQL and inheritance
As we recall from Chapter 7, the
object/relational database allow the developer to create a class hierarchy of
related data items, where each item in the class hierarchy is a sub-type of
another data type. These types of IS-A relationships, while valid from a data
modeling viewpoint, do not have a simple implementation in object/relational
databases. Since most object/relational databases do not support hierarchical
relationships, it is impossible to directly represent the fact that a database
entity has sub-entities.
Once an object has been instantiated, the data
items within the object have been defined and the object may be used within SQL
to retrieve the data items. The problem is that a separate table must exist for
all sub-classes within the class hierarchy, since each my have separate data
items. Therefore, if we have a class hierarchy with ten separate classes, there
we may have ten separate tables to hold the instantiated objects. This can make
the SQL more complicated, especially since we must know the type of the object
when we are making the query. In the following example, we are querying the
data items from a luxury_sedan vehicle, and we must know that it is of the type
"luxury_sedan" in order to formulate the query:
SELECT
vehicle_number,
registration_number,
number_of_doors,
type_of_leather_upholstery
FROM
LUXURY_SEDAN
WHERE
sedan.key = 8383635;
Perhaps future implementation of
object/relational databases may remove the SQL restriction of having to
explicitly specify the target table by name, but there is no way the any SQL
optimizer will be able to tell the sub-type of the target object without some
kind of a hint.
Summary
Now that we have addressed the basic issues
relating to SQL and objects, let's take a look at how many of the database
developers are interfacing objects with SQL by developing object-oriented
applications that use relational database to store their object information.