Glossary and Abbreviations: Type (Class): the “description”, of a functional unit loosely coupled to other functional units which includes attributes describing its state and methods changing that state. If the “building instruction” aren’t detailed enough to allow someone to actually build and use the type, then it is said to be an abstract type . Instance of a Type (Object): one example of the type, a concrete object built following the instructions given in the type definition. Member: if talking of a specific type instance then a member is any of its attributes or methods, if talking of a type as a set, then members are instances of that type. R: concerning the relational tables. OR: Object relational, concerning the representation of objects by means of a relational database.
Encapsulation A feature that hides the complexities of a type from the user’s point of view. Ex. The user is usually interested in driving a car, not to detail how pushing the brake pedal is increasing the pressure on the brake primary oil circuit, which moves a membrane in a servo-mechanism, which multiplies the exertion etc. In Oracle this feature is not available, and for a reason, I guess: object must be storable as they are and encapsulation would imply the inaccessibility from the “ouside of the object” of some properties, but for this purpose there is already the column based security: GRANT … ON <table>(<list of fields>) … which is way more fine grained than the access modifiers in common OO languages (except, for example, the C++ family which has the concept of “friend” types), and yet is lacking the concept of local vs. global context opposition: the gobal context is seeing the type from the outside, which is the standard point of view of a User in Oracle, so it ignores protected and private members, while the local context is the one of a method executing on the type, which exists only inside the type methods. Unless another kind of privilege (local_context?) is created there would be no way to access the local information. Thus all attributes and methods are implicitly public. Inheritance Inheritance is the property of classes which allow them to be specified at different levels of abstraction, from the most abstract level to most practical one. Ex. A MeanOfTransportation is an abstract type for sails, trains, planes, cars, … the next level may be “Vehicle” which includes cars, TIRs, Trikes, …, then we have the Car (Audi, Fiat, VW, …) which includes all the specifics of any single model, then we have MyCar, which is able to refuse to start when it’s too cold and reproduces the characteristic shrieks whenever the doors are open. Here we do not discuss the multiple inheritance (as is in C++, for example) because in the database technology it has more downsides than benefits and as such it is luckily not present in Oracle’s implementation. In Oracle there is full support for Inheritance but for what concerns abstract types as they are called in OOP there is a catch: Oracle calls Abstract Data Type (Objects, available from version 8i) the ability to create a type (as in OOP) without methods. This is not compliant with OOP, as Abstract Types in that context are types in which at least one method is only specified by signature, and are by default not instantiable. Yet it is possible to declare “incomplete types” in Oracle just declaring the type specification, with the method’s signatures and making them INSTANTIABLE . You can instantiate an object of that type, create object tables of it but you cannot call the unimplemented methods (ORA-04067). This is quite a different concept of Abstractness, but it’s compatible with the old ADTs without hindering the usage of this features by programmers. Inheritance is achieved by the use of the keyword UNDER for types which are NOT FINAL . The meaning of FINAL is the same as in Java, it means “not extendable” thus you cannot derive a subclass from a FINAL type.
Polymorphism Polymorphism is a wide and polymorphic concept. It refers to the ability of referring to some logic leaving to the internal mechanism of the language the actual implementation executed. Usually two different kinds of polymorphism are recognized: overriding and overloading , both can be available at runtime or compile time (with the first widely more useful than the second). Overriding allows one subtype to redefine a NOT FINAL method of one of its superclasses (through the use of OVERRIDING ), thus allowing the programmer to specialize the logic for the specific subclass. Overloading instead allows to have the same logic implemented in different ways for different input parameters. One example is the ability of operators like ‘+’ to sum up integers and floating point numbers. These concepts are often associated with the Dynamic Dispatch mechanism in which it is the object, at runtime, who chooses the fitting method to execute in a call. Following we see an example of overriding (Oracle developers already know very well the overloading, as procedures and packages allow a great deal of it) in the implementation of the method area() in the subclasses of MyShapeType .
see http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1545206281987 ) drop table MyShapeTab ; drop type MyCircleType; drop type MyRectangleType; drop type MyShapeType; create or replace type MyShapeType as object ( Color varchar2 ( 10 ), member function area return number ) not final instantiable; / create or replace type MyCircleType under MyShapeType ( Radius number , overriding member function area return number ) not final; / create or replace type body MyCircleType is overriding member function area return number is begin return 3.14 *Radius*Radius; end; end; / create or replace type MyRectangleType under MyShapeType( SideA number(8) , SideB number(8) , overriding member function area return number ) not final; / create or replace type body MyRectangleType is overriding member function area return number is begin return SideA*SideB; end; end; create table MyShapeTab (shape MyShapeType); insert into MyShapeTab values(MyCircleType( 'blue' , 1 )); insert into MyShapeTab values(MyRectangleType( 'red' , 10 , 20 )); commit; select case when T.shape is of (MyCircleType) then 'MyCircleType' when T.shape is of ( MyRectangleType ) then 'MyRectangleType' end SubType, T.shape.area() Area from MyShapeTab T SUBTYPE AREA --------------- ---------- MyCircleType 3,14 MyRectangleType 200
The given example could be easily extended to represent hundreds of different shapes without changing (at least, changing explicitly) the storage structure of data. With a traditional Relational approach, the standard to represent different types is to create one table per type and then unite them as needed in every query. The query to find which shapes are of a given color would be, for example, very lengthy and would require to be changed every time a new type of shape is introduced. Without considering the problem of indexing: in that case the query would access one index for every type, instead with the OR (Object Relational) we have only one index. About this topic we see the syntactic difference between Relational and Object Relational tables. If we want to create said index on the shape’s color we have to be coherent with the point of view we choose: in our example create index shape_color_idx on MyShapeTab(color); would return, as expected, ORA-00904: &quot;COLOR&quot;: invalid identifier . That’s because we created MyShapeTab as Relational table with an Objects as column, so the right way would be create index shape_color_idx on MyShapeTab(shape.color); If we stick to OR model we would have created the table like this: create table MyShapeTab2 of MyShapeType; so that the first create index would have worked.
Identity This is again an aspect of OOP too often overlooked as it’s available since the beginning of information technology: Objects are, to use the Relational jargon, Strong entities, that means they are uniquely identifiable, they are individuals, when they are manipulated it is a single place where the changes take place. In databases technology there is big behavioral difference between strong and weak entities, which is the existence of a primary key, but more generally speaking it is always possible to treat a strong entity as it was week (just disable the PK), on the contrary not always it is possible to make strong a weak entity. This translates in current programming languages to the difference between “by value” and “by reference” parameter passing, or an object and a pointer to that object. The importance of strong entities is so high that many of the most common programming languages nowadays do have only references to objects and the occurrence of “weakification” is usually marked by some special construct signaling the occurrence of a cloning operation. As stated before databases know very well this mechanism, but since the introduction of ADT the granularity of this composition relations changed, if before it went down from the Entity itself (Strong or weak depending on the presence of a PK) to one primitive type (weak), now it can stop in the middle, at a level which aggregates many columns together. Of course Primary Keys can be the answer, but it is not common in OOP to identify, or even to care for that, identity and uniqueness are given as tool from the very foundations of the metamodel. To provide such identity in an OR way Oracle has the REF. The REF is build upon the OID (Object IDentifier) which in turn can be generated and maintained by the engine or PK-based. In the first case the OID is a 16 bytes raw value uniquely generated and accurately kept unique through distributed or replicated environments (replicated environments are especially vulnerable to object “failures”, there are a set of guidelines on how to work on objects to make replication work correctly – we won’t talk about this), in the second case (pkOID) it has the same properties as the PK.
There are essentially three kinds of REFs: Scoped : the REF is pointing to an object on a specific table. It occupies as much as the OID does. Example: you have person objects references somewhere, but on the base you use them to represent both users and managers . You can leave it unspecified, and let the REF itself carry the information about which table is storing the object (unscoped REF), or specify in the definition of the REF which table stores the referenced object. Unscoped : the REF has two parts, the OID of the referenced objet and the OID of the object in which it is stored, another 16 bytes. Oracle recommends to use scoped REFs whenever possible, because they are stored more efficiently and guarantee a little better performance. Unscoped with ROWID hint : to the standard unscoped REF the ROWID of the referenced object is added, another 10 bytes. So in the worst of situations and unscoped with rowid ref will be 36 bytes long. In the relational theory the concept of a “pointer” is forbidden for the reason that the relational schema must be independent of the underlying physical organization of data, instead primary keys (the logic part) and indexes (taking care of the connection between model and physical layer) are provided. The presence of the ROWID can improve object navigation at the expense of more space utilization but is prone to disrupt the integrity of data when rows migrate, for whatever reasons, and as with any composition relation, if not enforced by constrains, the REF itself can be dangling, i.e. referring to a non-existent object on the referenced object table. For what concerns the ROWID hint, Oracle always checks if the ROWID points to the OID it has on the REF and if not it reverts to the OID index, for dangling REFs instead, there is nothing to do except the good practice of defining and enforcing a foreign key. To maintain REFs Oracle supplies the command ANALYZE … VALIDATE REF UPDATE SET DANGLING TO NULL, where the first part fixes the ROWID hints where needed.
Note: we include as the first parameter an instance of the same class by reference (NOCOPY), this is because the engine is by default applying the methods on a copy which is then returned back. The self keyword is like this in C++-like languages, and it has nothing to do with PHP’s self static scope qualifier.
Upcasting is automatic, downcasting to access subclass' methods behaves as expected, but upcasting doesn't work on overridden methods! I've tested this on Windows, 10.2.0.1.0 and 18.104.22.168.0.
Static You can declare static methods and call them prefixing the type name. They follow the same rules as static methods in all the other OO languages. Static properties are forbidden instead, as they pose a number of logic problems, in facts they are class’ properties as opposed to instance’ properties. Thus they should be stored in the dictionary along with the structure of the class. But what then about already stored instances when these static properties change? Imagine what to do when you retrieve two instances with two different values ... When static properties are needed then you may consider using a package to hold them. Indeed packages have the same properties as OOP classes with all static methods and fields.
Note: we include as the first parameter an instance of the same class by reference (NOCOPY), this is because the engine is by default applying the methods on a copy which is then returned back. The self keyword is like this in C++-like languages, and it has nothing to do with PHP’s self static scope qualifier.
Essentially there are two kinds of table in Oracle: Relational and Object Relational. (OR) CREATE TABLE object_relational AS TABLE OF <some_type> (R) CREATE TABLE relational (some_type <some_type>) When you define an OR table there are some important aspects to keep in mind. OID and Primary Keys Every object must have a unique identifier and if the developer does not specify it then Oracle assigns one 16 bytes long of its choosing. You can specify yourself which primary key constitutes the Object ID (called pkOID) and save some space on the table and on the OID indexes. CREATE TABLE Person OF Person_o ( CONSTRAINT person_pk PRIMARY KEY (tax_id) … ) OBJECT IDENTIFIER IS PRIMARY KEY; Indeed even if a primary key is defined, you can keep the generated OID as object identifier. This could be the case when your primary key is bigger than 16bytes.
When you need to store a REF or pkREF you should always scope it unless the scattering of references on many entities is really a feature of your model. Ex: one Experiment is lead by Scientist which is in turn a Person. Also Technician-s and Employee-s are Person-s. Rather than store all the Person type instances in a Person OR table, we choose to store them separately, in Technician, Employee and Scientist Entities. The Experiment table, OR table for Experiment_o types, for the sci_leader field should look like CREATE TABLE Experiment OF Experiment_o ( sci_leader SCOPE IS Scientist … ); given Experiment_o type has a field sci_leader of type Person. If facts there is no purpose allowing references to Person-s which are not scientists. Note the fields are defined in the type while the storage properties are specified in the corresponding OR tables. This REF is efficiently stored and uses the fastest indexes. If conditions suggest those indexes won't be fast enough, you can choose CREATE TABLE Experiment OF Experiment_o ( sci_leader WITH ROWID … );
Check Constraints and Triggers There is full freedom of declaring constraints on object properties on OR tables as we do on R tables. The only limit on Check Constraints is they cannot use DEREF (the action of getting the actual object from its REF) or any nested table construct (plus standard limitations on check constraints). Triggers can be defined on object tables as well, but not on the storage table for nested tables.
The type_id, when queries are frequently considering this field (through the use of IS OF predicate), can be indexes, and being usually at very low cardinality is an excellent candidate for a bitmap index. Given an object its type_id is accessible through the SYS_TYPEID() function.
Imagine a real world hierarchy and the storage structure generated by such a metamodel! That would be a disaster, ending up with just one huge table per root type. This originates from the OO metamodel that considers inheritance an “is of” relation, so that a subtype is always substitutable in place of a supertype because the subtype is also all its supertypes. The way oracle represent hierarchies is not the only way, but is one simple and fast enough to be practical; another approach could be one table per type-slice, that is all the successive descendants properties of a type find place in a separate table. This produces smaller tables overall and smaller storage, but needs a number of joins to retrieve just one object. Impractical. Another approach is the LOB or XML approach in which the object is rendered through some middleman language (XML). This last approach would be really interesting to test in Oracle due to all its XML oriented features.
Nested tables add a 16 bytes unique RAW key to the parent table, key which is stored on the nested table to perform joins, called NESTED_TABLE_ID. Beware, Oracle does not create an index on the storage table for the NESTED_TABLE_ID! Every nested table in a table adds its own unique key, which is a waste of space, but it has an optimization hint: RETURN AS VALUE/LOCATOR. In the first case the nested table data is fetched and sent along with the query data, in the second only a pointer to that data is sent to the application, and the access to that data triggers the fetch of the actual content. Nested tables, being tables, do have all the possible creation options of any table, for example they can be made Index Organized. If you consider the kind of model in which a nested table is used, a parent-child relation, in which the direction of navigation is from parent to child, we see that we are by design fetching for every parent row all the child rows on the nested table. If we can suppose the nested table has “short” rows, then this is pretty much the use case for IOT A limitation is that you cannot define foreign keys on nested table, or query directly from them (without using dirty tricks), although you can create indexes on them.
Type Evolution (Altering Types) The OR paradigm imposes many constraints on the horizon of possibilities, mainly because it follows a strict logic around type hierarchies. For example, once a type (its specification only! The body is totally encapsulated inside the type) has a dependent object it is non-droppable and non-replaceable. It is alterable though. In general you can (from v.9 on): add and drop attributes and methods: when adding an attribute to a type with subtypes consider that all the default constructors of the subtype will suffer a parameter shifting due to the implicit ordering of arguments because the superclass' attribute always precede the subclass' – another good reason to add user defined constructors. enlarge an attribute; modify final and instantiable predicates; modify nested collections' properties. But consider that as well as with tables types do have dependencies and before evolving a type is a good idea to apprehend its dependencies. This is usually the reason scaring DBAs from allowing OR features in their DB. Some operations are in facts more complicated and involve more steps, at first glance, than standard relational table maintenance. But if you consider the relations of data from the business point of view the amount of effort to change the model is the same, the only difference is that using the OR metamodel a greater part of that logic is transferred to the data model itself, thus making its change a bit more complex. Please refer to Database Application Developer's Guide - Object-Relational Features, c. 7 for an introduction on the topic.
Oracle Objects And Transactions
Oracle Objects and Transactional Systems Andrea Parrilli Oracle and Java Developer [email_address]
Topics - OID and Primary Key - REFs and Foreign Keys - Substitutability - Collections - Type evolution - Encapsulation - Inheritance - Polymorphism - Identity Objects for storage OOP in Oracle Objects as a developer tool - Casting - Static - References
OOP in Oracle - Not really available - Objects must be storable - Not exactly as column security GRANT … ON <table>(<list of fields>) … - Different scopes: “user scope” vs. “code scope” Who's asking (authorized user vs. unauthorized user) vs. Where are we (local vs. global context) Encapsulation - Single inheritance - No abstract types: just declare the type [NOT] INSTANTIBLE If a non implemented method is called then ORA-04067 - Extendability declared through [NOT] FINAL Inheritance
OOP in Oracle - Compile or Runtime - Overriding: Specialize subtypes methods - Overloading: Specialize methods on input parameters: available since the beginning in PL/SQL - Dynamic method dispatch: at runtime the right method is chosen depending on the object type Polymorphism Select case when T. shape is of ( MyCircleType ) then 'MyCircleType' when T. shape is of ( MyRectangleType ) then 'MyRectangleType' end SubType, T. shape . area () Area from MyShapeTab T SUBTYPE AREA --------------- ---------- MyCircleType 3,14 MyRectangleType 200
OOP in Oracle create or replace type MyShapeType as object ( Color varchar2(10), member function area return number ) not final instantiable; create or replace type MyCircleType under MyShapeType ( Radius number, Overriding member function area return number ) not final; create or replace type body MyCircleType is Overriding member function area return number is begin return 3.14*Radius*Radius; end; end; create or replace type MyRectangleType under MyShapeType( SideA number(8), SideB number(8), overriding member function area return number ) not final; create or replace type body MyRectangleType is overriding member function area return number is begin return SideA*SideB; end; end; create table MyShapeTab(shape MyShapeType); insert into MyShapeTab values(MyCircleType('blue', 1)); insert into MyShapeTab values(MyRectangleType('red', 10, 20)); commit;
OOP in Oracle Relational approach to represent many types - One table per type (too many tables, complex queries, difficult maintenance ) - One table to store type metadata, one or more tables to store instances' values (slow method, many joins, application can corrupt data) Object Relational approach to represent many types - One object relational table per type function (business related) - allows better performance than the “melting pot” approach, neater design - Accurate storage design (see substitutability) - More logic inside database objects - Less database objects to maintain
OOP in Oracle Reflection - First cited in 1982, B. C. Smith - Widely available in interpreted languages with easy interfaces - In databases since first design (dictionary) - DBMS_METADATA package, we can get XML representation of any DB object, then parsing is needed (still too complicated to be widespread tool) Identity - in OOP objects are always strong entities (every instance has its own pointer, cloning must be explicitly declared): always passed by reference. - in OOP programmers do not usually care to define a “primary key” - in DB entities are weak by default, can be made strong - to implement object identity and referencing Oracle provides OID (Object Identifier), and REFs. - this is tricky! In OOP when we want to compare two instances we can do it on an object level (comparing pointers) or on a value level (comparing attributes). Oracle uses only the pointers (the OID) to compare objects when replicating, so when updating an object we must take care not to substitute it but change the attributes values (see replication guidelines)
OOP in Oracle OID - Can be Oracle-managed - RAC-wide 16 bytes unique identifier - or Primary key based (uses as much space as the PK) REF - Unscoped REF: OID plus DB-object-id - Scoped: just the OID - Unscoped with ROWID: unscoped REF plus 10 bytes ROWID hint. This is a violation of the relational paradigm! Can lead to disrupted ROWIDs: ANALYZE … VALIDATE REF Referential integrity - REFs are not Foreign Keys: you have to define and enforce them by yourself, a disrupted reference is said to be “dangling” ANALYZE … UPDATE SET DANGLING TO NULL
Objects as a programming tool Object support is available in SQL and PL/SQL SQL: downcasting (narrowing) CREATE TABLE shapes OF MyShapeType; INSERT INTO shapes VALUES(mycircletype('red', 33)); INSERT INTO shapes VALUES(myrectangletype('blue', 11, 22)); SELECT TREAT(VALUE(s) AS MyCircleType).radius FROM shapes s ORDER BY 1 NULLS FIRST; TREAT(VALUE(S)ASMYCIRCLETYPE).RADIUS ------------------------------------ 33 TREAT will return null if the argument is not in the lineage of the destination type. In SQL method calls on nulls are null! Quite different from Java ... But in PL/SQL access into null exception is risen
Objects as a programming tool Object support is available in SQL and PL/SQL SQL: upcasting (widening) is implicit Note that when a field is not substitutable the insertion of a subtype, even upcasted, will generate an error! CREATE TABLE abstract_shapes OF MyShapeType NOT SUBSTITUTABLE AT ALL LEVELS; SQL> INSERT INTO abstract_shapes 2 SELECT TREAT(VALUE(s) AS MyShapeType) FROM shapes s; SELECT TREAT(VALUE(s) AS MyShapeType) FROM shapes s * ERROR at line 2: ORA-00932: inconsistent datatypes: expected - got UDT
Objects as a programming tool CREATE OR REPLACE TYPE superclass AS OBJECT ( sup NUMBER, CONSTRUCTOR FUNCTION superclass RETURN self AS RESULT, MEMBER PROCEDURE init_superclass(self IN OUT NOCOPY superclass), NOT FINAL MEMBER FUNCTION do_something RETURN NUMBER ) NOT FINAL INSTANTIABLE; PL/SQL: Example
Objects as a programming tool CREATE OR REPLACE TYPE BODY superclass IS CONSTRUCTOR FUNCTION superclass RETURN self AS RESULT IS BEGIN self.init_superclass(); RETURN; END superclass; MEMBER PROCEDURE init_superclass(self IN OUT NOCOPY superclass) IS BEGIN self.sup := 33; END; NOT FINAL MEMBER FUNCTION do_something RETURN NUMBER IS BEGIN return self.sup; END do_something; END;
Objects as a programming tool CREATE OR REPLACE TYPE subclass UNDER superclass ( sub NUMBER, CONSTRUCTOR FUNCTION subclass RETURN self AS RESULT, MEMBER PROCEDURE init_subclass(self IN OUT NOCOPY subclass), OVERRIDING MEMBER FUNCTION do_something RETURN NUMBER, MEMBER FUNCTION only_sub RETURN NUMBER ) FINAL INSTANTIABLE;
Objects as a programming tool CREATE OR REPLACE TYPE BODY subclass IS CONSTRUCTOR FUNCTION subclass RETURN self AS RESULT IS BEGIN self.init_superclass(); self.init_subclass(); RETURN; END subclass; MEMBER PROCEDURE init_subclass(self IN OUT NOCOPY subclass) IS BEGIN self.sub := 11; END init_subclass; OVERRIDING MEMBER FUNCTION do_something RETURN NUMBER IS BEGIN RETURN self.sub; END do_something; MEMBER FUNCTION only_sub RETURN NUMBER IS BEGIN RETURN 77; END only_sub; END;
Objects as a programming tool DECLARE o superclass := new subclass(); –- sub=11, sup=33 BEGIN -- this will genereate an error -- dbms_output.put_line('sub=' || o.sub); dbms_output.put_line('sub=' || TREAT(o AS subclass).sub); -- these will generate an error too -- dbms_output.put_line('only_sub(): ' || o.only_sub()); dbms_output.put_line('Overridden as superclass: ' || TREAT(o AS superclass).do_something()); dbms_output.put_line(o.do_something()); END; sub=33 Overridden as superclass: 33 33 PL/SQL procedure successfully completed.
Objects as a programming tool Static scope - Can define static methods, when the the object context is not needed, this slightly improves performance - Static properties are forbidden: objects must be storable Where to store static properties? The dictionary? What with old instances? Problems ... Application design should be very accurate on how to manage them. Although something like “VOLATILE” property, not stored but used in internal mechanisms of the class could be useful, for example to use PL/SQL types, and from that to STATIC VOLATILE, there is not this much distance When a static scope is needed use a package, they very look like singletons of purely static classes.
Objects as a programming tool REF in PL/SQL DECLARE R REF MyClass; S MyClass := new MyClass(33, 11); BEGIN R := REF(S); … This simply won't work: a REF is not a pointer in memory or an object id as in PHP or Java. It rather is a pointer to the storage area where the object - is held. How do I share the same object between two classes? As far as I know you can't: - NOCOPY is just an hint; - even if it was always applied we miss something like RETURN NOCOPY - default is cloning, copying
Objects as a programming tool How to build a session-scoped service class? Instead of storing the service classes (can be very complicated and will introduce many nested tables and object tables just to hold few instances, or just one) and getting a REF to it, may be advisable to use packages in the way the Oracle framework ( DBMS_* , UTL_* , etc.) is programmed. - Get a service id from the package: this correspond to getting an instance of the service class through a Factory. To program this in PL/SQL is easy: every instance property becomes a TABLE OF <that_property_type> INDEX BY PLS_INTEGER , the latter is the service id and even if said property is itself a table, it would work anyway, because a table is a valid base type for a PL/SQL table. - In every service call include the service id: in an OO language this is done by the compiler when you invoke a method on an instance: obj.method(param) -> method_proc(&obj, param); - Share the service id with all the components needing it as you would share an instance of the class; - Upon termination release the resources!
Objects as a programming tool How to build a instance -scoped service class? Business Delegate pattern - One session runs the service (server, BusinessService) - Others access this service by means of a stub, the BusinessDelegate, which in turn exposes all the relevant business methods, and instead of executing their logic locally, simply invoke the server’s methods over a link. - The only logic needed in the business delegate is the use of LookupService to get the link to forward the messages to the server and get back the response. On single instances Pipes (DBMS_PIPE) are an excellent link and Lookup. All the logic can be coded in just one package!
Objects as a programming tool Object Types are tensors (as opposed to scalars) and as such they do not have a predefined ordering in their dominion. To provide this ordering we may define an ORDER or MAP method (not both) in the type to provide ordering: the first performs the comparison in the method body itself returning 1, 0 -1 depending on the comparison outcome (>, =, <) and this opacity implies the optimizer has limited freedom of organizing the sort process other than nested looping on the entries; the second method, instead, maps the object to a scalar value allowing for hash sorts. Thus it is recommended to define, when possible, a MAP method rather than an ORDER. Ordering Objects
Object Storage Two kinds of table in Oracle: Relational and Object Relational (OR) CREATE TABLE object_relational OF <some_type> (R) CREATE TABLE relational (some_type <some_type>) OID and PK CREATE TABLE Person OF Person_o ( CONSTRAINT person_pk PRIMARY KEY (tax_id) … ) OBJECT IDENTIFIER IS PRIMARY KEY; Indeed even if a primary key is defined, you can keep the generated OID as object identifier. This could be the case when your primary key is bigger than 16 bytes. But design consideration could require the usage of a PK anyway, as OIDs are opaque.
Object Storage <ul><ul><li>REFs and scoping </li></ul></ul>One Experiment is lead by a Scientist which is in turn a Person. Also Technician-s and Employee-s are Person-s. Rather than store all the Person type instances in a Person OR table, we choose to store them separately, in Technician, Employee and Scientist Entities CREATE TABLE Experiment OF Experiment_o ( sci_leader SCOPE IS Scientist … ); CREATE TABLE Experiment OF Experiment_o ( sci_leader WITH ROWID … ); Note the fields are defined in the type while the storage properties are specified in the corresponding OR tables. This REF is efficiently stored and uses the fastest indexes SCOPED WITH ROWID is not supported. Note none of the two ways is enforcing referential integrity
Object Storage <ul><ul><li>REFs and Foreign Keys </li></ul></ul>CREATE TABLE Experiment OF Experiment_o ( sci_leader SCOPE IS Scientist … CONSTRAINT experiment_sci_leader_fk FOREIGN KEY sci_leader REFERENCES Scientist ... ); <ul><ul><li>Triggers and Check Constraints </li></ul></ul>- Triggers on Object Relational tables work in the same way they do on Relational tables - Cannot define a trigger on a Nested Table - DEREF is forbidden in Check Constraints
Object Storage SQL> desc myshapetab Name Null? Type -------------------------- -------- ---------------------------- SHAPE MYSHAPETYPE SQL> set describe depth all SQL> desc myshapetab Name Null? Type -------------------------- -------- ---------------------------- SHAPE MYSHAPETYPE MYSHAPETYPE is NOT FINAL COLOR VARCHAR2(10) METHOD ------ MEMBER FUNCTION AREA RETURNS NUMBER Getting Information on Types
Object Storage select name, length, precision# from sys.col$ where obj# = (select object_id from user_objects where object_name = 'MYSHAPETAB'); NAME LENGTH PRECISION# ------------------------------ ---------- ---------- SHAPE 256 SYS_NC00002$ 16 SYS_NC00003$ 10 SYS_NC00004$ 22 SYS_NC00005$ 22 8 SYS_NC00006$ 22 8 How is Oracle storing Objects? (1) - A virtual column; - 16 bytes OID of the object; - 10 bytes for the color field in the base class MyShapeType; - 22 bytes for the unconstrained number MyCircleType.radius; - 2x22 bytes for the constrained sides of MyRectangleType; - Not shown here are also a small amount of space (usually one bit from v10 on) to represent the nullity of an Object (as opposed to an Object with all null attributes) and the TYPE_ID hidden column, varying from 1 to 4 bytes, to record the actual subtype stored.
Object Storage How is Oracle storing Objects? (2) If we do not specify otherwise Oracle is representing the whole type hierarchy originating from the base type of the table in the table itself so that it can accommodate indifferently objects of all the types into it. Every sibling in the hierarchy is using a projection of the table, ignoring its siblings' properties
Object Storage Oracle offers an option to restrict the substitutability on one object column to one specific type via the predicates [NOT] SUBSTITUTABLE AT ALL LEVELS and IS OF (ONLY <type>). Controlling Object Storage create table MyShapeNotSubs of MyShapeType not substitutable at all levels; insert into myshapenotsubs values (mycircletype('INDIGO', 22)) * ERROR at line 1: ORA-00932: inconsistent datatypes: expected - got UDT select name, length, precision# from sys.col$ where obj# = (select object_id from user_objects where object_name = 'MYSHAPENOTSUBS'); NAME LENGTH PRECISION# ------------------------------ ---------- ---------- SYS_NC_OID$ 16 SYS_NC_ROWINFO$ 1 COLOR 10
Object Storage The importance of substitutability derives also by the fact that whenever a subtype is added to the hierarchy, all the tables having substitutable columns of the supertype's type are altered to accommodate the new subtype's attributes. Also when a subtype is dropped, the VALIDATE option removes the subtype's column from all the relevant tables. This can be accomplished after type evolution by the use of ALTER TABLE ... UPGRADE Notes on Object Storage Table-wide substitutability clauses are incompatible with more fine grained COLUMN <column> IS OF … And for object tables turning off all substitutability means even object aggregated under the table type are not substitutable. See next page
Object Storage CREATE TYPE aggregated AS OBJECT (shape myshapetype) NOT FINAL INSTANTIABLE; CREATE Type aggregated_sub UNDER aggregated (circle mycircletype) FINAL INSTANTIABLE; CREATE TABLE aggregate_sub_t OF aggregated_sub NOT SUBSTITUTABLE AT ALL LEVELS; SQL> insert into aggregate_sub_t values ( 2 myshapetype('RED'), mycircletype('BLUE', 33)); 1 row created. Notes on Global Substitutability SQL> insert into aggregate_sub_t values ( 2 mycircletype('RED', 44), mycircletype('BLUE', 33)); mycircletype('RED', 44), mycircletype('BLUE', 33)) * ERROR at line 2: ORA-00932: inconsistent datatypes: expected MONDO.MYSHAPETYPE got MONDO.MYCIRCLETYPE
Object Storage They model a Weak Entity. Have you ever seen one? If you have information in your DB then you are going to query it. Nested tables and Varrays - nested tables have no theoretical upper bound while varrays are fixed length lists; - nested tables are stored in another table than the one using them, thus implicitly generating a JOIN operation at query time. Varrays are stored inline (if they fit the maximum atomic column length of 4000 bytes) as raw value or, if bigger, as LOBs (beware if using clustered tables, LOBs are not supported) with less overhead than nested tables. Their manipulation, on the contrary, is more heavy since their update involves the substitution of the entire collection with the updated one, while nested tables are manipulated row by row. - Nested tables can be manipulated by SQL while varrays can't. You can ask a varray if it contains a value but you cannot add one without a little PL/SQL (working with types this isn't such a big deal); - Ordering: nested tables do not define any intrinsic ordering, while varrays are inherently ordered collections; Differences
Object Storage - Nested tables add a 16 bytes unique RAW key to the parent table, key which is stored on the nested table to perform joins, called NESTED_TABLE_ID. Beware, Oracle does not create an index on the storage table for the NESTED_TABLE_ID! - - Every nested table in a table adds its own unique key, which is a waste of space - Nested tables cannot be directly referenced by Queries (without tricks) - You cannot define foreign keys or triggers on them Nested Tables Cons Nested Tables Pros - Optimization hint: RETURN AS VALUE/LOCATOR. In the first case the nested table data is fetched and sent along with the query data, in the second only a pointer to that data is sent to the application, and the access to that data triggers the fetch of the actual content (like a REF CURSOR, or a LOB with RETURN AS LOCATOR). - Can be made IOT: after all their use case is navigation from parent to children ...
Object Storage Once an Type has a dependency it is no more replaceable. This is because Objects must be storable, thus dropping and recompiling would mean leaving all the tables with old instances without a definition. They can be altered like a table with same attributes and the substitutability, extendability and instantiability can be modified. Methods and constructors signatures can be modified. All method implementations (the type body) are freely modifiable Type Evolution - Unlike OO languages classes, changing a type is way more difficult than just recompiling the class! - Changing a type has potential effects on many tables: must be done carefully. This latter problem is not really a problem if seeing the question from the data model point of view. If you need a new attribute to describe an entity then you have to add it whenever the entity appears. In an OR design you can do it by changing the type definition, while in a pure Relational design you have to manually change all the occurrences of that type.
Conclusions - In PL/SQL units, as a support tool to define PL/SQL tables - In tables to optimize navigation: REF WITH ROWID - In tables to neatly define your data model - In tables to support common mechanisms for auditing, historical queries, ... When/where to use Objects? - To program frameworks or services - To implement a full OR schema using nested tables - Nested tables are bad. Use standard tables instead or varrays if they fit and updates are infrequent It is true OR database design is nearer to the applications' way of representing and manipulating data, but applications change over time, data stands (T. Kyte) When/where to use Objects? When/where NOT to use Objects?