First Steps with the Java Class Library
To build your first Java Btrieve application:
1
2
3
4
5
6
7
8
9
Verify your Environment is Correct
You should make sure your environment is set up correctly as described in How to Set Up your Environment.
In addition, make sure that “psql.jar” is in your CLASSPATH environment variable. The installation procedure should have performed this step for you. To access the classes in the PSQL Java interface class library, you must import the package in your Java source files.
import PSQL.database.*;
Create your Database and Tables
Currently, the PSQL Java interface class library does not support the creation of databases and database tables. Use the PSQL Control Center utility to perform these tasks. Once the database Data Dictionary Files (DDFs) and data files have been created, you can use the Java API classes to populate and access the tables.
For more information on the PSQL Control Center and creating tables, see PSQL User's Guide.
Connect to a PSQL Database
There are two options for connecting to a PSQL database. Either connect directly to the engine or connect using the I*net Data Server.
To connect to a database using a URI
1
Session session = Driver.establishSession();
2
Database db = session.connectToDatabase(“btrv:///demodata”);
or
Database db = session.connectToDatabase(“btrv://user@host/demodata?pwd=password”);
See Advanced Operations Guide for more information on database security and URIs.
Obtain the Tables and Create Rowsets
Table table = db.getTable(“MyTable”);
where “MyTable” is the name of the database table. The table name is case sensitive.
You can also get a list of the database’s table names.
String [] names = db.getTableNames();
Once you have a Table object, you can obtain information about the columns and indexes from its TableMetaData object.
TableMetaData tmd = table.getTableMetaData();
Some examples:
// Get the number of columns.
int num_columns = tmd.getColumnCount();
// Get the data type for column 0
int data_type = tmd.getColumnDef(0).getType());
// Get the length in characters for the column 0
int col_length = tmd.getColumnDef(0).getOffset();
To access a Table’s rows, you need a RowSet object which can only be created by a Table object.
RowSet rowset = table.createRowSet();
You can create multiple RowSet objects from the same table. RowSet objects are used to retrieve a table’s rows, insert new rows, and delete and update existing rows. To iterate through all the table’s rows, create a RowSet object and call getNext() until an PsqlEOFException occurs.
try {
while(true)
Row row = rowset.getNext();
}
catch(PsqlEOFException ex) {
// No more rows
}
Once you have a RowSet object, you can obtain information about the RowSet from its RowSetMetaData.
RowSetMetaData rsmd = rowset.getRowSetMetaData();
Consult the methods of the RowSetMetaData class in PSQL Java Class Library Reference for more details.
Navigate the Rows in a Row Set
As previously shown, you can iterate through the row set’s rows by repeatedly calling the “getNext” method. You can iterate backwards by changing the “direction” property of the row set’s RowSetMetaData object.
rsmd.setDirection(Const.BTR_BACKWARDS);
After setting this property, the behavior of the “getNext” method becomes “getPrevious.”
*Note: You can “reset” the currency of the row set at any time to be before the first or after the last row (as shown in the following procedures). This is an efficient way to retrieve the first row or last row without having to iterate through the entire row set.
1
rsmd.setDirection(Const.BTR_FORWARDS);
2
rowset.reset();
3
Row first = rowset.getNext();
1
rsmd.setDirection(Const.BTR_BACKWARDS);
2
rowset.reset();
3
Row last = rowset.getNext();
Row objects inherit a multitude of accessor/mutator methods from the Buffer class. These methods allow you to set/get column data to/from a Row object’s buffer.
1
String str = row.getString(0);
or
String str = row.getString(“ColumnName”);
where “ColumnName” is the name defined in the data dictionary for column 0. Column names are case sensitive.
2
row.setString(0, “MyColumnData”);
or
row.setString(“ColumnName”, “MyColumnData”);
3
Consult the methods of the Buffer class in PSQL Java Interface Reference for more details.
Restrict or Filter the Data
To restrict/filter the rows in a row set, you have to use the RowSetMetaData’s “addFirstTerm,” “addOrTerm,” and “addAndTerm.” For example, if the first column of a table contains integer data and you want all the rows where the first column’s value is greater that 25, do the following.
1
RowSetMetaData rsmd = rowset.getRowSetMetaData();
2
ColumnDef cmd = rsmd.getColumnDef(0);
3
rowset.reset();
4
rowset.addFirstTerm(cmd, Const.BTR_GR, “23”);
5
Row row = rowset.getNext();
You can add additional terms with the RowSetMetaData’s “addOrTerm” and “addAndTerm.” These methods allow you to build up more complex filtering conditions similar to a SQL “WHERE” clause.
You can specify a subset of the columns to retrieve, similar to the SQL “select” statement, by using the RowSetMetaData methods, “addColumns” and “dropColumns.” By default, a row set will retrieve all the column data unless the row set was created with the “noColumns” parameter set to “true.”
RowSet rowset = table.createRowSet(true);
In this case, no columns will be retrieved. Alternatively, after creating the row set, you can drop all or some of the columns.
RowSetMetaData rsmd =rowset.getRowSetMetaData();
rsmd.dropAllColumns();
Now you can add the set of columns you’re interested in to the row set.
rsmd.addColumns(“LastName”, “FirstName”);
You can add columns to the row set by column names or by column numbers. See the various “addColumns” and “dropColumns” methods of the “RowSetMetaData” class in the PSQL Java Interface Reference documentation for more details.
If you access column data in the rows by column number, be aware that the column numbers will be affected by the new ordering produced by “addColumns” and “dropColumns” methods. For example, if “FirstName” was originally column number 3 in the row set, after dropping all the columns and then adding “LastName” and “FirstName,” the “FirstName” column number will be 1.
You can retrieve a row by using a defined index along with a comparison operator. For instance, if an index, “Last_Name” has been created on the “LastName” column for a hypothetical table, you can do the following to find a row with a “LastName” equal to “Smith.”
1
rsmd.setAccessPath(“Last_Name”);
2
Index index = rowset.createIndex(“Last_Name”);
3
index.setString(“LastName”, “Smith”);
4
try {
Row row = rowset.getByIndex(Const.BTR_EQ, index);
}
5
catch(PsqlOperationException ex) {
 
// If the error code == 4, then no row with LastName
// == Smith exists. This could be considered
// normal operation.
// If the status code is not 4, the operation failed
// for some other reason than “not found.”
 
if (ex.getErrorCode() != 4)
throw(ex);
}
If the index allows duplicate key values, the first row which satisfies the comparison operator will be returned. The returned row becomes the current row, allowing you to retrieve the next logical row, based on the index, with the row set’s “getNext” method.
You can get the list of index names from the table’s TableMetaData object.
String [] index_names = table.getTableMetaData().getIndexNames();
Be aware that an index is not required to have a name. In that case, you should use the index number instead. Index numbers are zero based.
Insert, Update, or Delete Rows
1
Row row = rowset.createRow();
2
// Set column 0 data
row.setString(0, “Column0String”);
// Set column 1 data
row.setInt(1, 45);
// Update column 2 data
row.setDouble(2, 99.99);
3
rowset.insertRow(row);
This newly inserted row becomes the current row of the row set. If you do not want the row set’s currency to be changed by the insertion, you can use the overloaded version of “insertRow” that takes a boolean argument indicating no currency change(NCC).
rowset.insertRow(row, true);
The row set’s current row will be unchanged.
1
Row row = rowset.getNext();
2
// Update column 1
row.setInt(1, 45);
3
rowset.updateRow(row);
Like “insertRow,” the newly updated row becomes the current row of the row set unless “no currency changed” is indicated, as shown following.
rowset.updateRow(row, true);
1
Row row = rowset.getNext();
2
rowset.deleteRow(row);
The row to be deleted does not have to be the “current” row i.e. the row returned from the last retrieval operation. If it is not the current row, the “deleteRow” method will make the deleted row the “current” row before deleting it. After the deletion, a call to “getNext” will return the row following the deleted row.
Combine Operations into a Transaction
Transactions allow you to combine a series of operations into a single operation that will either be committed or aborted.
You can make a set of operations a transaction by using the transaction methods of the Session class.
Here is an example of a transaction:
1
try {
session.startTransaction(BTR_CONCURRENT_TRANS);
2
// insert one or more operations here
3
session.commitTransaction(); }
catch(PsqlException ex)
4
{ // An error occurred.
session.abortTransaction(); }
Transactions can be “exclusive” or “concurrent.” See the PSQL Programmer's Guide for more information about transactions.
Store and Retrieve Binary Large Objects
The setObject() and getObject() methods can be used to store and retrieve Java objects that implement the java.io.Serializable interface. The setBinaryStream() and getBinaryStream() methods can be used to store and retrieve binary data using Java InputStreams. If we have a "simple" object Employees with the following interface:
public class Employee implements java.io.Serializable
public int getID(); //Gets the Employee ID
public void setID(int ID); //Sets the Employee ID
public String getName(); //Gets the Employee Name
public void setName(String name); //Sets Employee Name
public String getManagerName(); //Gets the Manager Name
public void setManagerName(); //Sets the Manager Name
...and we have a file that we wish to read at C:\Employees\Java Duke\report.txt, we can instantiate an Employee object, set its state with the mutator methods, and store it into the database in the Employee_Data column as well as storing the file in the Manager_Report column:
// Already performed usual setup
// (Driver.establishSession, and so forth)
// and instantiated an Employee object
// named employeeObject.
employeeObject.setName("Java Duke");
employeeObject.setID(123456789);
employeeObject.setManagerName("Big Boss");
FileInputStream managerReport = null;
try
{
managerReport = new FileInputStream(C:\Employees\Java Duke\report.txt);
}
catch(IOException ioe)
{
//Handle the exception.
}
 
//Set the column values for the row,
// assuming that a RowSet object
//(rowset) has already been created.
 
Row employeeRow = rowset.createRow();
 
//Set the ID column of the database.
employeeRow.setInt("ID", employeeObject.getID());
 
//Set the Employee object into the row.
try
{
employeeRow.setObject("Employee_Data", employeeObject);
}
catch(PsqlIOException pioe)
{
//Handle the exception.
}
 
//Set the manager's report into the row.
employeeRow.setBinaryStream("Manager_Report", managerReport);
 
//Insert the row.
rowset.insertRow(employeeRow);
 
//Now we can retrieve this row from the database
 
RowSetMetaData rsmd = rowset.getRowSetMetaData();
ColumnDef cdef = rsmd.getColumnDef("ID");
rsmd.addFirstTerm(cdef, Const.BTR_EQ, "123456789");
Row rowRetrieved = rowset.getNext();
 
//After the row is retrieved, we can
// perform the getObject and
// getBinaryStream methods on the row in
// order to retrieve the desired data.
 
try
{
Employee employeeRetrieved =
(Employee)rowRetrieved.getObject("Employee_Data");
}
catch(PsqlException pe) //This method throws both
// PsqlIOException
{ //and PsqlClassNotFoundException
//Handle the exception.
}
 
InputStream reportRetrieved =
rowRetrieved.getBinaryStream("Manager_Report");
 
// These objects have now been reconstituted.
// You can invoke the methods that have been
// defined for either the object itself or
// its parents as you normally would.
 
String managerName = employeeRetrieved.getManagerName();
 
// You probably wouldn't normally want
// to process the entire file.
// in one chunk, but you could
// if you have the resources.
byte file[] = new byte[reportRetrieved.available()];
reportRetrieved.read(file);
 
For more information, see Binary Large Object Support.