An Object-Relational SQL Example

11 313 2
An Object-Relational SQL Example

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

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, :new.BIRTH_DATE, :new.MOTHERS_MAIDEN_NAME ); if :new.identifiers is not null then t_identifiers := :new.identifiers; for i in t_identifiers.first t_identifiers.last loop insert into person_identifier ( PERSON_ID, ID, ID_TYPE ) values ( :new.PERSON_ID, t_identifiers( i ).ID, t_identifiers( i ).ID_TYPE ); end loop; end if; end; / create or replace trigger person_ov_iou instead of update on person_ov for each row declare t_identifiers person_identifier_tab; begin update person set PERSON_ID = :new.PERSON_ID, LAST_NAME = :new.LAST_NAME, FIRST_NAME = :new.FIRST_NAME, MIDDLE_NAME = :new.MIDDLE_NAME, BIRTH_DATE = :new.BIRTH_DATE, MOTHERS_MAIDEN_NAME = :new.MOTHERS_MAIDEN_NAME where PERSON_ID = :old.PERSON_ID; delete person_identifier where PERSON_ID = :old.PERSON_ID; if :new.identifiers is not null then t_identifiers := :new.identifiers; for i in t_identifiers.first t_identifiers.last loop insert into person_identifier ( PERSON_ID, ID, ID_TYPE ) values ( :new.PERSON_ID, t_identifiers( i ).ID, t_identifiers( i ).ID_TYPE ); end loop; end if; end; / create or replace trigger person_ov_iod instead of delete on person_ov for each row declare begin delete person_identifier where PERSON_ID = :old.PERSON_ID; delete person where PERSON_ID = :old.PERSON_ID; end; / The next three triggers, IDENTIFIERS_OF_PERSON_OV_IOI, IDENTIFIERS_OF_PERSON_OV_IOU, and IDENTIFIERS_OF_PERSON_OV_IOD (shown in Example 14-2) handle inserts, updates, and deletes against the PERSON_IDENTIFIER table, respectively, which is represented by the nested table identifiers in person_ov. These are used only when the identifiers attribute of the person_typ is the only attribute modified by a SQL statement. Example 14-2. person_ov INSTEAD OF triggers for the PERSON_IDENTIFIER table create or replace trigger identifiers_of_person_ov_ioi instead of insert on nested table identifiers of person_ov for each row declare begin insert into person_identifier ( PERSON_ID, ID, ID_TYPE ) values ( :parent.PERSON_ID, :new.ID, :new.ID_TYPE ); end; / create or replace trigger identifiers_of_person_ov_iou instead of update on nested table identifiers of person_ov for each row declare begin update person_identifier set ID = :new.ID, ID_TYPE = :new.ID_TYPE where PERSON_ID = :parent.PERSON_ID and ID = :old.ID and ID_TYPE = :old.ID_TYPE; end; / create or replace trigger identifiers_of_person_ov_iod instead of delete on nested table identifiers of person_ov for each row declare begin delete person_identifier where PERSON_ID = :parent.PERSON_ID and ID = :old.ID and ID_TYPE = :old.ID_TYPE; end; / With our six INSTEAD OF triggers in place, we have a fully updateable object view, named person_ov, for the PERSON and PERSON_IDENTIFIERS table. With the person_ov object view, we can update the underlying tables with relational SQL or treat them as an object. Using object views, you can migrate a legacy relational database to a relational-object database. While creating an object view does not take much effort, writing the INSTEAD OF triggers to make the view updateable takes a great deal of effort. The best solution for a new application is to create object tables directly based on our user-defined data types. Object tables can be modified using either relational SQL or object-relational SQL. 14.2 Object Tables Now that you've seen the object view solution, let's examine the use of object tables. In this section, we'll take the four example entities: person, location, person location, and person identifier type, and create object tables to represent those entities as objects. We'll create the user-defined database types, and the object tables to implement them, in order of the dependence. To begin, we create an object table corresponding to the person identifier type entity. First, we need to define a type: create type PERSON_IDENTIFIER_TYPE_typ as object ( code varchar2(30), description varchar2(80), inactive_date date ) / Now that we have the type definition, we create the person_identifier_type_ot object table using the following DDL: create table PERSON_IDENTIFIER_TYPE_ot of PERSON_IDENTIFIER_TYPE_typ tablespace USERS pctfree 20 storage (initial 100 K next 100 K pctincrease 0) / alter table PERSON_IDENTIFIER_TYPE_ot add constraint PERSON_IDENTIFIER_TYPE_ot_PK primary key ( code ) using index tablespace USERS pctfree 20 storage (initial 10 K next 10 K pctincrease 0) / Notice that we also created a primary key constraint on the table using the code attribute. Next, we define the location type: create type LOCATION_typ as object ( location_id number, parent_location_id number, code varchar2(30), name varchar2(80), start_date date, end_date date, map member function get_map return varchar2, static function get_id return number ); / create type body LOCATION_typ as map member function get_map return varchar2 is begin return rpad( code, 30 )|| rpad( name, 80 )|| to_char( start_date, 'YYYYMMDDHH24MISS' ); end get_map; static function get_id return number is n_location_id number := 0; begin select location_seq.nextval into n_location_id from dual; return n_location_id; end get_id; end; / And now that we have the location type, we create the location_ot object table using the following DDL: create table LOCATION_ot of LOCATION_typ tablespace USERS pctfree 20 storage (initial 100 K next 100 K pctincrease 0) / alter table LOCATION_ot add constraint LOCATION_ot_PK primary key ( location_id ) using index tablespace USERS pctfree 20 storage (initial 10 K next 10 K pctincrease 0) / create unique index LOCATION_ot_UK1 on LOCATION_ot ( code, name, start_date ) tablespace USERS pctfree 20 storage (initial 100 K next 100 K pctincrease 0) / drop sequence LOCATION_ID / create sequence LOCATION_ID start with 1 order / This time we created a sequence to provide unique values for the primary key attribute location_id. We also created a unique "external" key against the code, name, and start_date attributes. Now we need to create the person_ot object table, but to do so, we first need to define not only the person type, but also the person_identifer type and the person_identifier collection type. We can reuse the three types we defined for the person_ov object view, so here's the DDL for the person_ot object table: create table PERSON_ot of PERSON_typ nested table identifiers store as PERSON_IDENTIFIER_ot tablespace USERS pctfree 20 storage (initial 100 K next 100 K pctincrease 0) / alter table PERSON_ot add constraint PERSON_ot_PK primary key ( person_id ) using index tablespace USERS pctfree 20 storage (initial 10 K next 10 K pctincrease 0) / alter table PERSON_IDENTIFIER_ot add constraint PERSON_IDENTIFIER_ot_PK primary key ( id, id_type ) using index tablespace USERS pctfree 20 storage (initial 10 K next 10 K pctincrease 0) / create unique index PERSON_ot_UK1 on PERSON_ot ( last_name, first_name, birth_date, mothers_maiden_name ) tablespace USERS pctfree 20 storage (initial 100 K next 100 K pctincrease 0) / drop sequence PERSON_ID / create sequence PERSON_ID start with 1 order / Here, we not only created a nested table but also created a primary key constraint on it to prevent duplicate values in the collection. In addition, we created a unique index, person_ot_uk1, on the attributes of the person_ot to prevent duplicate entries based on real-world values. [...]... point, we've defined four object tables, which we'll use as a basis for our object-relational SQL manipulations using JDBC in the next two chapters Chapter 15 will cover the use of the weakly typed data types for manipulating object data Chapter 16 will cover the use of the strongly typed interfaces Table 14-1 Relational to object-relational table migration Relational table(s) Object table person person_ot... person_ot and location_ot, respectively Had we not defined primary keys on person_ot and location_ot, we could just as well have used object references to link occurrences of person_location_typ to person_ot and location_ot objects The primary key approach to uniquely identifying an object is actually more loosely coupled than the use of a global unique identifier (GUID) object reference As a result, the... that we have a location_ot object table and a person_ot object table, we can create the intersection entity person location First, we define a type: create type PERSON_LOCATION_typ as object ( person_id number, location_id number, start_date date, end_date date ) / For this type, we used the attributes person_id and location_id to act as foreign keys to person_ot and location_ot, respectively Had we not... activities such as reporting, because it allows you to continue to use the tables as though they are relational and doesn't force you to follow the object references to establish relationships In addition, the use of primary keys also allows the use of foreign keys, which prevents dangling references that can occur when using GUID object references Finally, here's the DDL for the person_location_ot object table: . 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. 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

Ngày đăng: 29/09/2013, 09:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan