PostgreSQL: JDBC on PRISM at CSE York
JDBC Application Programs

This will walk you through building a database application program in Java via JDBC on CSE's PRISM environment for accessing PostgreSQL.

Priming the Shell

First, you need to download the PostgreSQL JDBC Driver, 42.2.14 and copy the jar file to your PRISM server. For other version of JDBC driver, check here.

Then, you need to let javac know, when compiling your java application program (APP), where to locate the JDBC library (that is, it is on the CLASSPATH). Same as running your program (java). Instead of setting the global CLASSPATH via environment variables (for all java APPs), we would recommand to use option -cp at runtime to set the class search path (only) for your APP.

Once you have a window (and, thus, shell) open on a PRISM machine (e.g., red.cs.yorku.ca), you can compile and run your code (e.g., the example in this page) as following:

% javac CustTotal.java
% java -cp <path-to-jdbc-driver-directory>/postgresql-42.2.14.jar:. CustTotal

The Connection

In your APP, you will need to first establish the appropriate driver with the DriverManager. This is specific for the database system (or other data source) your APP will be talking to via the JDBC API. E.g.,

        import java.net.*;
        import java.sql.*;
        ...
        // Register the driver with DriverManager.
        Class.forName("org.postgresql.Driver").newInstance();

Next, you want to establish a connection with a particular database (served by the particular database server "instance"). This is functionally the same thing as when you say psql -h db in the shell.

        private Connection conDB;   // Connection to the database system.
        private String url;         // URL: Which database?
	    ...
        url = "jdbc:postgresql://db:5432/<dbname>"; // URL: jdbc:postgresql://host:port/database.
        conDB = DriverManager.getConnection(url,"<username>","<password>"); 

This connection convention can be used to establish a connection to a database server and database that is on a remote system. In this case, we are connecting to your database running on the db server.

The getConnection method supports a number of additional properties which can be used to specify additional driver behaviour specific to PostgreSQL. These properties may be specified in either the connection URL or an additional Properties object. Check this here for more details.

When the APP is done, it is good programming practise to close the connection.

conDB.close();

Of course, the connection does automatically get closed when the APP process shuts down.

For each of these calls, one needs to encase them in a try...catch block (or further throw the exceptions yourself for your caller to worry about, which is a bit more rude). Each can throw a SQLException. Best for your APP writing would be to catch any such exception, print out some meaningful message about where in the process the failure occured, and then exit. There is not much really you can do (at this point) if the database system is refusing to cooperate.

Registering the driver can result in a number of exception types. Either just catch the generic Exception for it, or see the example for more.

Queries: "Talking" to the Database

In a JDBC application program, you get the information you need from the database, and set the information you need to in the database, via SQL. The SQL statements are composed in Java strings, prepared, and executed. A cursor is used to walk through the results (if needed) of an executed SQL query. E.g.,

        String            queryText = "";     // The SQL text.
        PreparedStatement querySt   = null;   // The query handle.
        ResultSet         answers   = null;   // A cursor.

Design a query:

        queryText =
            "SELECT COUNT(*) as NumOfCusts"
          + "    FROM yrb_customer";

Prepare the query:

        querySt = conDB.prepareStatement(queryText);

At this point the database system parses the query, and builds an executable query plan for it. The query has not been executed yet, though. A query handle object is returned (querySt here) which we use to execute the query.

To execute, we can say

        answers = querySt.executeQuery();

The executeQuery method returns a cursor object (answers here), which is of type ResultSet in JDBC speak.

If there are no tuples being returned (e.g., this is an update "query"), we could execute the SQL statement instead with the method updateQuery(). This returns an int that reports how many rows in the database were affected.

In the above case, we expect just a single answer tuple to be found.

        answers.next();
        int num_of_customers = answers.getInt("NumOfCusts");
        System.out.print("There are ");
        System.out.print(num_of_customers);
        System.out.println(" number of customers.");

Of course, as good programmers, we should check whether there is an answer tuple! If there are no customers, the query would have resulted in the empty table.

        if (answers.next()) {
            int num_of_customers = answers.getInt("NumOfCusts");
            System.out.print("There are ");
            System.out.print(num_of_customers);
            System.out.println(" number of customers.");
        } else {
            System.out.println("There are no customers.");
        }

For each of these calls, we might get an SQLException thrown. Usually this means that something went wrong with respect to the SQL statement in question. Each of the calls above should be encased in a try...catch block to catch such exceptions (or properly thrown along).

When we are done with the cursor, we should close it.

        // Close the cursor.
        answers.close();

Likewise, when we are done with a query handle, we ought to close it too.

        // We're done with the handle.
        querySt.close();

An Example

The following is an example. It reports the total sales for a specified customer from the YRB database. For running it, replace <dbname> and <username> with your PRISM username, <password> with your student number (your password if you have changed). Since we are using the schema search path yrb, we need to specify it in the URL of the connection.

CustTotal.java
1. /*============================================================================
2. CustTotal: A JDBC APP to list total sales for a customer from the YRB DB.
 
3. Parke Godfrey 
4. 2013 March 26 [revised]
5. 2004 March    [original]

6. Wenxiao Fu
7. 2020 July [adapted to PostgreSQL]
8. ============================================================================*/
 
9. import java.util.*;
10. import java.net.*;
11. import java.text.*;
12. import java.lang.*;
13. import java.io.*;
14. import java.sql.*;
 
15. /*============================================================================
16. CLASS CustTotal
17. ============================================================================*/
 
18. public class CustTotal {
19.     private Connection conDB;   // Connection to the database system.
20.     private String url;         // URL: Which database?
 
21.     private Integer custID;     // Who are we tallying?
22.     private String  custName;   // Name of that customer.
 
23.     // Constructor
24.     public CustTotal (String[] args) {
25.         // Set up the DB connection.
26.         try {
27.             // Register the driver with DriverManager.
28.             Class.forName("org.postgresql.Driver").newInstance();
29.         } catch (ClassNotFoundException e) {
30.             e.printStackTrace();
31.             System.exit(0);
32.         } catch (InstantiationException e) {
33.             e.printStackTrace();
34.             System.exit(0);
35.         } catch (IllegalAccessException e) {
36.             e.printStackTrace();
37.             System.exit(0);
38.         }
 
39.         // URL: Which database?
40.         url = "jdbc:postgresql://db:5432/<dbname>?currentSchema=yrb";
 
41.         // Initialize the connection.
42.         try {
43.             // Connect with a fall-thru id & password
44.             conDB = DriverManager.getConnection(url);
45.         } catch(SQLException e) {
46.             System.out.print("\nSQL: database connection error.\n");
47.             System.out.println(e.toString());
48.             System.exit(0);
49.         }    
 
50.         // Let's have autocommit turned off.  No particular reason here.
51.         try {
52.             conDB.setAutoCommit(false);
53.         } catch(SQLException e) {
54.             System.out.print("\nFailed trying to turn autocommit off.\n");
55.             e.printStackTrace();
56.             System.exit(0);
57.         }    
 
58.         // Who are we tallying?
59.         if (args.length != 1) {
60.             // Don't know what's wanted.  Bail.
61.             System.out.println("\nUsage: java CustTotal cust#");
62.             System.exit(0);
63.         } else {
64.             try {
65.                 custID = new Integer(args[0]);
66.             } catch (NumberFormatException e) {
67.                 System.out.println("\nUsage: java CustTotal cust#");
68.                 System.out.println("Provide an INT for the cust#.");
69.                 System.exit(0);
70.             }
71.         }
 
72.         // Is this custID for real?
73.         if (!customerCheck()) {
74.             System.out.print("There is no customer #");
75.             System.out.print(custID);
76.             System.out.println(" in the database.");
77.             System.out.println("Bye.");
78.             System.exit(0);
79.         }
 
80.         // Report total sales for this customer.
81.         reportSalesForCustomer();
 
82.         // Commit.  Okay, here nothing to commit really, but why not...
83.         try {
84.             conDB.commit();
85.         } catch(SQLException e) {
86.             System.out.print("\nFailed trying to commit.\n");
87.             e.printStackTrace();
88.             System.exit(0);
89.         }    
90.         // Close the connection.
91.         try {
92.             conDB.close();
93.         } catch(SQLException e) {
94.             System.out.print("\nFailed trying to close the connection.\n");
95.             e.printStackTrace();
96.             System.exit(0);
97.         }    
 
98.     }
 
99.     public boolean customerCheck() {
100.         String            queryText = "";     // The SQL text.
101.         PreparedStatement querySt   = null;   // The query handle.
102.         ResultSet         answers   = null;   // A cursor.
 
103.         boolean           inDB      = false;  // Return.
 
104.         queryText =
105.             "SELECT name       "
106.           + "FROM yrb_customer "
107.           + "WHERE cid = ?     ";
 
108.         // Prepare the query.
109.         try {
110.             querySt = conDB.prepareStatement(queryText);
111.         } catch(SQLException e) {
112.             System.out.println("SQL#1 failed in prepare");
113.             System.out.println(e.toString());
114.             System.exit(0);
115.         }
 
116.         // Execute the query.
117.         try {
118.             querySt.setInt(1, custID.intValue());
119.             answers = querySt.executeQuery();
120.         } catch(SQLException e) {
121.             System.out.println("SQL#1 failed in execute");
122.             System.out.println(e.toString());
123.             System.exit(0);
124.         }
 
125.         // Any answer?
126.         try {
127.             if (answers.next()) {
128.                 inDB = true;
129.                 custName = answers.getString("name");
130.             } else {
131.                 inDB = false;
132.                 custName = null;
133.             }
134.         } catch(SQLException e) {
135.             System.out.println("SQL#1 failed in cursor.");
136.             System.out.println(e.toString());
137.             System.exit(0);
138.         }
 
139.         // Close the cursor.
140.         try {
141.             answers.close();
142.         } catch(SQLException e) {
143.             System.out.print("SQL#1 failed closing cursor.\n");
144.             System.out.println(e.toString());
145.             System.exit(0);
146.         }
 
147.         // We're done with the handle.
148.         try {
149.             querySt.close();
150.         } catch(SQLException e) {
151.             System.out.print("SQL#1 failed closing the handle.\n");
152.             System.out.println(e.toString());
153.             System.exit(0);
154.         }
 
155.         return inDB;
156.     }
 
157.     public void reportSalesForCustomer() {
158.         String            queryText = "";     // The SQL text.
159.         PreparedStatement querySt   = null;   // The query handle.
160.         ResultSet         answers   = null;   // A cursor.
 
161.         queryText =
162.             "SELECT SUM(P.qnty * O.price) as total          "
163.           + "    FROM yrb_purchase P, yrb_offer O           "
164.           + "    WHERE P.cid = ?                            "
165.           + "      AND P.title = O.title AND P.year = O.year";
 
166.         // Prepare the query.
167.         try {
168.             querySt = conDB.prepareStatement(queryText);
169.         } catch(SQLException e) {
170.             System.out.println("SQL#2 failed in prepare");
171.             System.out.println(e.toString());
172.             System.exit(0);
173.         }
 
174.         // Execute the query.
175.         try {
176.             querySt.setInt(1, custID.intValue());
177.             answers = querySt.executeQuery();
178.         } catch(SQLException e) {
179.             System.out.println("SQL#2 failed in execute");
180.             System.out.println(e.toString());
181.             System.exit(0);
182.         }
 
183.         // Variables to hold the column value(s).
184.         float  sales;
         
185.         DecimalFormat df = new DecimalFormat("####0.00");
 
186.         // Walk through the results and present them.
187.         try {
188.             System.out.print("#");
189.             System.out.print(custID);
190.             System.out.print(" (" + custName + ") has spent $");
191.             if (answers.next()) {
192.                 sales = answers.getFloat("total");
193.                 System.out.print(df.format(sales));
194.             } else {
195.                 System.out.print(df.format(0));
196.             }
197.             System.out.println(".");
198.         } catch(SQLException e) {
199.             System.out.println("SQL#2 failed in cursor.");
200.             System.out.println(e.toString());
201.             System.exit(0);
202.         }
 
203.         // Close the cursor.
204.         try {
205.             answers.close();
206.         } catch(SQLException e) {
207.             System.out.print("SQL#2 failed closing cursor.\n");
208.             System.out.println(e.toString());
209.             System.exit(0);
210.         }
 
211.         // We're done with the handle.
212.         try {
213.             querySt.close();
214.         } catch(SQLException e) {
215.             System.out.print("SQL#2 failed closing the handle.\n");
216.             System.out.println(e.toString());
217.             System.exit(0);
218.         }
 
219.     }
 
220.     public static void main(String[] args) {
221.         CustTotal ct = new CustTotal(args);
222.     }
223.  }

parke godfrey