Using Java with Caché
Using the Java Binding
[Home] [Back] [Next]
InterSystems: The power behind what matters   
Class Reference   
Search:    

This chapter provides detailed examples of how to use the Caché Java binding within a Java application.

Generating Java Proxy Classes
To create a Java projection of a Caché class, specify that the Caché class automatically creates a Java class whenever it is compiled. To do this, add a projection definition to the Caché class. This projection definition specifies that the Class Compiler also generates Java code for this class whenever the class is compiled.
The process is as follows:
You can now compile the Java class using either the javac command or some other tool. Whenever you modify and compile the Caché, its projection automatically updates the projected Java class.
Important:
You should never modify the generated Java Projection code directly, or attempt to add custom Java connection code, since this can have serious unintended consequences. The generated code depends on InterSystems internal code that may be changed without notice.
Generating a Java Class from a Caché Class
To use a Caché class from a Java client, generate a Java class to run on the client side.
You can specify that a Caché class automatically creates a Java class whenever it is compiled; to do this, add a projection definition to the Caché class. This projection definition specifies that the Class Compiler also generates Java code for this class whenever the class is compiled. For more information, see the Projections chapter in Using Studio.
To generate a Java class, do the following:
Using Objects
A Caché Java binding application can be quite simple. Here is a complete sample program:
import com.intersys.objects.*;
public class TinyBind {
  public static void main( String[] args ) {
    try {
// Connect to the Cache' database
      String url="jdbc:Cache://localhost:1972/SAMPLES";
      String username="_SYSTEM";
      String password="SYS";
      Database dbconnection = 
        CacheDatabase.getDatabase (url, username, password);

// Create and use a Cache' object
      Sample.Person person = new Sample.Person( dbconnection );
      person.setName("Doe, Joe A");
      System.out.println( "Name: " + person.getName() );
    }

// Handle exceptions
    catch (CacheException ex) {
      System.out.println( "Caught exception: " +
        ex.getClass().getName() + ": " + ex.getMessage() );
    }
  }
}
This code performs the following actions:
The following sections discuss these basic actions in more detail.
Creating a Connection Object
The CacheDatabase class is a Java class that manages a connection to a specific Caché server and namespace. It has a static method, getDatabase(), for creating a connection. This method returns a connection to a Caché database that is derived from the Database interface.
To establish a connection:
The getDatabase() method establishes a TCP/IP connection from the Java client to the Caché server. The method takes three arguments, where the first includes a specified IP address (here, the local host, 127.0.0.1), a specified port number (here, 1972), and to a specified namespace (here, SAMPLES); the second and third arguments are the username and password, respectively, for logging into the server.
It returns a Database object, here called dbconnection. You can then use getDatabase() to establish the connection:
   dbconnection = CacheDatabase.getDatabase(url, username, password);
Note:
It is also possible to create a connection and run queries through the Java-standard JDBC connection interface. For details, see Using DriverManager to Connect in Using Caché with JDBC.
Creating and Opening Proxy Objects
The following code attempts to connect to the local Caché server:
   String url="jdbc:Cache://localhost:1972/SAMPLES";
   String username="_SYSTEM";
   String password="sys";
   //...
   dbconnection = CacheDatabase.getDatabase (url, username, password);
Next, the program uses standard Java functionality to prompt the user for an ID to open and to get that value. Once there is an ID, the next step is to open the specified object:
   person = (Sample.Person)Sample.Person._open(dbconnection, new Id(strID));
This code invokes the _open() method of the Person object in the Sample package. This method takes two arguments: the database that contains the object being opened, and the ID of the object being opened. The value being returned is narrowed (cast) as an instance of Sample.Person because _open() is inherited from the Persistent class and, hence, returns an instance of that class.
Using Methods and Properties
Once the object is open, the program displays the value of the object's Name property.
   System.out.println("Name: " + person.getName());
Note that, unlike on the Caché server, references to object properties are through the get() and set() methods, rather than through direct references to the properties themselves.
Embedded Objects
Next, it displays the value of the City property and then gives the City property a new value:
   System.out.println("City: " + person.getHome().getCity());
   person.getHome().setCity("Ulan Bator");
The lines of code that manipulate the City property demonstrate the observation and modification of the properties of an embedded object. If a property is an object (such as the Home property), then it has its own properties (such as the City property) with accessor methods. You can invoke these methods using cascading dot syntax.
Saving and Closing
Having given the City property a new value, the application then saves the object, displays the value, closes the object, de-assigns it, and then shuts itself down.
   person._save();

   // Report the new residence of this person */
   System.out.println( "New City: " + person.getHome().getCity());

   // * de-assign the person object */
   dbconnection.closeObject(person.getOref());
   person = null;

   // Close the connection
   dbconnection.close();
Note:
Always Close Objects and Connections
Before closing a connection, it is important to call closeObject() on all objects that use the connection. Failure to do so may compromise the integrity of your data on the server. Objects are opened with a default concurrency value of 1, meaning that a read lock is acquired if the class uses more than one data node (see Object Concurrency in Using Caché Objects).
You must also call close() on all connection instances before they go out of scope. Failure to do so can cause memory leaks and other problems.
A Sample Java Binding Application
SampleApplication.java is a simple Java program that connects to the Caché SAMPLES database, opens and modifies an instance of a Sample.Person object saved within the database, and executes a predefined query against the database. This application is invoked from the operating command line, reads an object id value from the command line, and writes output using the Java system.out object. This example assumes that Caché and Java are running on a Windows machine.
SampleApplication.java is located in the <cachesys>/dev/Java/Samples directory (see Default Caché Installation Directory in the Caché Installation Guide for the location of <cachesys> on your system). In the Samples directory, compile the program:
   javac SampleApplication.java
And then run it:
   java SampleApplication
When executed, this program yields results such as:
   C:\java> java SampleApplication
   Enter ID of Sample.Person object to be opened: 1
   Name: Isaacs,Sophia R.
   City: Tampa
   New City: Ulan Bator

   C:\java>
Here is the complete Java source for the sample application:
/*
* SampleApplication.java
*/
import java.io.*;
import java.util.*;
import com.intersys.objects.*;

public class SampleApplication {
  public static void main(String[] args){
    Database dbconnection = null;
    String url="jdbc:Cache://localhost:1972/SAMPLES";
    String username="_SYSTEM";
    String password="sys";
    ObjectServerInfo info = null;
    Sample.Person person = null;

  try {
    // Connect to Cache on the local machine, in the SAMPLES namespace
    dbconnection = CacheDatabase.getDatabase (url, username, password);

    // Open an instance of Sample.Person,
    // whose ID is read in from the console
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    System.out.print("Enter ID of Person object to be opened:");
    String strID = br.readLine();

    // Use the entered strID as an Id and use that Id to
    // to open a Person object with the _open() method inherited
    // from the Persistent class. Since the _open() method returns
    // an instance of Persistent, narrow it to a Person by casting.
    person = (Sample.Person)Sample.Person._open(dbconnection, new Id(strID));

    // Fetch some properties of this object
    System.out.println("Name: " + person.getName());
    System.out.println("City: " + person.getHome().getCity());

    // Modify some properties
    person.getHome().setCity("Ulan Bator");

    // Save the object to the database
    person._save();

    // Report the new residence of this person */
    System.out.println( "New City: " + person.getHome().getCity());

    /* de-assign the person object */
    dbconnection.closeObject(person.getOref());
    person = null;

    // Close the connection
    dbconnection.close();

    } catch (Exception ex) {
    System.out.println("Caught exception: "
      + ex.getClass().getName()
      + ": " + ex.getMessage());
    }
  }
}
Using Streams
Caché allows you to create properties known as streams that hold large sequences of characters, either in character or binary format. Character streams are long sequences of text, such as the contents of a free-form text field in a data entry screen. Binary streams are usually image or sound files, and are akin to BLOBs (binary large objects) in other database systems.
The process for using a stream in Java is:
The Caché stream classes are GlobalBinaryStream and GlobalCharacterStream in the com.intersys.classes package. The basic methods involving streams are:
_write()
Adding content
_writeLine()
Adding content (character streams only)
_read()
Reading content
_readLine()
Reading content (character streams only)
_moveToEnd()
Going to the stream's end
_rewind()
Going to the stream's beginning
atEnd()
Check if the current position is at the end of the stream
_sizeGet()
Getting the size of the stream
isNull()
Checking if the stream has content or not
_clear()
Erasing the stream's content
Using Queries
A Caché query is designed to fit into the framework of JDBC but provides a higher level of abstraction by hiding direct JDBC calls behind a simple and complete interface of a dynamic query. It has methods for preparing an SQL statement, binding parameters, executing the query, and traversing the result set.
Class Queries
Caché allows you to define queries as part of a class. These queries are then compiled and can be invoked at runtime.
To invoke a predefined query, use the CacheQuery class: