Foxhound is the better* Database Monitor for SQL Anywhere.
*better: More thorough, more relevant, more effective.
...more Alerts, more All Clears, more details, more control in your hands.


[Home] [Back to Tip 81] [Forward to Tip 83] [Archives]
Breck Carter
Last modified: November 26, 1998
mail to: bcarter@bcarter.com



Tip 82: Java In The Database (2)
Nested Object Columns

Question: Can a Java object with nested Java objects be stored in a table?

Yes, it can. Figure 1 shows the HelloWorld class from Tip 81 - Getting Started, with some simplifications, and Figure 2 shows another class NestedWorld that contains an object of type HelloWorld.

Figure 1: Simplified HelloWorld.java From Tip 81
public class HelloWorld {
  
  public String hello ;
  public String world ;
  public java.lang.Integer counter ;

  HelloWorld () {                // Constructor 1
    hello = "Hello";
    world = "World";
    counter = new java.lang.Integer ( 1 );
  }

  HelloWorld ( String inHello,   // Constructor 2
               String inWorld,
               int inCounter
             ) {
    hello = inHello;
    world = inWorld;
    counter = new java.lang.Integer ( inCounter );
  }

  public String toString() {
    return hello + " " + world + ": " + counter.toString();
  }

  public int compareTo( HelloWorld anotherHelloWorld ) {

    // Compare first on the basis of counter
    // and then on the basis of toString()

    java.lang.Integer lVal = counter;
    java.lang.Integer rVal = anotherHelloWorld.counter;

    if ( lVal.intValue() > rVal.intValue() ) {
      return 1;
    }
    else if (lVal.intValue() < rVal.intValue() ) {
      return -1;
    }
    else {
      return toString().compareTo( anotherHelloWorld.toString() );
    }
  }
}

Figure 2: NestedWorld.java Contains Object of Type HelloWorld
public class NestedWorld {
  
  public HelloWorld nestedHelloWorld ;
  public java.lang.Integer outerCounter ;

  NestedWorld () {                         // Constructor 1
    nestedHelloWorld = new HelloWorld();
    outerCounter = new java.lang.Integer ( 999 );
  }

  NestedWorld ( String inHello,            // Constructor 2
                String inWorld,
                int inCounter,
                int inOuterCounter
              ) {
    nestedHelloWorld = new HelloWorld ( inHello, inWorld, inCounter );
    outerCounter     = new java.lang.Integer ( inOuterCounter );
  }

  NestedWorld ( HelloWorld inHelloWorld,   // Constructor 3
                int inOuterCounter
              ) {
    nestedHelloWorld = inHelloWorld;
    outerCounter     = new java.lang.Integer ( inOuterCounter );
  }

  public String toString() {
    return nestedHelloWorld.toString() 
           + ": " + outerCounter.toString();
  }

  public int compareTo( NestedWorld anotherNestedWorld ) {

    // Compare first on the basis of counter
    // and then on the basis of toString()

    java.lang.Integer lVal = outerCounter;
    java.lang.Integer rVal = anotherNestedWorld.outerCounter;

    if ( lVal.intValue() > rVal.intValue() ) {
      return 1;
    }
    else if (lVal.intValue() < rVal.intValue() ) {
      return -1;
    }
    else {
      return toString().compareTo( anotherNestedWorld.toString() );
    }
  }
}

Of particular interest in Figure 2 are the three constructors for NestedWorld. The first one simply calls the default constructor for HelloWorld to initialize nestedHelloWorld, and then fills in outerCounter with a default value.

The second constructor for NestedWorld expects values for all 4 base fields. It passes the first 3 parameters on to the second constructor for HelloWorld, and uses the 4th parameter to fill in outerCounter.

The third constructor for NestedWorld expects only 2 parameters but the first one is of type HelloWorld. The caller is responsible for creating an object of type HelloWorld and passing it to this constructor which then uses it to initialize nestedHelloWorld. The other parameter is used to fill in outerCounter.

Figure 3 shows how the two classes are loaded into the database and used to create a table called java_nested_world.

(Note: Simplified batch files for compiling classes and launching DBSRV6 and DBISQL are shown in the Appendix.)

Figure 3: Install and Use NestedWorld.class
INSTALL JAVA NEW FROM FILE 
   'c:\\amisc\\ASAJava\\HelloWorld.class';

INSTALL JAVA NEW FROM FILE 
   'c:\\amisc\\ASAJava\\NestedWorld.class';

CREATE TABLE java_nested_world
   ( pkey              INTEGER NOT NULL,
     JNestedWorld      NestedWorld NOT NULL,
     PRIMARY KEY ( pkey ) );

/* Reverse-order removal:
DROP TABLE java_nested_world;
REMOVE JAVA CLASS NestedWorld;
REMOVE JAVA CLASS HelloWorld;
*/

Figure 4 shows how the three different constructors are invoked. The first INSERT uses the first constructor to fill in the defaults, and the second INSERT uses the constructor that expects all 4 base fields to be passed to it.

The other INSERTs use the third constructor for NestedWorld, with a nested call to the second constructor for HelloWorld as the first argument. Support for nested objects is provided all the way up to the SQL level, proving once again that the folks at Watcom have their act together.

Figure 4: Insert and Select java_nested_world
INSERT INTO java_nested_world ( pkey, JNestedWorld )
   VALUES ( 1, NEW NestedWorld() );

INSERT INTO java_nested_world ( pkey, JNestedWorld )
   VALUES ( 2, NEW NestedWorld ( 'Hello', 'World', 0, 55 ) );

INSERT INTO java_nested_world ( pkey, JNestedWorld )
   VALUES ( 3, 
            NEW NestedWorld 
             ( NEW HelloWorld ( 'Hello', 'World', 2 ),
               66 ) );

INSERT INTO java_nested_world ( pkey, JNestedWorld )
   VALUES ( 4, 
            NEW NestedWorld 
             ( NEW HelloWorld ( 'Good', 'Bye', 0 ),
               77 ) );

INSERT INTO java_nested_world ( pkey, JNestedWorld )
   VALUES ( 5, 
            NEW NestedWorld 
             ( NEW HelloWorld ( 'Good', 'Bye', 2 ),
               88 ) );

SELECT *
  FROM java_nested_world 
 ORDER BY pkey;

Figure 5 shows what a simple SELECT * looks like when the classes implement nested toString methods. Again, this works the way you would expect it to.

Figure 5: Insert and Select via DBISQL Figure 5: Insert and Select via DBISQL


Appendix: Handy Batch Files

Three handy batch files were set up to launch the Java compiler, ASA server and DBISQL without worrying about directories, paths, etc. Here's how they are run via Windows 95 Start - Run:
     c:\amisc\ASAJava\run_javac NestedWorld
     c:\amisc\ASAJava\run_dbsrv6 ASAJava
     c:\amisc\ASAJava\run_dbisql
Figures 6, 7 and 8 show the contents of the batch files.

Figure 6: run_javac.bat
rem Start - Run Format: c:\amisc\ASAJava\run_javac [ClassName]

rem Define shortcuts for long paths:
SET ASA=E:\Program Files\Sybase\Adaptive Server Anywhere 6.0\java
SET JDK=e:\jdk1.1.6\bin
SET DEV=c:\amisc\ASAJava

rem Move to the development directory:
c:
cd "%DEV%"

rem Set the CLASSPATH for the compiler to use:
SET CLASSPATH=.;%ASA%;%ASA%\jdbcdrv.zip;%ASA%\asajdbc.zip

rem Compile %1.java into %1.class:
%JDK%\javac.exe -d %DEV% %1.java 

Figure 7: run_dbsrv6.bat
rem Start - Run Format: c:\amisc\ASAJava\run_dbsrv6 [DBFileName]

rem Define shortcuts for long paths:
SET ASA=E:\Program Files\Sybase\Adaptive Server Anywhere 6.0\win32
SET DEV=c:\amisc\ASAJava

rem Move to the development directory:
c:
cd "%DEV%"

rem Start %1.db via DBSRV6:
"%ASA%\dbsrv6.exe" -x NONE -c 12m -gp 4096 %DEV%\%1.db

Figure 8: run_dbisql.bat
rem Start - Run Format: c:\amisc\ASAJava\run_dbisql

rem Define shortcuts for long paths:
SET ASA=E:\Program Files\Sybase\Adaptive Server Anywhere 6.0\win32
SET DEV=c:\amisc\ASAJava

rem Move to the development directory:
c:
cd "%DEV%"

rem Launch DBISQL:
"%ASA%\dbisql.exe" -c "UID=dba;PWD=sql"


[Home] [Back to Tip 81] [Forward to Tip 83] [Archives] [mail to: bcarter@bcarter.com]