Application Development Guide

Embedding SQL Statements in Java

Static SQL statements in SQLJ appear in SQLJ clauses. SQLJ clauses are the mechanism by which SQL statements in Java programs are communicated to the database.

The SQLJ translator recognizes SQLJ clauses and SQL statements because of their structure, as follows:

The simplest SQLJ clauses are executable clauses and consist of the token #sql followed by an SQL statement enclosed in braces. For example, the following SQLJ clause may appear wherever a Java statement may legally appear. Its purpose is to delete all rows in the table named TAB:

   #sql { DELETE FROM TAB };

In an SQLJ executable clause, the tokens that appear inside the braces are SQL tokens, except for the host variables. All host variables are distinguished by the colon character so the translator can identify them. SQL tokens never occur outside the braces of an SQLJ executable clause. For example, the following Java method inserts its arguments into an SQL table. The method body consists of an SQLJ executable clause containing the host variables x, y, and z:

   void m (int x, String y, float z) throws SQLException 
   {
       #sql { INSERT INTO TAB1 VALUES (:x, :y, :z) };
   }

In general, SQL tokens are case insensitive (except for identifiers delimited by double quotation marks), and can be written in upper, lower, or mixed case. Java tokens, however, are case sensitive. For clarity in examples, case insensitive SQL tokens are uppercase, and Java tokens are lowercase or mixed case. Throughout this chapter, the lowercase null is used to represent the Java "null" value, and the uppercase NULL to represent the SQL null value.

Declaring Iterator Behavior in SQLJ

Unlike SQL statements that retrieve data from a table, applications that perform positioned UPDATE and DELETE operations, or that use iterators with holdability or returnability attributes, require two Java source files. Declare the iterator as public in one source file, appending the with and implements clause as appropriate.

To set the value of the holdability or returnability attribute, you must declare the iterator using the with clause for the corresponding attribute. The following example sets the holdability attribute to true for the iterator WithHoldCurs:

   #sql public iterator WithHoldCurs with (holdability=true) (String EmpNo);

Iterators that perform positioned updates require an implements clause that implements the sqlj.runtime.ForUpdate interface. For example, suppose that you declare iterator DelByName like this in file1.sqlj:

   #sql public iterator DelByName implements sqlj.runtime.ForUpdate(String EmpNo);

You can then use the translated and compiled iterator in a different source file. To use the iterator:

  1. Declare an instance of the generated iterator class
  2. Assign the SELECT statement for the positioned UPDATE or DELETE to the iterator instance
  3. Execute positioned UPDATE or DELETE statements using the iterator

To use DelByName for a positioned DELETE in file2.sqlj, execute statements like those in Deleting Rows Using a Positioned Iterator.

   {
      DelByName deliter; // Declare object of DelByName class
      String enum;
(1)   #sql deliter = { SELECT EMPNO FROM EMP WHERE WORKDEPT='D11'};
      while (deliter.next())
      {
(2)      enum = deliter.EmpNo(); // Get value from result table
(3)      #sql { DELETE WHERE CURRENT OF :deliter };
         // Delete row where cursor is positioned
      }
   }

Notes:

  1. (1)This SQLJ clause executes the SELECT statement, constructs an iterator object that contains the result table for the SELECT statement, and assigns the iterator object to variable deliter.

  2. (2)This statement positions the iterator to the next row to be deleted.

  3. (3)This SQLJ clause performs the positioned DELETE.

SQLJ Example: App.sqlj

The following example SQLJ application, App.sqlj , uses static SQL to retrieve and update data from the EMPLOYEE table of the DB2 sample database.

  1. Declare iterators. This section declares two types of iterators:

    App_Cursor1
    Declares column data types and names, and returns the values of the columns according to column name (Named binding to columns).

    App_Cursor2
    Declares column data types, and returns the values of the columns by column position (Positional binding to columns).
  2. Initialize the iterator. The iterator object cursor1 is initialized using the result of a query. The query stores the result in cursor1.
  3. Advance the iterator to the next row. The cursor1.next() method returns a Boolean false if there are no more rows to retrieve.
  4. Move the data. The named accessor method empno() returns the value of the column named empno on the current row. The named accessor method firstnme() returns the value of the column named firstnme on the current row.
  5. SELECT data into a host variable. The SELECT statement passes the number of rows in the table into the host variable count1.
  6. Initialize the iterator. The iterator object cursor2 is initialized using the result of a query. The query stores the result in cursor2.
  7. Retrieve the data. The FETCH statement returns the current value of the first column declared in the ByPos cursor from the result table into the host variable str2.
  8. Check the success of a FETCH..INTO statement. The endFetch() method returns a Boolean true if the iterator is not positioned on a row, that is, if the last attempt to fetch a row failed. The endFetch() method returns false if the last attempt to fetch a row was successful. DB2 attempts to fetch a row when the next() method is called. A FETCH...INTO statement implicitly calls the next() method.
  9. Close the iterators. The close() method releases any resources held by the iterators. You should explicitly close iterators to ensure that system resources are released in a timely fashion.

JDBC Example: App.sqlj
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
 
#sql iterator App_Cursor1 (String empno, String firstnme) ; (1)
#sql iterator App_Cursor2 (String) ;
 
class App 
{
 
  /**********************
   **  Register Driver **
   **********************/
 
 static
 {
   try
   {
     Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
   }
   catch (Exception e)
   {
     e.printStackTrace();
   }
 }
 
  /********************
   **      Main      **
   ********************/
  
 public static void main(String argv[])
 {
   try
   {
     App_Cursor1 cursor1; 
     App_Cursor2 cursor2; 
 
     String str1 = null;
     String str2 = null;
     long   count1;
 
     // URL is jdbc:db2:dbname
     String url = "jdbc:db2:sample";
 
     DefaultContext ctx = DefaultContext.getDefaultContext();
     if (ctx == null)
     {
       try
       {
         // connect with default id/password
         Connection con = DriverManager.getConnection(url);
         con.setAutoCommit(false);
         ctx = new DefaultContext(con);
       }
       catch (SQLException e)
       {
         System.out.println("Error: could not get a default context");
         System.err.println(e) ;
         System.exit(1);
       }
       DefaultContext.setDefaultContext(ctx);
     }
 
     // retrieve data from the database
     System.out.println("Retrieve some data from the database.");
     #sql cursor1 = {SELECT empno, firstnme FROM employee}; (2)
 
     // display the result set
     // cursor1.next() returns false when there are no more rows
     System.out.println("Received results:");
     while (cursor1.next()) (3)
     {
       str1 = cursor1.empno(); (4)
       str2 = cursor1.firstnme();
 
       System.out.print (" empno= " + str1);
       System.out.print (" firstname= " + str2);
       System.out.print ("");
     }
     cursor1.close(); (9)
 
     // retrieve number of employee from the database
     #sql { SELECT count(*) into :count1 FROM employee }; (5)
     if (1 == count1)
       System.out.println ("There is 1 row in employee table");
     else
       System.out.println ("There are " + count1
                            + " rows in employee table");
 
     // update the database
     System.out.println("Update the database. ");
     #sql { UPDATE employee SET firstnme = 'SHILI' WHERE empno = '000010' };
 
     // retrieve the updated data from the database
     System.out.println("Retrieve the updated data from the database.");
     str1 = "000010";
     #sql cursor2 = {SELECT firstnme FROM employee WHERE empno = :str1}; (6)
 
     // display the result set
     // cursor2.next() returns false when there are no more rows
     System.out.println("Received results:");
     while (true)
     {
       #sql { FETCH :cursor2 INTO :str2 }; (7)
       if (cursor2.endFetch()) break; (8)
 
       System.out.print (" empno= " + str1);
       System.out.print (" firstname= " + str2);
       System.out.print ("");
     }
     cursor2.close(); (9)
 
     // rollback the update
     System.out.println("Rollback the update.");
     #sql { ROLLBACK work };
     System.out.println("Rollback done.");
   }
   catch( Exception e )
   {
     e.printStackTrace();
   }
 }
}

Host Variables in Java

Arguments to embedded SQL statements are passed through host variables, which are variables of the host language that appear in the SQL statement. Host variables have up to three parts:

The evaluation of a Java identifier does not have side effects in a Java program, so it may appear multiple times in the Java code generated to replace an SQLJ clause.

The following query contains the host variable, :x, which is the Java variable, field, or parameter x visible in the scope containing the query:

   SELECT  COL1,  COL2  FROM  TABLE1  WHERE  :x  > COL3

All host variables specified in compound SQL are input host variables by default. You have to specify the parameter mode identifier OUT or INOUT before the host variable in order to mark it as an output host variable. For example:

   #sql {begin compound atomic static                                 
        select count(*) into :OUT count1 from employee;            
        end compound}

Calls to Stored Procedures and Functions in SQLJ

Databases may contain stored procedures, user-defined functions, and user-defined methods. Stored procedures, user-defined functions, and user-defined methods are named schema objects that execute in the database. An SQLJ executable clause appearing as a Java statement may call a stored procedure by means of a CALL statement like the following:

       #sql { CALL SOME_PROC(:INOUT myarg) };

Stored procedures may have IN, OUT, or INOUT parameters. In the above case, the value of host variable myarg is changed by the execution of that clause. An SQLJ executable clause may call a function by means of the SQL VALUES construct. For example, assume a function F that returns an integer. The following example illustrates a call to that function that then assigns its result to Java local variable x:

       {
           int x;
           #sql x = { VALUES( F(34) ) };
       }

Compiling and Running SQLJ Programs

To run an SQLJ program with program name MyClass, do the following:

  1. Translate the Java source code with Embedded SQL to generate the Java source code MyClass.java and profiles MyClass_SJProfile0.ser, MyClass_SJProfile1.ser, ... (one profile for each connection context):
       sqlj MyClass.sqlj
    

    When you use the SQLJ translator without specifying an sqlj.properties file, the translator uses the following values:

                                                                
       sqlj.url=jdbc:db2:sample                                         
       sqlj.driver=COM.ibm.db2.jdbc.app.DB2Driver                       
       sqlj.online=sqlj.semantics.JdbcChecker                           
       sqlj.offline=sqlj.semantics.OfflineChecker                       
    

    If you do specify an sqlj.properties file, make sure the following options are set:

       sqlj.url=jdbc:db2:dbname
       sqlj.driver=COM.ibm.db2.jdbc.app.DB2Driver
       sqlj.online=sqlj.semantics.JdbcChecker
       sqlj.offline=sqlj.semantics.OfflineChecker
    

    where dbname is the name of the database. You can also specify these options on the command line. For example, to specify the database mydata when translating MyClass, you can issue the following command:

       sqlj -url=jdbc:db2:mydata MyClass.sqlj
    

    Note that the SQLJ translator automatically compiles the translated source code into class files, unless you explicitly turn off the compile option with the -compile=false clause.

  2. Install DB2 SQLJ Customizers on generated profiles and create the DB2 packages in the DB2 database dbname:
       db2profc -user=user-name -password=user-password -url=jdbc:db2:dbname
         -prepoptions="bindfile using MyClass0.bnd package using MyClass0"
         MyClass_SJProfile0.ser
       db2profc -user=user-name -password=user-password -url=jdbc:db2:dbname
         -prepoptions="bindfile using MyClass1.bnd package using MyClass1"
         MyClass_SJProfile1.ser
       ...
    
  3. Execute the SQLJ program:
       java MyClass
    

The translator generates the SQL syntax for the database for which the SQLJ profile is customized. For example,

   i = { VALUES (  F(:x) ) };

is translated by the SQLJ translator and stored as

   ? = VALUES (F (?))

in the generated profile. When connecting to a DB2 Universal Database database, DB2 will customize the VALUE statement into:

                 
   VALUES(F(?)) INTO ?

but when connecting to a DB2 Universal Database for OS/390 database, DB2 customizes the VALUE statement into:

   SELECT F(?) INTO ? FROM SYSIBM.SYSDUMMY1

If you run the DB2 SQLJ profile customizer, db2profc, against a DB2 Universal Database database and generate a bind file, you cannot use that bind file to bind up to a DB2 for OS/390 database when there is a VALUES clause in the bind file. This also applies to generating a bind file against a DB2 for OS/390 database and trying to bind with it to a DB2 Universal Database database.

For detailed information on building and running DB2 SQLJ programs, refer to the Application Building Guide.

SQLJ Translator Options

The SQLJ translator supports the same precompile options as the DB2 PRECOMPILE command, with the following exceptions:

   CONNECT 
   DISCONNECT 
   DYNAMICRULES
   NOLINEMACRO 
   OPTLEVEL
   OUTPUT
   SQLCA 
   SQLFLAG
   SQLRULES
   SYNCPOINT 
   TARGET 
   WCHARTYPE 

To print the content of the profiles generated by the SQLJ translator in plain text, use the profp utility as follows:

   profp MyClass_SJProfile0.ser
   profp MyClass_SJProfile1.ser
     ...

To print the content of the DB2 customized version of the profile in plain text, use the db2profp utility as follows, where dbname is the name of the database:

   db2profp -user=user-name -password=user-password -url=jdbc:db2:dbname
            MyClass_SJProfile0.ser
   db2profp -user=user-name -password=user-password -url=jdbc:db2:dbname
            MyClass_SJProfile1.ser
       ...


[ Top of Page | Previous Page | Next Page ]