Tải bản đầy đủ (.pdf) (11 trang)

An Object-Relational SQL Example

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (30.03 KB, 11 trang )

The CallableStatement object used in this chapter's examples is an interface. The full
interface name is java.sql.CallableStatement. This interface is implemented by
oracle.jdbc.driver.OracleCallableStatement, which extends
oracle.jdbc.driver.OraclePreparedStatement. This means that all the proprietary
methods that are available in OracleStatement and OraclePreparedStatement are also
available in Oracle-CallableStatement. The following is a list of the proprietary methods
available in OracleCallableStatement, all of which can throw a SQLException:
clearParameters( )
ARRAY getARRAY(int parameterIndex)
InputStream getAsciiStream(int parameterIndex)
BFILE getBFILE(int parameterIndex)
InputStream getBinaryStream(int parameterIndex)
BLOB getBLOB(int parameterIndex)
CHAR getCHAR(int parameterIndex)
CLOB getCLOB(int parameterIndex)
ResultSet getCursor(int parameterIndex)
Object getCustomDatum(int parameterIndex, CustomDatumFactory factory)
DATE getDATE(int parameterIndex)
NUMBER getNUMBER(int parameterIndex)
Datum getOracleObject(int parameterIndex)
RAW getRAW(int parameterIndex)
REF getREF(int parameterIndex)
ROWID getROWID(int parameterIndex)
STRUCT getSTRUCT(int parameterIndex)
InputStream getUnicodeStream(int parameterIndex)
registerOutParameter(
int paramIndex, int sqlType, int scale, int maxLength)
Part IV: Object-Relational SQL
We've now covered everything there is to know about using JDBC with relational
SQL. Our second and third options for how to use the database involve object-
relational SQL. Object-relational SQL is the application of SQL to Oracle


database objects and forms the basis of the object portion of Oracle's object-
relational features. In Part IV we'll cover the use of the Statement, ResultSet,
PreparedStatement, and CallableStatement Java objects with Oracle
database objects. So let's start our journey into object-relational SQL with an
overview of Oracle's object-relational technology.
Chapter 14. An Object-Relational SQL Example
Oracle documentation refers to Oracle8i's ability to store user-defined data types in the database
as object-relational SQL. I think this is quite appropriate. With Oracle you have the choice of
creating three different kinds of tables:
• A traditional relational table using native SQL data types such as VARCHAR2, DATE,
and NUMBER.
• A relational table with object columns. This type of table is a hybrid, using both native
SQL data types and user-defined data types.
• An object table, which is defined solely based on a user-defined data type.
The best part of this architecture is that it's flexible enough to facilitate both relational SQL and
object-oriented development tools by providing both a relational view and an object view of the
same database. You can create object views against relational tables to create an object face to
a relational database or create object tables and access the column attributes as though they are
part of relational tables by using the TABLE operator. You can have your cake and eat it too!
In this chapter, we'll discuss the use of JDBC with database objects. We'll start by examining
object analysis and design but not from the traditional point of view. Instead, we'll look at how we
can transform our relational model from that shown in Chapter 8 into an object model. We'll then
look at how we can transform our relational database into an object database by implementing
object views on our relational data. We'll finish up by creating object tables to replace our
relational tables, and we'll use those object tables for examples in the chapters that follow.
14.1 From Relational Tables to Object Views
In Chapter 8, we discussed creating a demographic database for HR data. By the end of the
chapter we had gone through several evolutions with our analysis and had presented 11 entities.
We then created the DDL for five of the entities:
PERSON

PERSON_IDENTIFIER
PERSON_IDENTIFIER_TYPE
PERSON_LOCATION
LOCATION
We can now take these five entities and create object views to present our relational database as
an object-relational database. Seeing that we are now looking at the data from an object
perspective, we have two ways in which to implement an object solution. We can create object
views on top of our relational tables or transform our entities into object tables.
14.1.1 Transforming Entities into Objects
Recalling all the entities we identified in Chapter 8, we can first create object tables for
EMPLOYMENT STATUS, LOCATION, ORGANIZATION, and POSITION. Then we can create
another object table for PERSON, folding into it the following intersection entities as nested tables
or varying arrays:
PERSON_EMPLOYMENT_STATUS
PERSON_LOCATION
PERSON_ORGANIZATION
PERSON_POSITION
PERSON_IDENTIFIER
Nested tables and varying arrays are both referred to as collections. Adding the five collections
listed here to a person object table would create a rather large person object that would need
to retrieve all related person data at one time. This may be desirable for you, the programmer, but
it will lead to poor application performance. It is much more advisable to fold only the
PERSON_IDENTIFIER entity into the person entity as a collection, creating a new PERSON
object table, because it is common to query for persons by their identifiers. This leaves the
intersection entities as separate object tables with the end result being that we are left with four
objects: PERSON, PERSON_IDENTIFIER_TYPE, PERSON_LOCATION, and LOCATION.
Our next decision is whether to use references, or primary and foreign keys to enforce referential
integrity. Since there are some negative performance implications associated with using
references in object views, we'll use primary and foreign keys. With these decisions behind us,
let's move forward by creating object views for our four new objects.

14.1.2 Creating Object Views
To create an object view, follow these steps:
1. Define an object type in which its attributes correspond to the column types of the
associated relational table.
2. Identify a unique value from the underlying relational table to act as a reference value for
the rows.
3. Create an object view to extract the data.
4. Create INSTEAD OF triggers to make the view updateable.
We'll do this for the PERSON and PERSON_IDENTIFIER tables. As a reminder, the following is
the DDL for the PERSON and PERSON_IDENTIFIER tables introduced in Chapter 8:
create table PERSON (
person_id number not null,
last_name varchar2(30) not null,
first_name varchar2(30) not null,
middle_name varchar2(30),
birth_date date not null,
mothers_maiden_name varchar2(30) not null )

create table PERSON_IDENTIFIER (
person_id number not null,
id varchar2(30) not null,
id_type varchar2(30) not null )
14.1.2.1 Creating user-defined data types
Step 1 is to create a user-defined data type to represent the data from these two tables as an
object. We'll start with the PERSON_IDENTIFIER table. Here's the DDL to create a
corresponding user-defined data type:
create type PERSON_IDENTIFIER_typ as object (
id varchar2(30),
id_type varchar2(30) )
Notice that we don't have the person_id in the type definition. That's because the person_id

will be implicit, because the identifiers are stored in the form of a nested table within the enclosing
person object. Next, we need to create a nested table type definition to transform the
PERSON_IDENTIFIER table into a collection for the person object. Here's the DDL to do that:
create type PERSON_IDENTIFIER_tab as
table of PERSON_IDENTIFIER_typ
Now that we have a collection type for the PERSON_IDENTIFIER table, we can define the
person type:
create type PERSON_typ as object (
person_id number,
last_name varchar2(30),
first_name varchar2(30),
middle_name varchar2(30),
birth_date date,
mothers_maiden_name varchar2(30),
identifiers person_identifier_tab,
map member function get_map return varchar2,
member function get_age return number,
member function get_age_on( aid_date in date ) return number,
static function get_id return number );
/

create type body PERSON_typ as
map member function get_map return varchar2 is
begin
return rpad( last_name, 30 )||
rpad( first_name, 30 )||
rpad( middle_name, 30 )||
rpad( mothers_maiden_name,30 )||
to_char( birth_date, 'YYYYMMDDHH24MISS' );
end get_map;


member function get_age return number is
begin
return trunc( months_between( SYSDATE, birth_date ) / 12 );
end get_age;

member function get_age_on( aid_date in date ) return number is
begin
return trunc( months_between( aid_date, birth_date ) / 12 );
end get_age_on;

static function get_id return number is
n_person_id number := 0;
begin
select person_seq.nextval into n_person_id from dual;
return n_person_id;
end get_id;

end;
/
We've added one static and three member methods to person_typ. I'll use these in the coming
chapters to demonstrate how to call a database object's methods.
14.1.2.2 Selecting a reference value
Now that we have defined types for the PERSON and PERSON_IDENTIFIER tables, we need to
decide which value to use for an object reference. An object reference acts as a unique identifier
for an object, just as a primary key acts as a unique identifier for a row in a relational table. Since
we're creating object views, and the column person_id is common to both tables, we'll use it for
the reference value.
If we were creating an object table, as we will do later in this chapter, we could choose between
using a unique value in the attribute of a user-defined data type or a reference. A reference is a

database-generated global unique identifier (GUID). My preference, even with object tables, is to
use an attribute as a primary key instead of a GUID, because you can create foreign key
constraints between object tables with a primary key.
14.1.2.3 Creating an object view
Now that we have all the necessary types defined and have selected a reference value, we can
move on to step 3, which is to create a view to extract the data from our two relational tables and
cast it to a person type object. Here's the object view:
create or replace view person_ov of
person_typ with object identifier( person_id ) as
select person_id,
last_name,
first_name,
middle_name,
birth_date,
mothers_maiden_name,
cast(
multiset (
select i.id,
i.id_type
from person_identifier i
where i.person_id = p.person_id ) as
person_identifier_tab ) as
identifiers
from person p
In this object view, we select data from the PERSON table and use the CAST and MULTISET
keywords to transform the related values in the PERSON_IDENTIFER table into a
person_identifier_tab object. The MULTISET keyword is used because the result of the
subquery has multiple rows. The CAST keyword takes the values and creates a
person_identifier_typ object for each row, which in turn becomes elements of the
person_identifier_tab object. The result of a query against the person_ov object view is a

person_typ object for each PERSON row in the database. Each person_typ object includes
any related person_identifiers.
14.1.2.4 Creating INSTEAD OF triggers
At this point, we can retrieve data from the PERSON and PERSON_IDENTIFIER tables in the
form of a table of person_ov objects. However, if we need to insert, update, or delete objects,
we need to create INSTEAD OF triggers on person_ov. INSTEAD OF triggers encapsulate
insert, update, and delete logic for a view in the form of PL/SQL or Java code. Example 14-1
shows the three INSTEAD OF triggers required for the PERSON table, and Example 14-2
shows the three required triggers for the nested PERSON_IDENTIFIER table. All six of these
INSTEAD OF triggers are required to make the person_ov object view updateable.
The first three triggers, shown in Example 14-1, are PERSON_OV_IOI, PERSON_OV_IOU,
and PERSON_OV_IOD. These triggers intercept inserts, updates, and deletes against the
person_ov object view and propagate them to the PERSON table instead.
Example 14-1. person_ov INSTEAD OF triggers for the PERSON table
create or replace trigger person_ov_ioi
instead of insert on person_ov
for each row
declare
t_identifiers person_identifier_tab;
begin

insert into person (
PERSON_ID,
LAST_NAME,
FIRST_NAME,
MIDDLE_NAME,
BIRTH_DATE,
MOTHERS_MAIDEN_NAME )
values (
:new.PERSON_ID,

:new.LAST_NAME,
:new.FIRST_NAME,
:new.MIDDLE_NAME,

×