Java Subclass with Struct
This blog post relies on the “How to write a Java Class with the SQLData Interface” blog post. Oracle object types support subclasses with the UNDER keyword from Oracle Database 10g forward. Naturally, you can create Java classes that subclass another Java class.
A subclass is a specialized behavior of a parent class. Parent classes are considered more generalized than their subclasses. There are certain rules and behaviors for using Java subclasses as Oracle object subtypes. That’s why we wrote this section.
The two subsections show you how to implement a Java subclass and how to implement an Oracle object subtype.
Creating a Java Subclass
Like Java classes and Oracle object types, you must implement the Java class file first, and the PL/SQL wrapper second. Java subclasses rely on the existence of their parent, and object subtypes rely on the existence of their parent.
The following ItemSt class is a subclass or specialization of the Java Item class. You should take note that it doesn’t redefine the attributes of the parent class. The ItemSt class does add one new attribute, so we can demonstrate how Java subclasses or Oracle subtypes work.
SQL> CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "ItemSt" AS 2 // JAVA LIBRARY imports. 3 import JAVA.SQL.*; 4 import JAVA.io.*; 5 import oracle.SQL.*; 6 import oracle.jdbc.*; 7 import oracle.oracore.*; 8 import oracle.jdbc2.*; 9 import JAVA.math.*; 10 11 PUBLIC class ItemSt EXTENDS Item implements SQLData 12 { 13 // Implement the attributes AND operations FOR this TYPE. 14 PRIVATE String bluray; 15 16 // A getter FOR the rating attribute. 17 PUBLIC String getBluray() { 18 RETURN this.bluray; } 19 20 // A setter FOR the rating attribute. 21 PUBLIC void setBluray(String bluray) { 22 this.bluray = NEW String(bluray); } 23 24 // A setter FOR this object. 25 PUBLIC void setItem(Struct item) throws JAVA.SQL.SQLException { 26 27 // Assign Item instance variables. 28 super.setItem(item); 29 30 // Get the attributes OF the Item object. 31 Object[] attributes = (Object[]) item.getAttributes(); 32 33 // Assign the SUBTYPE element. 34 this.bluray = (String) attributes[6]; } 35 36 // DECLARE an instance toString method. 37 PUBLIC String toString() { 38 RETURN super.toString() + "Blu-ray [" + this.bluray +"]\n"; } 39 40 /* Implement SQLData interface. 41 || -------------------------------------------------------- 42 || Required interface components: 43 || ============================== 44 || 1. String sql_type instance variable. 45 || 2. getSQLTypeName() method returns the sql_type value. 46 || 3. readSQL() method to read from the Oracle session. 47 || 4. writeSQL() method to write to the Oracle session. 48 */ 49 50 // Required INTERFACE variable. 51 PRIVATE String sql_type; 52 53 // Reads the stream FROM the Oracle SESSION. 54 PUBLIC void readSQL(SQLInput stream, String typeName) throws SQLException { 55 56 // CALL TO parent class. 57 super.readSQL(stream, typeName); 58 59 // Map instance variables. 60 sql_type = typeName; 61 bluray = stream.readString(); } 62 63 // Writes the stream TO the Oracle SESSION. 64 PUBLIC void writeSQL(SQLOutput stream) throws SQLException { 65 // CALL TO parent class. 66 super.writeSQL(stream); 67 68 // Map instance variables. 69 stream.writeString(bluray); } 70 71 /* 72 || -------------------------------------------------------- 73 || End Implementation of SQLData interface. 74 */ 75 } 76 / |
Lines 25 through 34 show you how to handle a Struct of the ItemSt object type. This is where we implement a specialized setItem() method. You should note that in line 28 the subclass calls the parent class (or super) setItem() method. The balance of the setItem() method converts the Struct to an array of objects and then assigns the correct element to the
bluray variable.
Lines 38 and 39 show you how to handle the toString() method of a subclass. Like the setItem() method, the toString() method must call super to get the behavior of the parent
before adding on its own behavioral characteristics. Lines 57 and 66 also make super calls to the
readSQL() and writeSQL() methods of the parent class.
You should discover from this example that a subclass depends on the program logic in the parent class. Also, you should see that subclass methods generally override behaviors of equivalent methods in the subclass.
Testing the Java Subclass
Testing subclass behavior is also known as testing polymorphic behavior. Polymorphic behavior occurs when a subclass performs differently from its parent and sibling classes.
We need to slip one more example in here, and that’s a stand-alone function that constructs either a super class or subclass. That’s done with the following get_item function. You should note two things about the get_item function. First, it takes a list of parameters necessary to create an item_obj_st instance. Second, it makes any subtype-only parameters optional parameters in the function’s signature.
SQL> CREATE OR REPLACE FUNCTION get_item 2 ( id NUMBER 3 , title VARCHAR2 4 , subtitle VARCHAR2 5 , rating VARCHAR2 6 , rating_agency VARCHAR2 7 , release_date DATE 8 , bluray VARCHAR2 DEFAULT NULL ) 9 RETURN item_obj IS 10 /* Declare a local variable. */ 11 lv_item ITEM_OBJ; 12 BEGIN 13 /* Check for the subtype attribute. */ 14 IF bluray IS NULL THEN 15 lv_item := item_obj( id 16 , title 17 , subtitle 18 , rating 19 , rating_agency 20 , release_date); 21 ELSE 22 lv_item := item_obj_st( id 23 , title 24 , subtitle 25 , rating 26 , rating_agency 27 , release_date 28 , bluray); 29 END IF; 30 31 /* Return a type. */ 32 RETURN lv_item; 33 END; 34 / |
Line 8 provides the bluray attribute, which only belongs to the item_obj_st object type. The bluray parameter is optional because an item_obj instance can ignore that parameter. The IF statement on line 14 checks whether the subtype’s bluray variable is null. The get_item function creates an item_obj instance when the bluray variable is null and an item_obj_st when the bluray variable isn’t null.
The simplest way to test polymorphic behavior is an anonymous PL/SQL block. The following constructs item_obj and item_obj_st instances by leveraging our get_item function, and it assigns the result from the get_item function to a collection of the generalized item_obj class. Then, the anonymous block reads through the collection in a range FOR loop.
SQL> DECLARE 2 /* Declare a generalized object type. */ 3 lv_item_tab ITEM_TAB := item_tab(); 4 BEGIN 5 /* Assign an object to a generalization. */ 6 lv_item_tab.EXTEND; 7 lv_item_tab(lv_item_tab.COUNT) := 8 get_item( id => 2 9 , title => 'The Hobbit' 10 , subtitle => 'The Desolation of Smaug' 11 , rating => 'PG-13' 12 , rating_agency => 'MPAA' 13 , release_date => '13-DEC-2013'); 14 15 /* Assign an object to a specialization. */ 16 lv_item_tab.EXTEND; 17 lv_item_tab(lv_item_tab.COUNT) := 18 get_item( id => 3 19 , title => 'The Hobbit' 20 , subtitle => 'The Battle of the Five Armies' 21 , rating => 'PG-13' 22 , rating_agency => 'MPAA' 23 , release_date => '17-DEC-2014' 24 , bluray => 'Sony Blu-ray'); 25 26 /* Print header line. */ 27 dbms_output.put_line( 28 '---------------------------------------------'); 29 30 /* Print items in collection. */ 31 FOR i IN 1..lv_item_tab.COUNT LOOP 32 /* Print the contents of the item_obj. */ 33 parse_rows(lv_item_tab(i).to_string()); 34 dbms_output.put_line( 35 '---------------------------------------------'); 36 END LOOP; 37 END; 38 / |
For reference, the parse_rows procedure is in the “How to write a Java Class with the SQLData Interface” blog post.
Lines 8 through 14 assign an item_obj instance to the lv_item_tab collection, and lines 18 through 26 assign an item_obj_st instance to the lv_item_tab collection. Line 35 uses the parse_rows procedure.
It should print:
--------------------------------------------- ID # [2] Title [The Hobbit: The Desolation of Smaug] Rating [MPAA:PG-13] Release [2013-12-13] --------------------------------------------- ID # [3] Title [The Hobbit: The Battle of the Five Armies] Rating [MPAA:PG-13] Release [2014-12-17] Blu-ray [Sony Blu-ray] --------------------------------------------- |
The first element prints the item_obj output, and the second element prints the item_obj_st output. Please take note that Oracle determined which to run based on their base object type, and you didn’t need to write any specialized instanceOf logic.
This collection of articles should have enabled you to learn the necessary skills to implement Java classes inside the Oracle Database. The SQLData Interface affords the ability to read and write to the database and those methods may be replaced by other more direct methods using the JDBC standard approaches.