Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
141,71 KB
Nội dung
Previous Page TOC Next Page Home ● 18 ❍ Object-Oriented Programming with Packages ■ The Package Specification ■ The Package Body ■ Package Variables and Initialization ■ Overloading ■ Retrieving Results ■ Exception Handling ■ Package Privileges ■ Accessing Oracle Packages from Client Applications ■ Object-Oriented Concepts ■ Summary 18 Object-Oriented Programming with Packages Although Oracle is a relational database, (as opposed to an object-oriented database), it provides a very powerful object- oriented feature in its implementation of packages. An Oracle package is a group of procedures, functions, variables, constants, cursors, and type declarations that function as a logical unit. Packages provide many of the characteristics typically associated with object-oriented languages, including encapsulation, information hiding, and function overloading. Packages can also provide improved performance because when a packaged object is referenced, the entire package is loaded into memory. This reduces or eliminates disk I/O for subsequent calls to objects in the package. As a result, these calls execute more quickly than similar calls to stand-alone functions and procedures, which must be read from disk as requested. There are two parts to a package: the package specification and the package body. The package specification provides the interface through which applications and other subprograms access packaged objects. The package body contains the actual code for objects in the specification, as well as any declarations and subprograms that are private to the package. If a package specification has only variables, constants, and type declarations, it need not have a body at all. This independence from the body of the package enables the specification to be compiled separately, even when a body is required. This can improve the development process by enabling developers to define the application interface before writing the underlying code. Objects referencing the package are dependent only on the specification. Therefore, the package body can also be compiled independently from the specification without affecting any external references, provided that there are no changes to the interface. The following sections demonstrate the creation of package specifications and bodies, highlighting key features. In addition to PL/SQL, an example is provided in C++ to illustrate the use of Oracle packages in object-oriented client applications. The Package Specification Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. The package specification must contain all objects that will be accessed by external subprograms or applications. It can be viewed as the public declarations section of the package. You can construct packages to perform all operations on an underlying database object or to perform operations on groups of similar objects. Any logical grouping of data and subprograms is an acceptable candidate for a package, as dictated by the application or applications that will be accessing the database. Listing 18.1 shows an example of a package specification that encapsulates methods for maintaining lookup tables in the database. Listing 18.1. This package specification contains functions used to maintain lookup tables. CREATE OR REPLACE PACKAGE lookup_admin AS FUNCTION add_address_type(description VARCHAR2) RETURN NUMBER; FUNCTION add_phone_type(description VARCHAR2) RETURN NUMBER; FUNCTION add_contact_type(description VARCHAR2) RETURN NUMBER; FUNCTION add_contact_method(description VARCHAR2) RETURN NUMBER; FUNCTION add_contact_reason(description VARCHAR2) RETURN NUMBER; /* add update and delete functions here */ END lookup_admin; In addition to functions and procedures, the package specification can contain variables, constants, and user-defined exceptions and data types. The code example in Listing 18.2 includes a user-defined data type based on an underlying table and provides functions to operate on the table. Listing 18.2. This package specification contains a user-defined data type. CREATE OR REPLACE PACKAGE manage_individuals AS TYPE indiv_rec IS RECORD( ID NUMBER(10) ,last_name VARCHAR2(30) ,first_name VARCHAR2(30) ,notes VARCHAR2(255) ,date_of_birth DATE ,last_updt_user VARCHAR2(20) ,last_updt_date DATE ); Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. FUNCTION insert_individual(indiv_in INDIV_REC) RETURN NUMBER; FUNCTION update_individual(indiv_in INDIV_REC) RETURN NUMBER; FUNCTION delete_individual(indiv_in INDIV_REC) RETURN NUMBER; END manage_individuals; Perhaps the most powerful feature of packaged functions and procedures is overloading. Overloading enables a single function or procedure to accept different sets of parameters. To overload a packaged subprogram in Oracle, simply declare it separately for each desired parameter list, as shown in Listing 18.3. Listing 18.3. This package specification demonstrates function overloading. CREATE OR REPLACE PACKAGE manage_individuals AS FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2) RETURN NUMBER; FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, notes_in VARCHAR2) RETURN NUMBER; FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, d_o_b DATE, notes_in VARCHAR2) RETURN NUMBER; FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, d_o_b DATE) RETURN NUMBER; /* add update and delete functions here */ END manage_individuals; Be careful to avoid ambiguous parameter lists. For example, if the d_o_b parameter in the fourth function declaration of Listing 18.3 was defined as type VARCHAR2, it would become indistinguishable from the second function declaration. In the context of Listing 18.3, that would result in values being inserted into the wrong columns. You should recompile the package specification as infrequently as possible. Other packaged and stand-alone subprograms that reference objects in the package specification will be invalidated when it is recompiled. As a result, objects referencing the package specification must also be recompiled every time the specification is recompiled. The Package Body The package body contains the code for all subprograms defined in the specification, as well as any private variables, constants, cursors, data types, or subprograms. Objects declared within the package body are accessible only by other objects within the body. This enables you to use the package body to hide information and encapsulate subprograms within the package. However, objects within the package body can reference objects in other package specifications, as Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. well as stand-alone objects. A package body cannot exist without a package declaration. If the body does not contain all subprograms and cursors declared in the specification, or if declarations in the body are in conflict with declarations in the specification, compilation errors result. However, you can compile the body separately from the specification, which is extremely useful when you are debugging packaged subprograms. Packaged subprograms that contain explicit commits and rollbacks cannot be accessed by triggers or other subprograms that apply transactions. You should keep this in mind when you are designing packages, along with the effects of any implicit commits and rollbacks that might occur. Transactions applied within a packaged subprogram are rolled back implicitly when an unhandled exception occurs. An implicit commit occurs for all uncommitted transactions when the current session is terminated. In general, packaged subprograms involving transactions should not participate in transactions with other subprograms and should not be referenced by triggers. It is usually preferable to explicitly commit or roll back transactions that occur within packaged subprograms. Package Variables and Initialization The first time a packaged object is referenced, the entire package is loaded into memory. It is important to note that each session gets its own instance of package variables. Packaged data cannot be shared across sessions, and all values stored for a particular session are lost when the session ends. Variables declared within the package body, but outside of subprograms, hold their values for the life of the session. As with stand-alone functions and procedures, variables declared within packaged subprograms persist only within the scope of the subprograms in which they are declared. Variables and cursors declared at the package level can be accessed by all subprograms within the package body. Any code in the body of the package itself is executed only once, when the package is first loaded. For this reason, package code is typically used only to initialize package variables. Listing 18.4, which is a portion of the package body for the specification in Listing 18.1, uses only one statement in the package body. Listing 18.4. This package body provides functions to insert records into lookup tables. CREATE OR REPLACE PACKAGE BODY lookup_admin AS user_id VARCHAR2(20); FUNCTION add_address_type(description VARCHAR2) RETURN NUMBER IS BEGIN INSERT INTO address_type VALUES(address_type_ids.nextval, description, user_id, sysdate); COMMIT; RETURN(0); EXCEPTION WHEN OTHERS THEN ROLLBACK; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. RETURN(1); END add_address_type; /* all functions in the specification must be defined in the body */ BEGIN SELECT user INTO user_id FROM dual; END lookup_admin; Packaged subprograms and data are accessed using owner.package_name.object_name notation. You can create public synonyms for packages, as with other objects, to eliminate the need for the owner prefix. Note that the SELECT statement in the package body is executed only once, which is a somewhat of an optimization when multiple transactions are applied using the functions in the package. For example, the SELECT statement stores the user_id upon package instantiation (first function call). All subsequent calls do not execute the SELECT statement. To this point, the code listings in this chapter have included functions in preference to procedures. Each of these functions returns a value that indicates the success or failure of the operation it performs. The same result can be achieved by using an output parameter in a procedure, as illustrated by the package specification in Listing 18.5, which simply redefines the functions declared in Listing 18.3 as procedures. Listing 18.5. This package specification demonstrates the use of an output parameter in an overloaded procedure. CREATE OR REPLACE PACKAGE manage_individuals AS PROCEDURE insert_individual(ret_code OUT NUMBER, last_in IN VARCHAR2, first_in IN VARCHAR2); PROCEDURE insert_individual(ret_code OUT NUMBER, last_in IN VARCHAR2, first_in IN VARCHAR2, notes_in IN VARCHAR2); PROCEDURE insert_individual(ret_code OUT NUMBER, last_in IN VARCHAR2, first_in IN VARCHAR2, d_o_b IN DATE, notes_in IN VARCHAR2); PROCEDURE insert_individual(ret_code OUT NUMBER, last_in IN VARCHAR2, first_in IN VARCHAR2, d_o_b IN DATE); /* add update and delete functions here */ END manage_individuals; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. The use of functions instead of procedures is merely a design consideration based on the assumption that it is better to clearly distinguish return codes from actual data. Overloading The capability to overload a subprogram is one of the primary advantages of packages. This feature is not available to stand-alone procedures and functions. Overloading is particularly useful when you are inserting records into tables with optional fields, or when you are updating existing records. When overloading is implemented correctly, you can minimize the data passed between the application and the database and reduce the possibility of error. Listing 18.6 shows an example of function overloading in the package body, based on the package specification in Listing 18.3. Listing 18.6. This package demonstrates function overloading. CREATE OR REPLACE PACKAGE BODY manage_individuals AS user_id VARCHAR2(20); FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2) RETURN NUMBER IS new_id NUMBER; BEGIN SELECT individual_ids.nextval INTO new_id FROM dual; INSERT INTO individual (id, last_name, first_name, last_updt_user, last_updt_date) VALUES (new_id, last_in, first_in, user_id, sysdate); COMMIT; RETURN(new_id); EXCEPTION WHEN OTHERS THEN ROLLBACK; RETURN(1); END insert_individual; FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, notes_in VARCHAR2) RETURN NUMBER IS new_id NUMBER; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. BEGIN SELECT individual_ids.nextval INTO new_id FROM dual; INSERT INTO individual (id, last_name, first_name, notes, last_updt_user, last_updt_date) VALUES (new_id, last_in, first_in, notes_in, user_id, sysdate); COMMIT; RETURN(new_id); EXCEPTION WHEN OTHERS THEN ROLLBACK; RETURN(1); END insert_individual; FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, d_o_b DATE, notes_in VARCHAR2) RETURN NUMBER IS new_id NUMBER; BEGIN SELECT individual_ids.nextval INTO new_id FROM dual; INSERT INTO individual (id, last_name, first_name, date_of_birth, notes, last_updt_user, last_updt_date) VALUES (new_id, last_in, first_in, d_o_b, notes_in, user_id, sysdate); COMMIT; RETURN(new_id); EXCEPTION WHEN OTHERS THEN ROLLBACK; RETURN(1); END insert_individual; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. FUNCTION insert_individual(last_in VARCHAR2, first_in VARCHAR2, d_o_b DATE) RETURN NUMBER IS new_id NUMBER; BEGIN SELECT individual_ids.nextval INTO new_id FROM dual; INSERT INTO individual (id, last_name, first_name, date_of_birth, last_updt_user, last_updt_date) VALUES (new_id, last_in, first_in, d_o_b, user_id, sysdate); COMMIT; RETURN(new_id); EXCEPTION WHEN OTHERS THEN ROLLBACK; RETURN(1); END insert_individual; BEGIN SELECT user INTO user_id FROM dual; END manage_individuals; Consider how you might accomplish this insert by using a user-defined record type or a single function that accepts all values. Using either alternative, applications calling the packaged insert function would have to ensure that null values are supplied for the fields for which no data exists. It is a much better programming practice to encapsulate all default values within the packaged routines rather than in various calling routines. The potential for problems is magnified for update operations. In update operations, the function would need logic to determine which fields are actually being updated or would have to update all columns in the table. In the latter case, the application would then be responsible for supplying all values accurately to avoid accidental column updates. Function overloading simplifies application development by enabling applications to supply only the values required for each transaction. Passing only the values needed to perform the update can improve performance through minimizing disk writes of unnecessary data. Retrieving Results Oracle stored procedures and functions currently do not support the retrieval of result sets. However, you can overcome this limitation by using a packaged subprogram. Remember that cursors declared at the package level persist for the Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. duration of the session. This enables a set of functions to open a cursor and perform operations on it, maintaining the current position within the cursor from one call to the next. Output parameters can be used to pass data from packaged functions to the calling application. Listing 18.7 shows an example of how these features can be used to return result sets to an application from a packaged subprogram. Listing 18.7. This code example uses a packaged cursor and functions to retrieve a result set. CREATE OR REPLACE PACKAGE address_type_info AS FUNCTION get_next_address_type(id_out OUT NUMBER, description_out OUT VARCHAR2) RETURN NUMBER; FUNCTION close_address_type RETURN NUMBER; FUNCTION reopen_address_type RETURN NUMBER; END address_type_info; CREATE OR REPLACE PACKAGE BODY address_type_info AS last_id NUMBER(10); CURSOR c1 IS SELECT id, description FROM address_type; FUNCTION get_next_address_type(id_out OUT NUMBER, description_out OUT VARCHAR2) RETURN NUMBER IS end_of_cursor EXCEPTION; temp_id NUMBER(10); temp_desc VARCHAR2(40); BEGIN FETCH c1 INTO temp_id, temp_desc; IF (temp_id = last_id) THEN RAISE end_of_cursor; ELSE last_id := temp_id; id_out := temp_id; description_out := temp_desc; END IF; RETURN(0); EXCEPTION Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. WHEN end_of_cursor THEN RETURN(1); WHEN OTHERS THEN RETURN(1); END get_next_address_type; FUNCTION close_address_type RETURN NUMBER IS BEGIN CLOSE c1; RETURN(0); EXCEPTION WHEN OTHERS THEN RETURN(1); END close_address_type; FUNCTION reopen_address_type RETURN NUMBER IS BEGIN OPEN c1; RETURN(0); EXCEPTION WHEN OTHERS THEN RETURN(1); END reopen_address_type; BEGIN OPEN c1; END address_type_info; Note that the cursor is opened in the body of the package itself. To retrieve the first row, an application need only call address_type_info.get_next_address_type to retrieve the first row. When this function returns 1, it informs the calling application that the end of the cursor has been reached. The application should then call address_type_info. close_address_type. The OPEN c1 statement in the body of the cursor will be executed only once, when the package is first loaded. In order to access the cursor a second time, the application must first call address_type_info. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... Although Oracle is a relational database, the Oracle package provides the object-oriented capabilities of encapsulation, information hiding, and function overloading Inheritance is not supported in version 7.1, but because Oracle seems to be moving in an object-oriented direction, it cannot be ruled out as a possibility in version 8 The newest of the Oracle development tools, including Power Objects and Oracle. .. Oracle error message into the package variable The example in Listing 18.8 is just one way to deal with exceptions in packages Oracle includes many other predefined functions used to handle exceptions, including SQLCODE, EXCEPTION_INIT, and RAISE_APPLICATION_ERROR SQLCODE returns the Oracle error number associated with an exception; EXCEPTION_INIT enables the developer to associate a name with an Oracle. .. access to the lookup tables referenced in Listing 18.9, you should create a separate package that does not include the insert functions Accessing Oracle Packages from Client Applications Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark Oracle' s implementation of the package fits the object model used in C++ particularly well, and using packages exclusively can simplify the process... members of the Individual class are identical to the columns of the Individual table in Oracle, with one exception The date of birth is stored as a string, requiring the overloaded form Individual::Insert(char*, char*, char*) to be able to distinguish a date from ordinary text in order to call the proper function in Oracle Perhaps a better implementation of the Individual class would include an overloaded... perform the insertion so that the data members could be protected Declaring the data members as public is analogous to declaring variables in an Oracle package specification Despite the shortcomings of the example in Listing 18.10, it demonstrates the point that if Oracle packages are designed properly they can be replicated in the client application This can simplify the design of the client application,... Windows development tools use ODBC to communicate with the database Unfortunately, the current Oracle ODBC driver does not support the access of packaged objects through ODBC In order to access packaged objects, you must create stand-alone functions and subprograms to call the packaged objects from within Oracle Listing 18.10 is an example of a stub that can be used to access packaged functions Because... ins_indiv_stub1; Products that communicate with SQL*Net and the Oracle Call Interface directly can be used to overcome this ODBCspecific limitation Using packages in an ODBC application is also inconsistent with one of the primary goals of ODBC, which is to provide database independence Object-Oriented Concepts As mentioned previously, Oracle packages provide several features that are typically associated... This point was illustrated in the context of Oracle packages in several ways In Listing 18.6, the user who last updated a record and the timestamp indicating when the record was last updated were inserted by a function, without any intervention by the user or the calling application Tables, functions, procedures, and other database objects can also be hidden by Oracle packages as illustrated in Listing... returned to the application This is just one example of how packages can be used to overcome many of the limitations of PL/SQL Exception Handling Oracle provides many predefined exceptions, and a number of functions and procedures that can be used to handle them Oracle implicitly raises predefined exceptions when they occur in PL/SQL blocks Among these, the OTHERS exception is extremely valuable because... Transaction Processing Understanding how a transaction begins, executes, and ends, and knowing what happens along each step of the way are vital parts of making Oracle work for you This knowledge is helpful not only to system and database administrators, but to Oracle developers as well Knowing when a transaction is issued a rollback segment, or how locking occurs in the database can drastically change the strategy . Accessing Oracle Packages from Client Applications ■ Object-Oriented Concepts ■ Summary 18 Object-Oriented Programming with Packages Although Oracle is. Exception Handling Oracle provides many predefined exceptions, and a number of functions and procedures that can be used to handle them. Oracle implicitly