EDB Postgres Advanced Server JDBC Connector Guide v42.2.5.6
EDB Postgres Advanced Server JDBC Connector Guide v42.2.5.6
Table of Contents
1 Introduction ................................................................................................................. 5
1.1 Typographical Conventions Used in this Guide ................................................. 6
2 Requirements Overview.............................................................................................. 7
2.1 Supported Server Versions ................................................................................. 7
2.2 Supported Platforms............................................................................................ 7
3 Advanced Server JDBC Connector Overview ............................................................ 8
3.1 JDBC Driver Types............................................................................................. 8
3.2 The JDBC Interface ............................................................................................ 9
3.3 JDBC Classes and Interfaces ............................................................................ 10
3.4 The JDBC DriverManager ................................................................................ 11
3.5 Advanced Server JDBC Connector Compatibility ........................................... 12
4 Installing and Configuring the JDBC Connector ...................................................... 14
4.1 Installing the JDBC Connector ......................................................................... 14
4.1.1 Installing the Connector with an RPM Package ........................................... 15
4.1.1.1 Updating an RPM Installation .............................................................. 17
4.1.2 Installing the Connector on an SLES 12 Host .............................................. 17
4.1.3 Installing a DEB Package on a Debian or Ubuntu Host ............................... 20
4.1.4 Using the Graphical Installer to Install the Connector.................................. 21
4.2 Configuring the Advanced Server JDBC Connector ........................................ 25
5 Using the Advanced Server JDBC Connector with Java applications ..................... 26
5.1 Loading the Advanced Server JDBC Connector .............................................. 27
5.2 Connecting to the Database .............................................................................. 28
5.2.1 Additional Connection Properties ................................................................. 29
5.2.2 Preferring Synchronous Secondary Database Servers .................................. 31
5.2.2.1 Configuring Master and Secondary Database Servers Overview ......... 32
5.2.2.2 Sample Master and Secondary Database Servers ................................. 33
5.3 Executing SQL Statements through Statement Objects.................................... 38
5.3.1 Using Named Notation with a CallableStatement Object............................. 39
5.4 Retrieving Results from a ResultSet Object ..................................................... 41
5.5 Freeing Resources ............................................................................................. 42
5.6 Handling Errors ................................................................................................. 43
1 Introduction
The JDBC connector provides connectivity between a Java application and an Advanced
Server database. This guide provides installation instructions, usage instructions, and
examples that demonstrate the Advanced Server specific functionality of the JDBC
Connector.
The JDBC connector is written in Java and conforms to Sun's JDK architecture, as
described in section 3.1.
The JDBC connector is built on and supports all of the functionality of the PostgreSQL
community driver. For more information about the features and functionality of the
driver, please refer to the community documentation at:
https://jdbc.postgresql.org/documentation/head/index.html
In the following descriptions a term refers to any word or group of words which may be
language keywords, user-supplied values, literals, etc. A term’s exact meaning depends
upon the context in which it is used.
Italic font introduces a new term, typically, in the sentence that defines it for the
first time.
Fixed-width (mono-spaced) font is used for terms that must be given
literally such as SQL commands, specific table and column names used in the
examples, programming language keywords, etc. For example, SELECT * FROM
emp;
Italic fixed-width font is used for terms for which the user must
substitute values in actual usage. For example, DELETE FROM table_name;
A vertical pipe | denotes a choice between the terms on either side of the pipe. A
vertical pipe is used to separate two or more alternative terms within square
brackets (optional choices) or braces (one mandatory choice).
Square brackets [ ] denote that one or none of the enclosed term(s) may be
substituted. For example, [ a | b ], means choose one of “a” or “b” or neither
of the two.
Braces {} denote that exactly one of the enclosed alternatives must be specified.
For example, { a | b }, means exactly one of “a” or “b” must be specified.
Ellipses ... denote that the proceeding term may be repeated. For example, [ a |
b ] ... means that you may have the sequence, “b a a b a”.
2 Requirements Overview
The following section details the supported platforms for the Advanced Server JDBC
Connector.
The Advanced Server JDBC Connector is certified with Advanced Server version 9.4 and
above.
64 bit Linux:
The Advanced Server JDBC Connector graphical installers are supported on the
following Windows platforms:
64-bit Windows:
32-bit Windows:
Windows 10
Windows 8
Windows 7
Type 1 Driver
This driver type is the JDBC-ODBC bridge.
It is limited to running locally.
Must have ODBC installed on computer.
Must have ODBC driver for specific database installed on computer.
Generally can’t run inside an applet because of Native Method calls.
Type 2 Driver
This is the native database library driver.
Uses Native Database library on computer to access database.
Generally can’t run inside an applet because of Native Method calls.
Must have database library installed on client.
Type 3 Driver
100% Java Driver, no native methods.
Does not require pre-installation on client.
Can be downloaded and configured on-the-fly just like any Java class file.
Uses a proprietary protocol for talking with a middleware server.
Middleware server converts from proprietary calls to DBMS specific calls
Type 4 Driver
100% Java Driver, no native methods.
Does not require pre-installation on client.
Can be downloaded and configured on-the-fly just like any Java class file.
Unlike Type III driver, talks directly with the DBMS server.
Converts JDBC calls directly to database specific calls.
Figure 2.3 depicts the role of the DriverManager class in a typical JDBC application.
The DriverManager acts as the bridge between a Java application and the backend
database and determines which JDBC driver to use for the target database.
To determine JDK/JVM compatibility the following list matches versions of the JVM
with the JDBC specification implemented.
Note:
1. JRE/JDK versions 1.4 and 1.5 are no longer supported by the Advanced Server
JDBC Connector as the JDBC driver file edb-jdbc15.jar is no longer
provided.
2. The edb-jdbc16.jar file is not available for Linux on PowerPC 64 little endian
(ppc64le).
3. Community version numbers are based on the pgjdbc version, not the PostgreSQL
version.
4. Advanced Server JDBC releases are decoupled with EDB Postgres Advanced
Server releases.
From Community website: “The PostgreSQL JDBC driver has some unique
properties that you should be aware of before starting to develop any code for it.
The current development driver supports six server versions and six java
environments. This doesn't mean that every feature must work in every
combination, but a reasonable behaviour must be provided for non-supported
versions. While this extra compatibility sounds like a lot of work, the actual goal
is to reduce the amount of work by maintaining only one code base.”
From readme on pgjdbc: “PgJDBC regression tests are run against all PostgreSQL
versions since 8.4, including “build PostgreSQL from git master” version. Don't
assume pgjdbc 9.4.x is only PostgreSQL 9.4 compatible.”
For the supported community versions, see the PostgreSQL JDBC Driver website located
at:
https://jdbc.postgresql.org/download.html#supported
Before installing the JDBC Connector, you must have Java installed on your system; you
can download a Java installer that matches your environment from the Oracle Java
Downloads website at:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
Detailed installation instructions are available through the associated Docs link on the
same page.
Please note that you may need to enable the [extras] repository definition in
the CentOS-Base.repo file (located in /etc/yum.repos.d).
You must also have credentials that allow access to the EnterpriseDB repository. For
information about requesting credentials, visit:
https://info.enterprisedb.com/rs/069-ALB-339/images/Repository%20Access%2004-09-
2019.pdf
To create the repository configuration file, assume superuser privileges and invoke the
following command:
After creating the edb.repo file, use your choice of editor to ensure that the value of the
enabled parameter is 1, and replace the username and password placeholders in
the baseurl specification with the name and password of a registered EnterpriseDB
user.
[edb]
name=EnterpriseDB RPMs $releasever - $basearch
baseurl=https://<username>:<password>@yum.enterprisedb.com/
edb/redhat/rhel-$releasever-$basearch
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/ENTERPRISEDB-GPG-KEY
After saving your changes to the configuration file, you can use the yum install
command to install JDBC Connector. For example, the following command installs
JDBC Connector:
When you install an RPM package that is signed by a source that is not recognized by
your system, yum may ask for your permission to import the key to your local server. If
prompted, and you are satisfied that the packages come from a trustworthy source, enter a
y, and press Return to continue.
During the installation, yum may encounter a dependency that it cannot resolve. If it
does, it will provide a list of the required dependencies that you must manually resolve.
yum will update the edb.repo file to enable access to the current EDB repository,
configured to connect with the credentials specified in your edb.repo file. Then, you
can use yum to upgrade any installed packages:
You can use the zypper package manager to install the connector on an SLES 12 host.
zypper will attempt to satisfy package dependencies as it installs a package, but requires
access to specific repositories that are not hosted at EnterpriseDB.
Before installing the connector, use the following commands to add EnterpriseDB
repository configuration files to your SLES host:
Edbas11suse.repo
edbasdependencies.repo
edbastools.repo
After creating the repository configuration files, use the zypper refresh command to
refresh the metadata on your SLES host to include the EnterpriseDB repositories:
When prompted for a User Name and Password, provide your connection credentials
for the EnterpriseDB repository. If you need credentials, contact EnterpriseDB at:
https://www.enterprisedb.com/repository-access-request
Before installing EDB Postgres Advanced Server or supporting components, you must
also add SUSEConnect and the SUSE Package Hub extension to the SLES host, and
register the host with SUSE, allowing access to SUSE repositories. Use the commands:
https://www.suse.com/support/kb/doc/?id=7016626
Then, you can use the zypper utility to install the connector:
To install a DEB package on a Debian or Ubuntu host, you must have credentials that
allow access to the EnterpriseDB repository. To request credentials for the repository,
visit:
https://www.enterprisedb.com/repository-access-request
The following steps will walk you through on using the EnterpriseDB apt repository to
install a DEB package. When using the commands, replace the username and
password with the credentials provided by EnterpriseDB.
sudo su –
sh -c 'echo "deb
https://username:[email protected]/$(lsb_release -
cs)-edb/ $(lsb_release -cs) main" >
/etc/apt/sources.list.d/edb-$(lsb_release -cs).list'
wget -q -O - https://username:password
@apt.enterprisedb.com/edb-deb.gpg.key | apt-key add -
apt-get update
Note: By default, the Debian 9x and Ubuntu 18.04 platform installs Java version 10.
Make sure you install Java version 8 on your system to run the EDB Java-based
components.
You can use the EnterpriseDB Connectors Installation wizard to add the JDBC connector
to your system; the wizard is available at:
https://www.enterprisedb.com/advanced-downloads
This section demonstrates using the Installation Wizard to install the Connectors on a
Windows system. (Download the installer, and then, right-click on the installer icon, and
select Run As Administrator from the context menu.)
When the Language Selection popup opens, select an installation language and click
OK to continue to the Setup window (shown in Figure 3.1).
Use the Installation Directory dialog (see Figure 3.2) to specify the directory in
which the connector will be installed, and click Next to continue.
Click Next on the Ready to Install dialog (see Figure 3.3) to start the installation;
popup dialogs confirm the progress of the installation wizard.
When the wizard informs you that it has completed the setup, click the Finish button to
exit the dialog (see Figure 3.4).
You can also use StackBuilder Plus to add or update the connector on an existing
Advanced Server installation; to open StackBuilder Plus, select StackBuilder Plus from
the Windows Apps menu or through Linux Applications menu (see Figure 3.5).
When StackBuilder Plus opens, follow the onscreen instructions. Select the
EnterpriseDB JDBC Connector option from the Database Drivers node of the
tree control (see Figure 3.6).
Follow the directions of the onscreen wizard to add or update an installation of the
EnterpriseDB Connectors.
Note: The edb-jdbc16.jar file is not available for Linux on PowerPC 64 little endian
(ppc64le).
To make the JDBC driver available to Java, you must either copy the appropriate java
.jar file for the JDBC version that you are using to your $java_home/jre/lib/ext
directory or append the location of the .jar file to the CLASSPATH environment
variable.
Note that if you choose to append the location of the jar file to the CLASSPATH
environment variable, you must include the complete pathname:
/usr/edb/jdbc/edb-jdbcxx.jar
Listing 1.1
import java.sql.*;
public class ListEmployees
{
public static void main(String[] args)
{
try
{
Class.forName("com.edb.Driver");
String url = "jdbc:edb://localhost:5444/edb";
String user = "enterprisedb";
String password = "enterprisedb";
Connection con = DriverManager.getConnection(url, user, password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM emp");
while(rs.next())
{
System.out.println(rs.getString(1));
}
rs.close();
stmt.close();
con.close();
System.out.println("Command successfully executed");
}
catch(ClassNotFoundException e)
{
System.out.println("Class Not Found : " + e.getMessage());
}
catch(SQLException exp)
{
System.out.println("SQL Exception: " + exp.getMessage());
System.out.println("SQL State: " + exp.getSQLState());
System.out.println("Vendor Error: " + exp.getErrorCode());
}
}
}
This example is simple, but it demonstrates the fundamental steps required to interact
with an Advanced Server database from a Java application:
Clean up
Handle any errors that may occur
Class.forName("com.edb.Driver");
After loading the bytecode for the driver, the driver registers itself with another JDBC
class (named DriverManager) that is responsible for managing all the JDBC drivers
installed on the current system.
If the JVM is unable to locate the named driver, it throws a ClassNotFound exception
(which is intercepted with a catch block near the end of the program). The
DriverManager is designed to handle multiple JDBC driver objects. You can write a
Java application that connects to more than one database system via JDBC. The next
section explains how to select a specific driver.
After the driver has loaded and registered itself with the DriverManager, the
ListEmployees class can attempt to connect to the database server, as shown in the
following code fragment:
All JDBC connections start with the DriverManager. The DriverManager class
offers a static method called getConnection() that is responsible for creating a
connection to the database. When you call the getConnection() method, the
DriverManager must decide which JDBC driver to use to connect to the database; that
decision is based on a URL (Uniform Resource Locator) that you pass to
getConnection().
jdbc:<driver>:<connection parameters>
The first component in a JDBC URL is always jdbc. When using the Advanced Server
JDBC Connector, the second component (the driver) is edb.
The Advanced Server JDBC URL takes one of the following forms:
jdbc:edb:<database>
jdbc:edb://<host>/<database>
jdbc:edb://<host>:<port>/<database>
In addition to the standard connection parameters, the Advanced Server JDBC driver
supports connection properties that control behavior specific to EnterpriseDB. You can
specify these properties in the connection URL or as a Properties object parameter
passed to DriverManager.getConnection(). Listing 1.2 demonstrates how to use
a Properties object to specify additional connection properties:
Listing 1.2
String url = "jdbc:edb://localhost/edb";
Properties props = new Properties();
props.setProperty("user", "enterprisedb");
props.setProperty("password", "enterprisedb");
props.setProperty("sslfactory", "com.edb.ssl.NonValidatingFactory");
props.setProperty("ssl", "true");
Note: By default the combination of SSL=true and setting the connection URL
parameter sslfactory=org.postgresql.ssl.NonValidatingFactory encrypts
the connection but does not validate the SSL certificate. To enforce certificate validation,
you must use a Custom SSLSocketFactory.
https://jdbc.postgresql.org/documentation/head/ssl-factory.html
To specify additional connection properties in the URL, add a question mark and an
ampersand-separated list of keyword-value pairs:
Some of the additional connection properties are shown in the following table:
The Advanced Server JDBC Connector supports option preferSyncSecondary for the
targetServerType connection property as listed in Table 5-2.
The specification of this capability in the connection URL is shown by the following
syntax:
jdbc:edb://master:port,secondary_1:port_1,secondary_2:port_2,.../
database?targetServerType=preferSyncSecondary
Parameters
master:port
The IP address or a name assigned to the master database server followed by its
port number. If master is a name, it must be specified with its IP address in the
/etc/hosts file on the host running the Java program. Note: The master
database server can be specified in any location in the list. It does not have to
precede the secondary database servers.
secondary_n:port_n
database
The master database server may be specified in any location in the connection list.
Connection for accessing the database for usage by the Java program is first
attempted on a synchronous secondary. The secondary servers are available for
read-only operations.
No connection attempt is made to any servers running in asynchronous mode.
The order in which connection attempts are made is determined by the
loadBalanceHosts connection property as described in Table 5-2. If disabled,
which is the default setting, connection attempts are made in the left-to-right order
specified in the connection list. If enabled, connection attempts are made
randomly.
If connection cannot be made to a synchronous secondary, then connection to the
master database server is used. If the master database server is not active, then the
connection attempt fails.
The following section provides a brief overview of setting up the master and secondary
database servers for hot standby, synchronous replication.
For general information on hot standby usage, which is needed for the
preferSyncSecondary option, see The PostgreSQL Core Documentation available at:
https://www.postgresql.org/docs/11/static/hot-standby.html
For information on creating a base backup for the secondary database server from the
master, see Section 25.3.2, Making a Base Backup (describes usage of the
pg_basebackup utility program) or Section 25.3.3, Making a Base Backup Using the
Low Level API within Section 25.3 Continuous Archiving and Point-in-Time Recovery
(PITR) in The PostgreSQL Core Documentation available at:
https://www.postgresql.org/docs/11/static/continuous-archiving.html
For information on the configuration parameters that must be set for hot standby usage,
see The PostgreSQL Core Documentation available at:
https://www.postgresql.org/docs/11/static/runtime-config-replication.html
The following section provides a basic example of setting up the master and secondary
database servers.
On the master database server’s pg_hba.conf file, there must be a replication entry for
each unique replication database USER/ADDRESS combination for all secondary database
servers. In the following example, superuser enterprisedb is used as the replication
database user for both the secondary1 database server on 192.168.2.22 and the
secondary2 database server that is local relative to the master.
After the master database server has been configured in the postgresql.conf file
along with its pg_hba.conf file, database server secondary1 is created by invoking
the following command on host 192.168.2.22 for secondary1:
su – enterprisedb
Password:
-bash-4.1$ pg_basebackup -D /opt/secondary1 -h 192.168.2.24 -p 5444 -Fp -R -X stream -l
'Secondary1'
standby_mode = 'on'
primary_conninfo = 'user=enterprisedb password=password host=192.168.2.24
port=5444 application_name=secondary1'
su - enterprisedb
Password:
-bash-4.1$ psql -d edb -c "SELECT pg_start_backup('Secondary2')"
Password:
pg_start_backup
-----------------
0/6000028
(1 row)
standby_mode = 'on'
primary_conninfo = 'user=enterprisedb password=password host=localhost
port=5444 application_name=secondary2'
Make sure the configuration parameter settings in the postgresql.conf file of the
secondary database servers are properly set, particularly with hot_standby=on.
The following table lists the basic postgresql.conf configuration parameter settings
of the master database server as compared to the secondary database servers:
To ensure that the secondary database servers are properly set up in synchronous mode,
use the following query on the master database server. Note that the sync_state
column lists applications secondary1 and secondary2 as sync.
The /etc/hosts file on the host running the Java program contains the following
entries with the server names specified in the connection URL string:
Note: Though the connection URL names secondary1 and secondary2 happen to
match the application names given in the primary_conninfo and
synchronous_standby_names parameters, this is not a requirement. The connection
URL names are independent of the application names specified by the
primary_conninfo and synchronous_standby_names parameters.
For this example, the preferred synchronous secondary connection option results in the
first usage attempt made on secondary1, then on secondary2 if secondary1 is not
active, then on the master if both secondary1 and secondary2 are not active as
demonstrated by the following program that displays the IP address and port of the
database server to which the connection is made.
import java.sql.*;
public class InetServer
{
public static void main(String[] args)
{
try
{
Class.forName("com.edb.Driver");
String url =
"jdbc:edb://master:5444,secondary1:5445,secondary2:5446/edb?targetServerType=preferSync
Secondary";
String user = "enterprisedb";
String password = "password";
Connection con = DriverManager.getConnection(url, user, password);
rs.close();
con.close();
System.out.println("Command successfully executed");
}
catch(ClassNotFoundException e)
{
System.out.println("Class Not Found : " + e.getMessage());
}
catch(SQLException exp)
{
System.out.println("SQL Exception: " + exp.getMessage());
System.out.println("SQL State: " + exp.getSQLState());
System.out.println("Vendor Error: " + exp.getErrorCode());
}
}
}
Case 1: When all database servers are active, connection is made to secondary1 on
192.168.2.22 port 5445.
$ java InetServer
192.168.2.22/32:5445
Command successfully executed
$ java InetServer
192.168.2.24/32:5446
Command successfully executed
Case 3: When secondary2 is also shut down, connection is made to the master on
192.168.2.24 port 5444.
$ java InetServer
192.168.2.24/32:5444
Command successfully executed
A Statement object sends SQL statements to a database. There are three kinds of
Statement objects. Each is specialized to send a particular type of SQL statement:
You must construct a Statement object before executing an SQL statement. The
Statement object offers a way to send a SQL statement to the server (and gain access to
the result set). Each Statement object belongs to a Connection; use the
createStatement() method to ask the Connection to create the Statement object.
The executeQuery() method expects a single argument: the SQL statement that you
want to execute. executeQuery() returns data from the query in a ResultSet object.
If the server encounters an error while executing the SQL statement provided, it throws
an SQLException (and does not return a ResultSet).
The JDBC Connector (Advanced Server version 9.6 and later) supports the use of named
parameters when instantiating a CallableStatement object. This syntax is an
extension of JDBC supported syntax, and does not conform to the JDBC standard.
When using the CallableStatement class, you can use ordinal notation or named
notation to specify values for an actual arguments. You must set a value for each IN or
INOUT parameter marker in a statement.
You can also use named parameter notation when specifying argument values for a
CallableStatement object. Named parameter notation allows you to supply values
for only those parameters that are required by the procedure, omitting any parameters that
have acceptable default values. You can also specify named parameters in any order.
When using named notation, each parameter name should correspond to a COLUMN_NAME
returned by a call to the DatabaseMetaData.getProcedureColumns method. You
should use the => token when including a named parameter in a statement call.
Examples
deptno NUMBER
)
The following example uses named notation to provide parameters; using named
notation, you can omit parameters that have default values or re-order parameters:
A ResultSet object is the primary storage mechanism for the data returned by an SQL
statement. Each ResultSet object contains both data and metadata (in the form of a
ResultSetMetaData object). ResultSetMetaData includes useful information
about results returned by the SQL command: column names, column count, row count,
column length, and so on.
To access the row data stored in a ResultSet object, an application calls one or more
getter methods. A getter method retrieves the value in particular column of the current
row. There are many different getter methods; each method returns a value of a
particular type. For example, the getString() method returns a STRING type; the
getDate() method returns a Date, and the getInt() method returns an INT type.
When an application calls a getter method, JDBC tries to convert the value into the
requested type.
Each ResultSet keeps an internal pointer that points to the current row. When the
executeQuery() method returns a ResultSet, the pointer is positioned before the
first row; if an application calls a getter method before moving the pointer, the getter
method will fail. To advance to the next (or first) row, call the ResultSet’s next()
method. ResultSet.next() is a boolean method; it returns TRUE if there is another
row in the ResultSet or FALSE if you have moved past the last row.
After moving the pointer to the first row, the sample application uses the getString()
getter method to retrieve the value in the first column and then prints that value. Since
ListEmployees calls rs.next() and rs.getString() in a loop, it processes each
row in the result set. ListEmployees exits the loop when rs.next() moves the
pointer past the last row and returns FALSE.
while(rs.next())
{
System.out.println(rs.getString(1));
}
You must call next()before reading any values. next() returns true if another
row is available and prepares the row for processing.
Under the JDBC specification, an application should access each row in the
ResultSet only once. It is safest to stick to this rule, although at the current
time, the Advanced Server JDBC driver will allow you to access a field as many
times as you want.
When you’ve finished using a ResultSet, call the close() method to free the
resources held by that object.
Every JDBC object consumes some number of resources. A ResultSet object, for
example, may contain a copy of every row returned by a query; a Statement object may
contain the text of the last command executed, and so forth. It’s usually a good idea to
free up those resources when the application no longer needs them. The sample
application releases the resources consumed by the Result, Statement, and
Connection objects by calling each object’s close() method:
rs.close();
stmt.close();
con.close();
If you attempt to use a JDBC object after closing it, that object will throw an error.
When a JDBC object throws an error (an object of type SQLException or of a type
derived from SQLException), the SQLException object exposes three different pieces
of error information:
In the example, the following code displays the value of these components should an
error occur:
For example, if the server tries to connect to a database that does not exist on the
specified host, the following error message is displayed:
Provide this method a single parameter of type String, containing the SQL command
that you wish to execute.
Listing 1.3 demonstrates using the executeUpdate() method to add a row to the emp
table.
NOTE: the following example is not a complete application, only a method - the samples
in the remainder of this document do not include the code required to set up and tear
down a Connection. To experiment with the example, you must provide a class that
invokes the sample code.
Listing 1.3
public void updateEmployee(Connection con)
{
try
{
Console console = System.console();
Statement stmt = con.createStatement();
}
}
Next, updateEmployee() prompts the user for an employee name and number:
For example, if the user enters an employee number of 6000 and a name of Jones, the
INSERT statement passed to executeUpdate() will look like this:
The executeUpdate() method returns the number of rows affected by the SQL
statement (an INSERT typically affects one row, but an UPDATE or DELETE statement can
affect more). If executeUpdate() returns without throwing an error, the call to
System.out.println displays a message to the user that shows the number of rows
affected.
System.out.println("");
System.out.println("Success - "+rowcount+" rows affected.");
The catch block displays an appropriate error message to the user if the program
encounters an exception:
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
You can use executeUpdate() with any SQL command that does not return a result
set. Some simple syntax examples using executeUpdate() with SQL commands
follow:
To use the DELETE command with executeUpdate()to remove a row from a table:
To use the DROP TABLE command with executeUpdate() to delete a table from a
database:
To use the CREATE TABLE command with executeUpdate() to add a new table to a
database:
To use the ALTER TABLE command with executeUpdate() to change the attributes of
a table:
Listing 1.4
import java.sql.*;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
...
public void showEmployees(Connection con)
{
try
{
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM emp");
ResultSetMetaData rsmd = rs.getMetaData();
Vector labels = new Vector();
for(int column = 0; column < rsmd.getColumnCount(); column++)
labels.addElement(rsmd.getColumnLabel(column + 1));
Before writing the showEmployees() method, you must import the definitions for a
few JDK-provided classes:
import java.sql.*;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
With the column headers in place, showEmployees() extracts each row from the
ResultSet and copies it into a new vector (named rows). The rows vector is actually a
vector of vectors: each entry in the rows vector contains a vector that contains the data
values in that row. This combination forms the two-dimensional array that you will need
to build a JTable. After creating the rows vector, the program reads through each row
in the ResultSet (by calling rs.next()). For each column in each row, a getter
method extracts the value at that row/column and adds the value to the rowValues
vector. Finally, showEmployee() adds each rowValues vector to the rows vector:
At this point, the vector (labels) contains the column headers, and a second two-
dimensional vector (rows) contains the data for the table. Now you can create a JTable
from the vectors and a JFrame to hold the JTable:
The showEmployees() method includes a catch block to intercept any errors that may
occur and display an appropriate message to the user:
catch(Exception err)
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
To avoid unnecessary clutter, the rest of the code samples in this document will use the
console to interact with the user instead of creating a graphical use interface.
The Advanced Server JDBC driver retrieves the results of a SQL query as a ResultSet
object. If a query returns a large number of rows, using a batched ResultSet will:
When you reduce the fetch size of a ResultSet object, the driver doesn’t copy the entire
ResultSet across the network (from the server to the client). Instead, the driver
requests a small number of rows at a time; as the client application moves through the
result set, the driver fetches the next batch of rows from the server.
Batched result sets cannot be used in all situations. Not adhering to the following
restrictions will make the driver silently fall back to fetching the whole ResultSet at
once:
Limiting the batch size of a ResultSet object can speed the retrieval of data and reduce
the resources needed by a client-side application. Listing 1.5 creates a Statement
object with a batch size limited to five rows.
Listing 1.5
// Make sure autocommit is off
conn.setAutoCommit(false);
rs.close();
stmt.close();
The call to conn.setAutoCommit(false) ensures that the server won’t close the
ResultSet before you have a chance to retrieve the first row. After preparing the
Connection, you can construct a Statement object:
The following code sets the batch size to five (rows) before executing the query:
stmt.setFetchSize(5);
For each row in the ResultSet object, the call to println() prints a row was
returned.
Remember, while the ResultSet contains all of the rows in the table, they are only
fetched from the server five rows at a time. From the client’s point of view, the only
difference between a batched result set and an unbatched result set is that a batched result
may return the first row in less time.
Next, we will look at another feature (the PreparedStatement) that you can use to
increase the performance of certain JDBC applications.
Listing 1.6
public void AddEmployee(Connection con)
{
try
{
Console c = System.console();
String command = "INSERT INTO emp(empno,ename) VALUES(?,?)";
PreparedStatement stmt = con.prepareStatement(command);
stmt.setObject(1,new Integer(c.readLine("ID:")));
stmt.setObject(2,c.readLine("Name:"));
stmt.execute();
Instead of hard-coding data values in the SQL statement, you insert placeholders to
represent the values that will change with each iteration. Listing 1.6 shows an INSERT
statement that includes two placeholders (each represented by a question mark):
With the parameterized SQL statement in hand, the AddEmployee() method can ask the
Connection object to prepare that statement and return a PreparedStatement object:
At this point, the PreparedStatement has parsed and planned the INSERT statement,
but it does not know what values to add to the table. Before executing the
PreparedStatement, you must supply a value for each placeholder by calling a setter
method. setObject() expects two arguments:
The AddEmployee() method prompts the user for an employee ID and name and calls
setObject() with the values supplied by the user:
stmt.setObject(1,new Integer(c.readLine("ID:")));
stmt.setObject(2, c.readLine("Name:"));
stmt.execute();
Stored procedures are especially useful when extensive data manipulation is required
before storing data from the client. It is also efficient to use a stored procedure to
manipulate data in a batch program.
The CallableStatement class provides a way for a Java program to call stored
procedures. A CallableStatement object can have a variable number of parameters
used for input (IN parameters), output (OUT parameters), or both (IN OUT parameters).
The syntax for invoking a stored procedure in JDBC is shown below. Note that the
square brackets indicate optional parameters; they are not part of the command syntax.
Each question mark serves as a placeholder for a parameter. The stored procedure
determines if the placeholders represent IN, OUT, or IN OUT parameters and the Java code
must match. We will show you how to supply values for IN (or IN OUT) parameters and
how to retrieve values returned in OUT (or IN OUT) parameters in a moment.
Listing 1.7-a
CREATE OR REPLACE PROCEDURE increaseSalary
IS
BEGIN
UPDATE emp SET sal = sal * 1.10;
END;
Listing 1.7-b
public void SimpleCallSample(Connection con)
{
try
{
CallableStatement stmt = con.prepareCall("{call increaseSalary()}");
stmt.execute();
System.out.println("Stored Procedure executed successfully");
}
catch(Exception err)
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
}
As the name implies, the prepareCall() method prepares the statement, but does not
execute it. As you will see in the next example, an application typically binds parameter
values between the call to prepareCall() and the call to execute(). To invoke the
stored procedure on the server, call the execute() method.
stmt.execute();
This stored procedure (increaseSalary) did not expect any IN parameters and did not
return any information to the caller (using OUT parameters) so invoking the procedure is
simply a matter of creating a CallableStatement object and then calling that object’s
execute() method.
The next section demonstrates how to invoke a stored procedure that requires data (IN
parameters) from the caller.
Listing 1.8-a creates the stored procedure in the Advanced Server database:
Listing 1.8-a
CREATE OR REPLACE PROCEDURE empInsert(
pEname IN VARCHAR,
pJob IN VARCHAR,
pSal IN FLOAT4,
pComm IN FLOAT4,
pDeptno IN INTEGER,
pMgr IN INTEGER
)
AS
DECLARE
CURSOR getMax IS SELECT MAX(empno) FROM emp;
max_empno INTEGER := 10;
BEGIN
OPEN getMax;
FETCH getMax INTO max_empno;
INSERT INTO emp(empno, ename, job, sal, comm, deptno, mgr)
VALUES(max_empno+1, pEname, pJob, pSal, pComm, pDeptno, pMgr);
CLOSE getMax;
END;
Listing 1.8-b demonstrates how to invoke the stored procedure from Java:
Listing 1.8-b
public void CallExample2(Connection con)
{
try
{
Console c = System.console();
String commandText = "{call empInsert(?,?,?,?,?,?)}";
CallableStatement stmt = con.prepareCall(commandText);
stmt.setObject(1, new String(c.readLine("Employee Name :")));
stmt.setObject(2, new String(c.readLine("Job :")));
stmt.setObject(3, new Float(c.readLine("Salary :")));
stmt.setObject(4, new Float(c.readLine("Commission :")));
stmt.setObject(5, new Integer(c.readLine("Department No :")));
stmt.setObject(6, new Integer(c.readLine("Manager")));
stmt.execute();
}
catch(Exception err)
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
}
After supplying a value for each placeholder, this method executes the statement by
calling the execute() method.
The next example creates and invokes an SPL stored procedure called deptSelect.
This procedure requires one IN parameter (department number) and returns two OUT
parameters (the department name and location) corresponding to the department number.
The code in Listing 1.9-a creates the deptSelect procedure:
Listing 1.9-a
CREATE OR REPLACE PROCEDURE deptSelect
(
p_deptno IN INTEGER,
p_dname OUT VARCHAR,
p_loc OUT VARCHAR
)
AS
DECLARE
CURSOR deptCursor IS SELECT dname, loc FROM dept WHERE deptno=p_deptno;
BEGIN
OPEN deptCursor;
FETCH deptCursor INTO p_dname, p_loc;
CLOSE deptCursor;
END;
Listing 1.9-b shows the Java code required to invoke the deptSelect stored procedure:
Listing 1.9-b
public void GetDeptInfo(Connection con)
{
try
{
Console c = System.console();
String commandText = "{call deptSelect(?,?,?)}";
CallableStatement stmt = con.prepareCall(commandText);
stmt.setObject(1, new Integer(c.readLine("Dept No :")));
stmt.registerOutParameter(2, Types.VARCHAR);
stmt.registerOutParameter(3, Types.VARCHAR);
stmt.execute();
System.out.println("Dept Name: " + stmt.getString(2));
System.out.println("Location : " + stmt.getString(3));
}
catch(Exception err)
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
}
The JDBC type of each OUT parameter must be registered before the
CallableStatement object can be executed. Registering the JDBC type is done with
the registerOutParameter() method.
stmt.registerOutParameter(2, Types.VARCHAR);
stmt.registerOutParameter(3, Types.VARCHAR);
After executing the statement, the CallableStatement’s getter method retrieves the
OUT parameter values: to retrieve a VARCHAR value, use the getString() getter
method.
stmt.execute();
System.out.println("Dept Name: " + stmt.getString(2));
System.out.println("Location : " + stmt.getString(3));
In the current example GetDeptInfo() registers two OUT parameters and (after
executing the stored procedure) retrieves the values returned in the OUT parameters.
Since both OUT parameters are defined as VARCHAR values, GetDeptInfo() uses the
getString() method to retrieve the OUT parameters.
The code in the next example creates and invokes a stored procedure named empQuery
defined with one IN parameter (p_deptno), two IN OUT parameters (p_empno and
p_ename) and three OUT parameters (p_job, p_hiredate and p_sal). empQuery
then returns information about the employee in the two IN OUT parameters and three OUT
parameters.
Listing 1.10-a
CREATE OR REPLACE PROCEDURE empQuery
(
p_deptno IN NUMBER,
p_empno IN OUT NUMBER,
p_ename IN OUT VARCHAR2,
p_job OUT VARCHAR2,
p_hiredate OUT DATE,
p_sal OUT NUMBER
)
IS
BEGIN
SELECT empno, ename, job, hiredate, sal
INTO p_empno, p_ename, p_job, p_hiredate, p_sal
FROM emp
WHERE deptno = p_deptno
AND (empno = p_empno
OR ename = UPPER(p_ename));
END;
Listing 1.10-b demonstrates invoking the empQuery procedure, providing values for the
IN parameters, and handling the OUT and IN OUT parameters:
Listing 1.10-b
public void CallSample4(Connection con)
{
try
{
Console c = System.console();
String commandText = "{call emp_query(?,?,?,?,?,?)}";
CallableStatement stmt = con.prepareCall(commandText);
stmt.setInt(1, new Integer(c.readLine("Department No:")));
stmt.setInt(2, new Integer(c.readLine("Employee No:")));
stmt.setString(3, new String(c.readLine("Employee Name:")));
stmt.registerOutParameter(2, Types.INTEGER);
stmt.registerOutParameter(3, Types.VARCHAR);
stmt.registerOutParameter(4, Types.VARCHAR);
stmt.registerOutParameter(5, Types.TIMESTAMP);
stmt.registerOutParameter(6, Types.NUMERIC);
stmt.execute();
System.out.println("Employee No: " + stmt.getInt(2));
System.out.println("Employee Name: " + stmt.getString(3));
System.out.println("Job : " + stmt.getString(4));
System.out.println("Hiredate : " + stmt.getTimestamp(5));
System.out.println("Salary : " + stmt.getBigDecimal(6));
}
catch(Exception err)
{
System.out.println("An error has occurred.");
System.out.println("See full details below.");
err.printStackTrace();
}
}
The setInt() method is a type-specific setter method that binds an Integer value to
an IN or IN OUT placeholder. The call to setInt() specifies a parameter number and
provides a value to substitute in place of that placeholder:
Before executing the CallableStatement, you must register the JDBC type of each
OUT parameter by calling the registerOutParameter() method.
stmt.registerOutParameter(2, Types.INTEGER);
stmt.registerOutParameter(3, Types.VARCHAR);
stmt.registerOutParameter(4, Types.VARCHAR);
stmt.registerOutParameter(5, Types.TIMESTAMP);
stmt.registerOutParameter(6, Types.NUMERIC);
Remember, before calling a procedure with an IN parameter, you must assign a value to
that parameter with a setter method. Before calling a procedure with an OUT parameter,
you register the type of that parameter; then you can retrieve the value returned by calling
a getter method. When calling a procedure that defines an IN OUT parameter, you must
perform all three actions:
A REF CURSOR is a cursor variable that contains a pointer to a query result set returned
by an OPEN statement. Unlike a static cursor, a REF CURSOR is not tied to a particular
query. You may open the same REF CURSOR variable any number of times with the
OPEN statement containing different queries; each time, a new result set is created for
that query and made available via the cursor variable. A REF CURSOR can also pass a
result set from one procedure to another.
Advanced Server supports the declaration of both strongly-typed and weakly-typed REF
CURSORs. A strongly-typed cursor must declare the shape (the type of each column) of
the expected result set. You can only use a strongly-typed cursor with a query that
returns the declared columns; opening the cursor with a query that returns a result set
with a different shape will cause the server to throw an exception. On the other hand, a
weakly-typed cursor can work with a result set of any shape.
name SYS_REFCURSOR;
The stored procedure shown in Listing 1.11-a (getEmpNames) builds two REF CURSORs
on the server; the first REF CURSOR contains a list of commissioned employees in the
emp table, while the second REF CURSOR contains a list of salaried employees in the emp
table:
Listing 1.11-a
CREATE OR REPLACE PROCEDURE getEmpNames
(
commissioned IN OUT SYS_REFCURSOR,
salaried IN OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN commissioned FOR SELECT ename FROM emp WHERE comm is NOT NULL;
OPEN salaried FOR SELECT ename FROM emp WHERE comm is NULL;
END;
Listing 1.11-b
public void RefCursorSample(Connection con)
{
try
{
con.setAutoCommit(false);
String commandText = "{call getEmpNames(?,?)}";
CallableStatement stmt = con.prepareCall(commandText);
stmt.registerOutParameter(1, Types.REF);
stmt.registerOutParameter(2, Types.REF);
stmt.execute();
ResultSet commissioned = (ResultSet)stmt.getObject(1);
System.out.println("Commissioned employees:");
while(commissioned.next())
{
System.out.println(commissioned.getString(1));
}
stmt.registerOutParameter(1, Types.REF);
stmt.registerOutParameter(2, Types.REF);
stmt.execute();
The getObject() method retrieves the values from the first parameter and casts the
result to a ResultSet. Then, RefCursorSample iterates through the cursor and prints
the name of each commissioned employee:
The same getter method retrieves the ResultSet from the second parameter and
RefCursorExample iterates through that cursor, printing the name of each salaried
employee:
The BYTEA data type stores a binary string in a sequence of bytes; digital images and
sound files are often stored as binary data. Advanced Server can store and retrieve binary
data via the BYTEA data type.
The following Java sample stores BYTEA data in an Advanced Server database and then
demonstrates how to retrieve that data. The example requires a bit of setup; Listings
1.12-a, 1.12-b, and 1.12-c create the server-side environment for the Java example.
Listing 1.12-a creates a table (emp_detail) that stores BYTEA data. emp_detail
contains two columns: the first column stores an employee’s ID number (type INT) and
serves as the primary key for the table; the second column stores a photograph of the
employee in BYTEA format.
Listing 1.12-a
CREATE TABLE emp_detail
(
empno INT4 PRIMARY KEY,
pic BYTEA
);
Listing 1.12-b creates a procedure (ADD_PIC) that inserts a row into the emp_detail
table:
Listing 1.12-b
CREATE OR REPLACE PROCEDURE ADD_PIC(p_empno IN int4, p_photo IN bytea) AS
BEGIN
INSERT INTO emp_detail VALUES(p_empno, p_photo);
END;
And finally, Listing 1.12-c creates a function (GET_PIC) that returns the photograph for a
given employee:
Listing 1.12-c
CREATE OR REPLACE FUNCTION GET_PIC(p_empno IN int4) RETURN BYTEA IS
DECLARE
photo BYTEA;
BEGIN
SELECT pic INTO photo from EMP_DETAIL WHERE empno = p_empno;
RETURN photo;
END;
Listing 1.13 shows a Java method that invokes the ADD_PIC procedure (see Listing 1.12-
b) to copy a photograph from the client file system to the emp_detail table on the
server.
Listing 1.13
public void InsertPic(Connection con)
{
try
{
Console c = System.console();
int empno = Integer.parseInt(c.readLine("Employee No :"));
String fileName = c.readLine("Image filename :");
File f = new File(fileName);
if(!f.exists())
{
System.out.println("Image file not found. Terminating...");
return;
}
InsertPic() prompts the user for an employee number and the name of an image file:
If the requested file does not exist, InsertPic() displays an error message and
terminates:
if(!f.exists())
{
System.out.println("Image file not found. Terminating...");
return;
}
(p_photo). To provide actual values for those placeholders, InsertPic() calls two
setter methods. Since the first parameter is of type INTEGER, InsertPic() calls the
setInt() method to provide a value for p_empno. The second parameter is of type
BYTEA, so InsertPic() uses a binary setter method; in this case, the method is
setBinaryStream():
Now that the placeholders are bound to actual values, InsertPic() executes the
CallableStatement:
stmt.execute();
If all goes well, InsertPic() displays a message verifying that the image has been
added to the table. If an error occurs, the catch block displays a message to the user:
Now that you know how to insert BYTEA data from a Java application, Listing 1.14
demonstrates how to retrieve BYTEA data from the server.
Listing 1.14
public static void GetPic(Connection con)
{
try
{
Console c = System.console();
int empno = Integer.parseInt(c.readLine("Employee No :"));
CallableStatement stmt = con.prepareCall("{?=call GET_PIC(?)}");
stmt.setInt(2, empno);
stmt.registerOutParameter(1, Types.BINARY);
stmt.execute();
byte[] b = stmt.getBytes(1);
Next, GetPic() prepares a CallableStatement with one IN parameter and one OUT
parameter. The first parameter is the OUT parameter that will contain the photograph
retrieved from the database. Since the photograph is BYTEA data, GetPic() registers
the parameter as a Type.BINARY. The second parameter is the IN parameter that holds
the employee number (an INT), so GetPic() uses the setInt() method to provide a
value for the second parameter.
Next, GetPic() uses the getBytes getter method to retrieve the BYTEA data from the
CallableStatement:
stmt.execute();
byte[] b = stmt.getBytes(1);
The program prompts the user for the name of the file where it will store the photograph:
The FileOutputStream object writes the binary data that contains the photograph to
the destination filename:
Finally, GetPic() displays a message confirming that the file has been saved at the new
location:
The SQL CREATE TYPE command is used to create a user-defined object type, which is
stored in the Advanced Server database. The CREATE TYPE command is also used to
create a collection, commonly referred to as an array, which is also stored in the
Advanced Server database.
These user-defined types can then be referenced within SPL procedures, SPL functions,
and Java programs.
The basic object type is created with the CREATE TYPE AS OBJECT command along
with optional usage of the CREATE TYPE BODY command.
A nested table type collection is created using the CREATE TYPE AS TABLE OF
command. A varray type collection is created with the CREATE TYPE VARRAY
command.
Example usage of an object type and a collection are shown in the following sections.
Listing 1.15 shows a Java method used by both examples to establish the connection to
the Advanced Server database.
Listing 1.15
public static Connection getEDBConnection() throws
ClassNotFoundException, SQLException {
String url = "jdbc:edb://localhost:5444/test";
String user = "enterprisedb";
String password = "edb";
Class.forName("com.edb.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
Create the object types in the Advanced Server database. Object type
addr_object_type defines the attributes of an address:
Object type emp_obj_typ defines the attributes of an employee. Note that one of these
attributes is object type ADDR_OBJECT_TYPE as previously described. The object type
body contains a method that displays the employee information:
Listing 1.16 is a Java method that includes these user-defined object types:
Listing 1.16
public static void testUDT() throws SQLException {
Connection conn = null;
try {
conn = getEDBConnection();
String commandText = "{call emp_obj_typ.display_emp(?)}";
CallableStatement stmt = conn.prepareCall(commandText);
The setObject() method binds the object instance emp to the IN OUT placeholder.
stmt.setObject(1, emp);
stmt.execute();
getObject() retrieves the emp_obj_typ object type. The attributes of the emp and
address object instances are then retrieved and displayed:
emp = (Struct)stmt.getObject(1);
Object[] attrEmp = emp.getAttributes();
System.out.println("empno: " + attrEmp[0]);
System.out.println("ename: " + attrEmp[1]);
Listing 1.17-a is an SPL function that uses collection types NUMBER_ARRAY and
CHAR_ARRAY as IN parameters and CHAR_ARRAY as the OUT parameter.
The function concatenates the employee ID from the NUMBER_ARRAY IN parameter with
the employee name in the corresponding row from the CHAR_ARRAY IN parameter. The
resulting concatenated entries are returned in the CHAR_ARRAY OUT parameter.
Listing 1.17-a
CREATE OR REPLACE FUNCTION concatEmpIdName
(
arrEmpIds NUMBER_ARRAY,
arrEmpNames CHAR_ARRAY
) RETURN CHAR_ARRAY
AS
DECLARE
i INTEGER := 0;
arrEmpIdNames CHAR_ARRAY;
BEGIN
arrEmpIdNames := CHAR_ARRAY(NULL,NULL);
FOR i IN arrEmpIds.FIRST..arrEmpIds.LAST LOOP
arrEmpIdNames(i) := arrEmpIds(i) || ' ' || arrEmpNames(i);
END LOOP;
RETURN arrEmpIdNames;
END;
Listing 1.17-b is a Java method that calls the Listing 1.17-a function, passing and
retrieving the collection types:
Listing 1.17-b
public static void testTableOfAsInOutParams() throws SQLException {
Connection conn = null;
try {
conn = getEDBConnection();
String commandText = "{? = call concatEmpIdName(?,?)}";
CallableStatement stmt = conn.prepareCall(commandText);
stmt.registerOutParameter(1, Types.ARRAY);
stmt.execute();
getArray() retrieves the collection returned by the function. The first two rows
consisting of the concatenated employee IDs and names are displayed:
Listing 1.18-a shows an SPL procedure that loops through the emp table and gives each
employee a 10% raise. As each employee is processed, adjustSalary executes a
RAISE NOTICE statement (in this case, the message contained in the notification reports
progress to the client application). Listing 1.18-b will demonstrate how to create a
NoticeListener that intercepts each notification.
Listing 1.18-a
CREATE OR REPLACE PROCEDURE adjustSalary
IS
v_empno NUMBER(4);
v_ename VARCHAR2(10);
CURSOR emp_cur IS SELECT empno, ename FROM emp;
BEGIN
OPEN emp_cur;
LOOP
FETCH emp_cur INTO v_empno, v_ename;
EXIT WHEN emp_cur%NOTFOUND;
Listing 1.18-b
stmt.execute();
System.out.println("Finished");
Each time the adjustSalary procedure executes a RAISE NOTICE statement, the
server sends the text of the message ("Salary increased for ...") to the
Statement (or derivative) object in the client application. JDBC invokes the
noticeReceived() method (possibly many times) before the call to
stmt.execute() completes.
Notice that each Statement object keeps a list of NoticeListeners. When the
JDBC driver receives a notification from the server, it consults the list maintained by the
Statement object. If the list is empty, the notification is saved in the Statement
object (you can retrieve the notifications by calling stmt.getWarnings() once the call
to execute() completes). If the list is not empty, the JDBC driver delivers an
SQLWarning to each listener, in the order in which the listeners were added to the
Statement.
9 Using SSL
In this section, we will cover:
Configuring the server
Configuring the client
Testing the SSL JDBC Connection
Using SSL without Certificate Validation
Using Certificate Authentication (without a password)
To learn about configuring the EnterpriseDB server for SSL, refer to:
https://www.enterprisedb.com/docs/en/10/pg/ssl-tcp.html
Note: Before you access your SSL enabled server from Java, ensure that you are logged
to server via edb-psql. The sample output should look like the one below if you have
established a SSL connection:
edb=#
There are a number of connection parameters for configuring the client for SSL. To know
more about the SSL Connection parameters and Additional Connection Properties, refer
to Section 5.2.
In this section, we will discuss more about the behavior of ssl connection parameter when
passed with different values. When you pass the connection parameter ssl=true into
the driver, the driver validates the SSL certificate and verifies the hostname. On contrary
to this behavior, using libpq defaults to a non-validating SSL connection.
You can get better control of the SSL connection using the sslmode connection
parameter. This parameter is the same as the libpq sslmode parameter and the
existing SSL implements the following:
sslmode=require
This mode makes the encryption mandatory and also requires the connection to fail if it
can’t be encrypted. The server is configured to accept SSL connections for this Host/IP
address and that the server recognizes the client certificate.
Note: In this mode, the JDBC driver accepts all server certificates.
sslmode=verify-ca
sslmode=verify-full
If sslmode=verify-full, the server host name is verified to make sure it matches the
name stored in the server certificate. The SSL connection fails if the server certificate
cannot be verified. This mode is recommended in most security-sensitive environments.
In the case where the certificate validation is failing you can try sslcert= and
LibPQFactory will not send the client certificate. If the server is not configured to
authenticate using the certificate it should connect.
The location of the client certificate, client key and root certificate can be overridden with
the sslcert, sslkey, and sslrootcert settings respectively. These default to
/defaultdir/postgresql.crt, /defaultdir/postgresql.pk8, and
/defaultdir/root.crt respectively where defaultdir is
In this mode, when establishing a SSL connection the JDBC driver will validate the
server's identity preventing "man in the middle" attacks. It does this by checking that the
server certificate is signed by a trusted authority, and that the host you are connecting to,
is the same as the hostname in the certificate.
If you are using Java's default mechanism (not LibPQFactory) to create the SSL
connection, you need to make the server certificate available to Java, which can be
achieved by implementing steps given below:
props.setProperty(“ssl”,“true”);
Sting url=“jdbc:edb://localhost/test?user=fred&password=secret&ssl=true”;
Note: The default password for the cacerts keystore is changeit. The alias to
postgresql is not important and you may select any name you desire.
4. If you do not have access to the system cacerts truststore, create your own
truststore as below:
$ java -Djavax.net.ssl.trustStore=mystore
com.mycompany.MyApp
For example:
By default the combination of SSL=true and setting the connection URL parameter
sslfactory=com.edb.ssl.NonValidatingFactory encrypts the connection but
does not validate the SSL certificate. To enforce certificate validation, you must use a
Custom SSLSocketFactory.
https://jdbc.postgresql.org/documentation/head/ssl-factory.html
In order to use certificate authentication (without a password), follow the below steps:
https://jdbc.postgresql.org/documentation/head/logging.html
Note: Previous versions of the Advanced Server JDBC Connector used a custom
mechanism to enable logging, which is now replaced by the use of
java.util.logging in versions moving forward from community version 42.1.4.1.
The older mechanism is no longer available.
loggerLevel
Logger level of the driver. Allowed values are OFF, DEBUG, or TRACE.
loggerFile
The following example sets the logging level to TRACE (FINEST) and the log file
to EDB-JDBC.LOG:
jdbc:edb://myhost:5444/mydb?loggerLevel=TRACE&loggerFile=EDB-JDBC.LOG
handlers = java.util.logging.FileHandler
//logging level
.level = OFF
Set the logging level for the JDBC Connector (maps to loggerLevel)
com.edb.level=FINEST
Note: Types.OTHER is not only used for UUID, but is also used if you do not specify any
specific type and allow the server or the JDBC driver to determine the type. In the case
where the parameter is an instance of java.util.UUID, the JDBC driver determines the
appropriate internal type and sends it to the server.