Chapter 2 Programming Information


Implementing advanced features

This section describes how to use advanced jConnect features and contains the following topics:

Using event notification

You can use the jConnect event notification feature to have your application notified when an Open Server procedure is executed.

To use this feature, you must use the SybConnection class, which extends the Connection interface. SybConnection contains a regWatch( ) method for turning event notification on and a regNoWatch( ) method for turning event notification off.

Your application must also implement the SybEventHandler interface. This interface contains one public method, void event(String proc_name, ResultSet params), which is called when the specified event occurs. The parameters of the event are passed to event( ) and it tells the application how to respond.

To use event notification in your application, call SybConnection.regWatch( ) to register your application in the notification list of a registered procedure. Use this syntax:

SybConnection.regWatch(proc_name,eventHdlr,option)

Whenever an event with the designated proc_name occurs on the Open Server, jConnect calls eventHdlr.event( ) from a separate thread. The event parameters are passed to eventHdlr.event( ) when it is executed. Because it is a separate thread, event notification does not block execution of the application.

If proc_name is not a registered procedure, or if Open Server cannot add the client to the notification list, the call to regWatch( ) throws a SQL exception.

To turn off event notification, use this call:

SybConnection.regNoWatch(proc_name)

Note   When you use Sybase event notification extensions, the application needs to call the close( ) method on the connection to remove a child thread created by the first call to regWatch( ). Failing to do so may cause the Virtual Machine to hang when exiting the application.

Event notification example

The following example shows how to implement an event handler and then register an event with an instance of your event handler, once you have a connection:

 public  class MyEventHandler implements SybEventHandler
 {
 	// Declare  fields and constructors, as needed.
 	...
 	public  MyEventHandler(String eventname)
 	{
 		...
 	}
 
 	// Implement  SybEventHandler.event.
 	public void event(String  eventName, ResultSet params)
 	{
 		try
 		{
 			// Check  for error messages received prior to event
 			// notification.
 			SQLWarning  sqlw = params.getWarnings();
 			if  sqlw != null
 			{
 				// process  errors, if any
 				...
 			}
 			// process  params as you would any result set with
 			// one  row.
 			ResultSetMetaData rsmd = params.getMetaData();
 			int  numColumns = rsmd.getColumnCount();
 			while  (params.next())          // optional
 			{
 				for  (int i = 1; i <= numColumns; i++)
 				{
 					System.out.println(rsmd.getColumnName(i) + " =
 						" + params.getString(i));
 				}
 				// Take  appropriate action on the event. For example,
 				// perhaps  notify application thread.
 				...
 			}
 		}
 		catch  (SQLException sqe)
 		{
 			// process  errors, if any
 			...
 		}
 	}
 }
 
 public  class MyProgram
 {
 	...
 	// Get  a connection and register an event with an instance
 	// of  MyEventHandler.
 	Connection conn = DriverManager.getConnection(...); 
 	MyEventHandler  myHdlr = new  MyEventHandler("MY_EVENT");
 
 	// Register  your event handler.
 	((SybConnection)conn).regWatch("MY_EVENT",  myHdlr,
 		SybEventHandler.NOTIFY_ALWAYS);
 	...
	conn.regNoWatch("MY_EVENT");
 	conn.close();
 }

Handling error messages

jConnect provides two classes for returning Sybase-specific error information, SybSQLException and SybSQLWarning, as well as a SybMessageHandler interface that allows you to customize the way jConnect handles error messages received from the server.

Retrieving Sybase-specific error information

jConnect provides an EedInfo interface that specifies methods for obtaining Sybase-specific error information. The EedInfo interface is implemented in SybSQLException and SybSQLWarning, which extend the SQLException and SQLWarning classes.

SybSQLException and SybSQLWarning contain the following methods:

Some error messages may be SQLException or SQLWarning messages, without being SybSQLException or SybSQLWarning messages. Your application should check the type of exception it is handling before it downcasts to SybSQLException or SybSQLWarning.

Customizing error-message handling

You can use the SybMessageHandler interface to customize the way jConnect handles error messages generated by the server. Implementing SybMessageHandler in your own class for handling error messages can provide the following benefits:

Note   Error-message handlers implementing the SybMessageHandler interface only receive server-generated messages. They do not handle messages generated by jConnect.

When jConnect receives an error message, it checks to see if a SybMessageHandler class has been registered for handling the message. If so, jConnect invokes the messageHandler( ) method. The messageHandler( ) method accepts a SQL exception as its argument, and jConnect processes the message based on what value is returned from messageHandler( ). The error-message handler can:

Installing an error-message handler

You can install an error-message handler implementing SybMessageHandler by calling the setMessageHandler( ) method from SybDriver, SybConnection, or SybStatement. If you install an error-message handler from SybDriver, all subsequent SybConnection objects inherit it. If you install an error-message handler from a SybConnection object, it is inherited by all SybStatement objects created by that SybConnection.

This hierarchy only applies from the time the error-message handler object is installed. For example, if you create a SybConnection object, myConnection, and then call SybDriver.setMessageHandler( ) to install an error-message handler object, myConnection cannot use that object.

To return the current error-message handler object, use
getMessageHandler( )
.

Error-message-handler example

The following example uses jConnect version 4.1.

import java.io.*;
 import java.sql.*;
 import  com.sybase.jdbcx.SybMessageHandler;
 import com.sybase.jdbcx.SybConnection;
 import  com.sybase.jdbcx.SybStatement;
 import java.util.*;
 
 public  class MyApp
 {
   static SybConnection  conn = null;
   static SybStatement stmt = null
    static ResultSet rs = null;
   static String  user = "guest";
   static String password = "sybase";
    static String server = "jdbc:sybase:Tds:192.138.151.39:4444";
    static final int AVOID_SQLE = 20001;
 
    public MyApp()
   {
      try
       {
         			Class.forName("com.sybase.jdbc.SybDriver").newInstance;
          			Properties props = new  Properties();
         			props.put("user",  user);
         			props.put("password",  password);
 			conn = (SybConnection) 
 			DriverManager.getConnection(server,  props);
 			conn.setMessageHandler(new  NoResultSetHandler());
 			stmt =(SybStatement)  conn.createStatement();
 			stmt.executeUpdate("raiserror  20001 'your error'");
 	
 		for  (SQLWarning sqw = _stmt.getWarnings();
 			sqw  != null;
 			sqw = sqw.getNextWarning());
 		{
 			if  (sqw.getErrorCode() == AVOID_SQLE);
 			{
 				System.out.println("Error" +	sqw.getErrorCode()+ 
 					"  was found in the Statement's warning list.");
 				break;
 			}
 		}
 		stmt.close();
 		conn.close();
 	}
 	catch(Exception  e)
 	{
 		System.out.println(e.getMessage());
 		e.printStackTrace();
 	}
 }
 
 class  NoResultSetHandler implements SybMessageHandler
 {
 	public  SQLException messageHandler(SQLException sqe)
 	{
 		int  code = sqe.getErrorCode();
 		if  (code == AVOID_SQLE)
 		{
 			System.out.println("User  " + _user + " downgrading " +
 				AVOID_SQLE + "  to a warning");
 			sqe = new  SQLWarning(sqe.getMessage(),
 				sqe.getSQLState(),sqe.getErrorCode());
 		}
 		return  sqe;
 	}
 }
 
 public  static void main(String args[])
 {
 	new  MyApp();
 }

Storing Java objects as column data in a table

Some database products enable you to directly store Java objects as column data in a database. In such databases, Java classes are treated as datatypes, and you can declare a column with a Java class as its datatype.

jConnect supports storing Java objects in a database by implementing the setObject( ) methods defined in the PreparedStatement interface and the getObject( ) methods defined in the CallableStatement and ResultSet interfaces. This allows you to use jConnect with an application that uses native JDBC classes and methods to directly store and retrieve Java objects as column data.

Note   To use getObject( ) and setObject( ), set the jConnect version to VERSION_4 or later. See "Setting the jConnect version".

The following sections describe the requirements and procedures for storing objects in a table and retrieving them using JDBC with jConnect:

Note   Adaptive Server Enterprise version 12.0 and later and Adaptive Server Anywhere version 6.0.x and later can store Java objects in a table, with some limitations. See the jConnect for JDBC Release Bulletin for more information.

Prerequisites for storing java objects as column data

To store Java objects belonging to a user-defined Java class in a column, three requirements must be met:

Sending Java objects to a database

To send an instance of a user-defined class as column data, use one of the following setObject( ) methods, as specified in the PreparedStatement interface:

void setObject(int parameterIndex, Object x, int  targetSqlType, 
 	int scale) throws SQLException;
void setObject(int parameterIndex, Object x, int  targetSqlType) 
 	throws SQLException;
void setObject(int parameterIndex, Object x) throws  SQLException;

In jConnect 4.5, you can also use PreparedStatement.setObject (int parameterIndex , Object x , Types.OTHER) method to pass a Java object to the database. The object must still implement the java.io.Serializable interface.In jConnect 5.5, you can use the Types.OTHER target sqlType to send a Java object, or you can use Types.JAVA_OBJECT (available only in jConnect 5.x).

The following example defines an Address class, shows the definition of a Friends table that has an Address column whose datatype is the Address class, and inserts a row into the table.

public class  Address implements Serializable
{
	public String	 streetNumber;
 	public  String	 street;
 	public String	 apartmentNumber;
 	public  String	 city;
 	public int	 zipCode;
	//Methods
	...
}

/* This code assumes a table  with the following structure
**	Create  table Friends:
**	(firstname  varchar(30)	, 
** 	lastname  varchar(30), 
**	address Address, 
**	phone  varchar(15))
*/

// Connect to the database containing  the Friends table.
Connection conn = 
 	DriverManager.getConnection("jdbc:sybase:Tds:localhost:5000", 
 		"username",  "password");
 
// Create a Prepared  Statement object with an insert statement 
//for  updating the Friends table.
PreparedStatement ps = conn.prepareStatement("INSERT  INTO 
 	Friends values (?,?,?,?)");
 
// Now,  set the values in the prepared statement object, ps.
// set  firstname to "Joan."
ps.setString(1, "Joan");
 
// Set  last name to "Smith."
ps.setString(2, "Smith");
 
// Assuming  that we already have "Joan_address" as an instance
// of  Address, use setObject(int parameterIndex, Object x) to 
// set  the address column to "Joan_address."
ps.setObject(3,  Joan_address);
 
// Set  the phone column to Joan's phone number.
ps.setString(4,  "123-456-7890");
 
// Perform  the insert.
ps.executeUpdate();

Receiving Java objects from the database

A client JDBC application can receive a Java object from the database in a result set or as the value of an output parameter returned from a stored procedure. If a result set contains a Java object as column data, use one of the following getObject( ) methods in the ResultSet interface to retrieve the object:

Object getObject(int  columnIndex) throws SQLException;
Object  getObject(String columnName) throws SQLException;
If an output parameter from a stored procedure contains a Java object, use the following getObject( ) method in the CallableStatement interface to retrieve the object:
Object getObject(int  parameterIndex) throws SQLException;

The following example illustrates the use of
ResultSet.getObject(int parameterIndex) to assign an object received in a result set to a class variable. The example uses the Address class and Friends table used in the previous section and presents a simple application that prints a name and address on an envelope.

/*
 ** This  application takes a first and last name, gets the 
 ** specified  person's address from the Friends table in the 
 ** database,  and addresses an envelope using the name and
 ** retrieved  address.
 */
 public class Envelope
 {
 	Connection  conn = null;
 	String firstName = null;
 	String  lastName = null;
 	String street = null;
 	String  city = null;
 	String zip = null;
 
 	public  static void main(String[] args)
 	{
 		if  (args.length < 2)
 		{
 		System.out.println("Usage:  Envelope <firstName> 
 			<lastName>");
 		System.exit(1);
 		}
 		// create  a 4" x 10" envelope
 		Envelope e = new  Envelope(4, 10);
 		try
 		{
 			// connect  to the database with the Friends table.
 			conn = DriverManager.getConnection(
 				"jdbc:sybase:Tds:localhost:5000",  "username", 
 					"password");
 			// look  up the address of the specified person
 			firstName = args[0];
 			lastName = args[1];
 			PreparedStatement  ps = conn.prepareStatement(
 				"SELECT  address FROM friends WHERE " +
 					"firstname = ?  AND lastname = ?");
 			ps.setString(1,  firstName);
 			ps.setString(2,  lastName);
 			ResultSet rs = ps.executeQuery();
 			if  (rs.next())
 			{
 				Address  a = (Address) rs.getObject(1);
 				// set  the destination address on the envelope
 				e.setAddress(firstName,  lastName, a);
 			}
 			conn.close();
 		}
 		catch  (SQLException sqe)
 		{
 			sqe.printStackTrace();
 			System.exit(2);
 		}
 		// if  everything was successful, print the envelope
 		e.print();
 	}
 	private  void setAddress(String fname, String lname, Address a)
 	{
 		street = a.streetNumber + "  " + a.street + " " +
 			a.apartmentNumber;
 		city = a.city;
 		zip = "" + a.zipCode;
 	}
 	private  void print()
 	{
 		// Print  the name and address on the envelope.
 		...
 	}
 }

You can find a more detailed example of HandleObject.java in the sample (jConnect 4.x) and sample2 (jConnect 5.x) subdirectories under your jConnect directory.

Dynamic class loading

Adaptive Server Anywhere version 6.0 and Adaptive Server Enterprise version 12.0 and later allow you to specify Java classes as:

In earlier versions, only classes that appeared in jConnect's CLASSPATH were accessible; that is, if a jConnect application attempted to access an instance of a class that was not in the local CLASSPATH, a java.lang.ClassNotFound exception would result.

jConnect version 5.2 implements DynamicClassLoader (DCL) to load a class directly from an Adaptive Server Anywhere or Adaptive Server Enterprise server and use it as if it was present in the local CLASSPATH.

All security features present in the superclass are inherited. The loader delegation model implemented in Java 2 is followed--first jConnect attempts to load a requested class from the CLASSPATH; if that fails, jConnect tries the DynamicClassLoader.

See Java in Adaptive Server Enterprise for more detailed information about use Java and Adaptive Server.

Using DynamicClassLoader

To use DCL functionality:

  1. Create and configure a class loader. Your jConnect application's code should look similar to this:

    Properties props = new Properties();// URL  of the server where the classes live.
    String classesUrl = "jdbc:sybase:Tds:myase:1200"; // Connection  properties for connecting to above server.
    props.put("user",  "grinch");
    props.put("password", "meanone");
    ... // Ask  the SybDriver for a new class loader.
    DynamicClassLoader  loader = driver.getClassLoader(classesUrl, props);
    
  2. Use the CLASS_LOADER connection property to make the new class loader available to the statement that executes the query. Once you create the class loader, pass it to subsequent connections as shown below (continuing from the code example in step 1).

    // Stash the class  loader so that other connection(s)
    // can  know about it.
    props.put("CLASS_LOADER", loader);// Additional  connection properties
    props.put("user", "joeuser");
    props.put("password",  "joespassword");// URL of the server we now want  to connect to.
    String url = "jdbc:sybase:Tds:jdbc.sybase.com:4446";// Make  a connection and go.
    Connection conn = DriverManager.getConnection(url,  props);
    
    Assuming the following Java class definition:
    class  Addr {
    			String street;
    			String  city;
    			String state;
    }
    


    and the following SQL table definition:

    create table employee (char(100)  name, int empid, Addr address)
    
  3. Use the following client-side code in the absence of an Addr class in the client application's CLASSPATH:

    Statement stmnt = conn.createStatement();
    
    // Retrieve some rows from the  table that has a Java class
    
    // as  one of its fields.
    
    ResultSet  rs = stmnt.executeQuery(
    
    			"select * from  employee where empid = '19'");
    
    if (rs.next() {
    
    			// Even  though the class is not in our class path,
    
    			// we  should be able to access its instance.
    
    			Object obj = rs.getObject("address");
    
    			// The  class has been loaded from the server,
    
    			// so  let's take a look.
    
    			Class  c = obj.getClass();
    


    		// Some  Java Reflection can be done here
    
    			// to  access the fields of obj.
    
    			...
    


    }
    

The CLASS_LOADER connection property provides a convenient mechanism for sharing one class loader among several connections.

You should ensure that sharing a class loader across connections does not result in class conflicts. For example, if two different, incompatible instances of class org.foo.Bar exist in two different databases, problems can arise if you use the same loader to access both classes. The first class is loaded when examining a result set from the first connection. When it is time to examine a result set from the second connection, the class is already loaded. The second class is never loaded, and there is no direct way for jConnect to detect this situation.

However, Java has a built-in mechanism for ensuring that the version of a class matches the version information in a deserialized object. The above situation is at least detected and reported by Java.

Classes and their instances do not need to reside in the same database or server, but there is no reason why both the loader and subsequent connections cannot refer to the same database/server.

Deserialization

The following example illustrates how to deserialize an object from a local file. The serialized object is an instance of a class that resides on a server and does not exist in the CLASSPATH.

SybResultSet.getObject( ) makes use of DynamicObjectInputStream, which is a subclass of ObjectInputStream that loads a class definition from DynamicClassLoader, rather than the default system ("boot") class loader.

// Make a stream  on the file containing the 
//serialized  object.
FileInputStream fileStream = new  FileInputStream("serFile");
// Make  a "deserializer" on it. Notice that, apart 
//from the additional parameter,  this is the same 
//as  ObjectInputStreamDynamicObjectInputStream 
stream = new DynamicObjectInputStream(fileStream,  loader);
// As  the object is deserialized, its class is 
//retrieved via the loader from  our server.
Object obj = stream.readObject();stream.close();

Preloading JARS

jConnect version 5.2 includes a new connection property called PRELOAD_JARS. When defined as a comma-delimited list of JAR file names, the JAR files are loaded in their entirety. In this context, "JAR" refers to the "retained JARname" used by the server. This is the JAR name specified in the install Java program, for example:

install java new jar 'myJarName' from  file '/tmp/mystuff.jar'

If you set PRELOAD_JARS, the JAR files are associated with the class loader, so it is unnecessary to preload them with every connection. You should only specify PRELOAD_JARS for one connection. Subsequent attempts to preload the same JAR files may result in performance problems as the JAR data is retrieved from the server unnecessarily.

Note   Adaptive Server Anywhere 6.x and later cannot return an JAR file as one entity, so jConnect iteratively retrieves each class in turn. However, Adaptive Server 12.x and later retrieves the entire JAR and loads each class that it contains.

Advanced features

There are various public methods in DynamicClassLoader. For more information, see the javadocs information in JDBC_HOME/docs/en/javadocs.

Additional features include the ability to keep a loader's database connection "alive" when a series of class loads is expected, and to explicitly load a single class by name.Public methods inherited from java.lang.ClassLoader can also be used. Methods in java.lang.Class that deal with loading classes are also available; however, use these methods with caution since some of them make assumptions about which class loader gets used. In particular, you should use the 3-argument version of Class.forName(), otherwise the system ("boot") class loader will be used. See "Handling error messages".

JDBC 2.0 optional package extensions support

The JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API) defines several new features that may be implemented by JDBC 2.0 drivers. jConnect version 5.2 has implemented the following optional package extension features:

The above features require classes and/or interfaces that are not found in standard Java 2 distributions. You must download javax.sql.* and javax.naming.* to implement

Databases and Connection Pooling, and you must download javax.transaction.xa.* to implement Distributed Transaction Management Support.

Note   Sybase recommends that you use JNDI 1.2, which is compatible with Java 1.1.6 and later.

JNDI for naming databases

Reference

The JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API), Chapter 5, "JNDI and the JDBC API."

Related interfaces

This feature provides JDBC clients with an alternative to the standard approach for obtaining database connections. Instead of invoking Class.forName ("com.sybase.jdbc2.jdbc.SybDriver"), then passing a JDBC URL to the DriverManager's getConnection( ) method, clients can access a JNDI name server using a logical name to retrieve a javax.sql.DataSource object. This object is responsible for loading the driver and establishing the connection to the physical database it represents. The client code is simpler and reusable because the vendor-specific information has been placed within the DataSource object.

The Sybase implementation of the DataSource object is com.sybase.jdbcx.SybDataSource (see the javadocs for details). This implementation supports the following standard properties using the design pattern for JavaBean components:

roleName is not supported.

jConnect provides an implementation of the javax.naming.spi.ObjectFactory interface so the DataSource object can be constructed from the attributes of a name server entry. When given a javax.naming.Reference, or a javax.naming.Name and a javax.naming.DirContext, this factory can construct com.sybase.jdbcx.SybDataSource objects. To use this factory, set the java.naming.object.factory system property to include com.sybase.jdbc2.SybObjectFactory.

Usage

You can use DataSource in different ways, in different applications. All options are discussed below with some code examples to guide you through the process. For more information, see the JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API), and the JNDI documentation on Sun's Web site.

1a. Configuration by administrator: LDAP

jConnect has supported LDAP connectivity since version 4.0. As a result, the recommended approach, which requires no custom software, is to configure DataSources as LDAP entries using the LDAP Data Interchange Format (LDIF). For example:

dn:servername:myASE, o=MyCompany,  c=US
1.3.6.1.4.1.897.4.2.5:TCP#1# mymachine  4000
1.3.6.1.4.1.897.4.2.10:PACKETSIZE=1024&user=me&password=secret
1.3.6.1.4.1.897.4.2.11:userdb

1b. Access by client

This is the typical JDBC client application. The only difference is that you access the name server to obtain a reference to a DataSource object, instead of accessing the DriverManager and providing a JDBC URL. Once you obtain the connection, the client code is identical to any other JDBC client code. The code is very generic and references Sybase only when setting the object factory property, which can be set as part of the environment.

The jConnect installation contains the sample program sample2/SimpleDataSource.java to illustrate the use of DataSource. This sample is provided for reference only; that is, you cannot run the sample unless you configure your environment and edit the sample appropriately. SimpleDataSource.java contains the following critical code:

import javax.naming.*;
import javax.sql.*;
import java.sql.*;

// set  necessary JNDI properties for your environment (same as above)
Properties jndiProps = new Properties();

// used  by JNDI to build the SybDataSource
jndiProps.put(Context.OBJECT_FACTORIES,
		"com.sybase.jdbc2.jdbc.SybObjectFactory");

// nameserver  that JNDI should talk to
jndiProps.put(Context.PROVIDER_URL, 	"ldap:
//some_ldap_server:238/o=MyCompany,c=Us");

// used  by JNDI to establish the naming context
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,
		"com.sun.jndi.ldap.LdapCtxFactory");

// obtain  a connection to your name server
Context  ctx = new InitialContext(jndiProps);
DataSource ds = (DataSource) ctx.lookup("servername=myASE");

// obtains  a connection to the server as configured earlier.
// in this case, the default  username and password will be used
Connection  conn = ds.getConnection();

// do standard JDBC methods
...

Explicitly passing the Properties to the InitialContext constructor is not required if the properties have already been defined within the virtual machine; that is, passed when Java was either set as part of the browser properties, or by using:

java -Djava.naming.object.factory=com.sybase.jdbc2.jdbc.SybObjectFactory

See your Java VM documentation for more information about setting environment properties.

2a. Configuration by administrator: custom

This phase is typically done by the person who does database system administration or application integration for their company. The purpose is to define a data source, then deploy it under a logical name to a name server. If the server needs to be reconfigured (for example, moved to another machine, port, and so on), then the administrator runs this configuration utility (outlined below) and reassigns the logical name to the new data source configuration. As a result, the client code does not change, since it knows only the logical name.

import javax.sql.*;
import com.sybase.jdbcx.*;
.....

// create a SybDataSource, and  configure it
SybDataSource ds = new  com.sybase.jdbc2.jdbc.SybDataSource();
ds.setUser("my_username");
ds.setPassword("my_password");
ds.setDatabaseName("my_favorite_db");
ds.setServerName("db_machine");
ds.setPortNumber(4000);
ds.setDescription("This DataSource represents  the Adaptive Server
		Enterprise server  running on db_machine at port 2638.  The default
		username  and password have been set to 'me' and 'mine' respectively.   
		Upon connection, the user will access  the my_favorite_db database on 
		this  server.");
Properties props = newProperties()
props.put("REPEAT_READ","false");
props.put("REQUEST_HA_SESSION","true");
ds.setConnectionProperties(props);
// store the DataSource object.  Typically this is
// done by setting  JNDI properties specific to the
// type  of JNDI service provider you are using.
// Then,  initialize the context and bind the object.
Context ctx = new InitialContext();
ctx.bind("jcbc/myASE", ds);


Once you set up your DataSource, you decide where and how you want to store the information. To assist you, SybDataSource is both java.io.Serializable and javax.naming.Referenceable, but it is still up to the administrator to determine how the data is stored depending on what service provider you are using for JNDI.

2b. Access by client

The client retrieves the DataSource object by setting its JNDI properties the same way the DataSource was deployed. The client needs to have an object factory available that can transform the object as it is stored (for example, serialized) into a Java object.

Context ctx = new InitialContext();
DataSource  ds = (DataSource ctx.lookup("jcbc/myASE");

Connection pooling

Reference

The JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API), Chapter 6, "Connection Pooling."

Related interfaces

Overview

Traditional database applications create one connection to a database that you use for each session of an application. However, a Web-based database application may need to open and close a new connection several times during the application's use. An efficient way to handle Web-based database connections is to use connection pooling, which maintains open database connections and manages connection sharing across different user requests to maintain performance and to reduce the number of idle connections. On each connection request, the connection pool first determines if there is an idle connection in the pool. If there is, the connection pool returns that connection instead of making a new connection to the database.

Connection pooling capabilities are provided by ConnectionPoolDataSource. If you use this interface, you can pool connections. If you use the DataSource interface, you cannot pool connections.

When you use ConnectionPoolDataSource, pool implementations listen to the PooledConnection. The implementation is notified when a user closes the connection, or if the user has an error that destroys the connection. At this point, the pool implementation decides what to do with the PooledConnection.

Without connection pooling, a transaction:

  1. Creates a connection to the database.
  2. Sends the query to the database.
  3. Gets back the result set.
  4. Displays the result set.
  5. Destroys the connection.

With connection pooling, the sequence looks more like this:

  1. Sees if an unused connection exists in the "pool" of connections.
  2. If so, uses it; otherwise creates a new connection.
  3. Sends the query to the database.
  4. Gets back the result set.
  5. Displays the result set.
  6. Returns the connection to the "pool." The user still calls "close( )", but the connection remains open and the pool is notified of the close request.

It is less costly to reuse a connection than to create a new one every time a client needs to establish a connection to a database.

To enable a third party to implement the connection pool, the jConnect implementation has the ConnectionPoolDataSource interface produce PooledConnections, similar to how the DataSource interface produces Connections.The pool implementation creates "real" database connections, using the getPooledConnection( ) methods of ConnectionPoolDataSource. Then, the pool implementation registers itself as a listener to the PooledConnection. Currently, when a client requests a connection, the pool implementation invokes getConnection( ) on an available PooledConnection. When the client finishes with the connection and calls close(), the pool implementation is notified via the ConnectionEventListener interface that the connection is free and available for reuse.

The pool implementation is also notified via the ConnectionEventListener interface if the client somehow corrupts the database connection, so that the pool implementation can remove that connection from the pool.For more information, refer to Appendix B of the the JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API).

Configuration by administrator: LDAP

This approach is the same as 1a. Configuration by administrator: LDAP described in "JNDI for naming databases," except that you enter an additional line to your LDIF entry. In the following example, the added line of code is bolded for your reference.

dn:servername=myASE, o=MyCompany,  c=US
1.3.6.1.4.1.897.4.2.5:TCP#1# mymachine  4000
1.3.6.1.4.1.897.4.2.10:PACKETSIZE=1024&user=me&password=secret
1.3.6.1.4.1.897.4.2.11:userdb 
1.3.6.1.4.1.897.4.2.18:ConnectionPoolDataSource 

Access by middle-tier clients

This procedure initializes three properties (INITIAL_CONTEXT_FACTORY, PROVIDER_URL, and OBJECT_FACTORIES as shown on page 78), and retrieves a ConnectionPoolDataSource object. For a more complete code example, see sample2/SimpleConnectionPool.java. The fundamental difference is:

...
ConnectionPoolDatabase  cpds = (ConnectionPoolDataSource)
		ctx.lookup("servername=myASE");
PooledConnection  pconn = cpds.getPooledConnection();

Distributed transaction management support

This feature provides a standard Java API for performing distributed transactions with either Adaptive Server Enterprise version 12.x or version 11.x with XA-Server.

Note   This feature is designed for use in a large multitier environment.

Reference

See Chapter 7, "Distributed Transactions," in the JDBC 2.0 Optional Package (formerly the JDBC 2.0 Standard Extension API).

Related interfaces

Background and system requirements

For Adaptive Server Enterprise 12.0 and later

Figure 2-2: Distributed transaction management
support with version 12.x

For Adaptive Server Enterprise 11.x

jConnect also provides a standard Java API for performing distributed transactions with Adaptive Server Enterprise version 11.x as your database server.

Adaptive Server Enterprise 12.x use

Configuration by administrator: LDAP

This approach is the same as 1a. Configuration by administrator: LDAPdescribed in "JNDI for naming databases", except that you enter an additional line to the LDIF entry. In the following example, the added line of code is displayed in bold.

dn:servername:myASE, o=MyCompany,  c=US
1.3.6.1.4.1.897.4.2.5:TCP#1# mymachine  4000
1.3.6.1.4.1.897.4.2.10:PACKETSIZE=1024&user=me&password=secret
1.3.6.1.4.1.897.4.2.11:userdb 
1.3.6.1.4.1.897.4.2.18:XADataSource 

Access by middle-tier clients

This procedure initializes three properties (INITIAL_CONTEXT_FACTORY, PROVIDER_URL, and OBJECT_FACTORIES), and retrieves a XADataSource object. For example:

...
XADataSource xads = (XADatasource)  ctx.lookup("servername=myASE");
XAConnection xaconn = xads.getXAConnection();

or override the default settings for the user name and password:

...
XADataSource xads = (XADatasource)  ctx.lookup("servername=myASE");
XAConnection xaconn = xads.getXAConnection("my_username","my_password");

Adaptive Server Enterprise 11.x use

Configuration by administrator: LDAP

This approach is the same as 1a. Configuration by administrator: LDAPdescribed in "JNDI for naming databases", except that you enter an additional three lines to the LDIF entry.

In the following example, the additional code lines are displayed in bold .

dn:servername:myASE, o=MyCompany,  c=US
1.3.6.1.4.1.897.4.2.5:TCP#1# mymachine  4000
1.3.6.1.4.1.897.4.2.10:PACKETSIZE=1024&user=me&password=secret
1.3.6.1.4.1.897.4.2.11:userdb
1.3.6.1.4.1.897.4.2.16:userconnection
1.3.6.1.4.1.897:4.2.17:1
1.3.6.1.4.1.897.4.2.18:XADataSource 

where . . .4.2.17:1 indicates that jConnect is going to connect to an XA-Server and userconnection corresponds to the Logical Resource Manager (LRM) to use. XA-Server has an xa_config file that contains these entries:

[xa]
lrm=userconnection
server=my_ase_11_server
XAServer=my_xa_server

Figure 2-4: Distributed transaction management support sample configuration

See the XA-Server documentation for details on how to write an xa_config file.

Access by middle-tier clients

This procedure initializes three properties (INITIAL_CONTEXT_FACTORY, PROVIDER_URL, and OBJECT_FACTORIES), and retrieves a XADataSource object. For example:

...
XADataSource xads = (XADatasource)  ctx.lookup("servername=myASE");
XAConnection xaconn = xads.getXAConnection();

With Adaptive Server Enterprise 11.x, you cannot override the default user name and password[Unmapped Entity: shy ] ; that is, you cannot call:

xads.getXAConnection("my_username","my_password");

because the lrm is associated with a specific user name and password.

 


Copyright © 2001 Sybase, Inc. All rights reserved.