This document illustrates the basics of the JDBC (Java Database Connectivity) API (Application Program Interface). Here, you will learn to use the basic JDBC API to create tables, insert values, query tables, retrieve results, update tables, create prepared statements, perform transactions on a database system from a Java program.
This document draws from the official Sun tutorial on JDBC Basics.
Call-level interfaces such as JDBC are programming interfaces allowing external programs to access SQL databases. They allow the execution of SQL commands within a general programming environment by providing library routines which interface with the database. In particular, Java-based JDBC has a rich collection of routines which make such an interface extremely simple and intuitive.
Here is an easy way of visualizing what happens in a call level interface: You are writing a normal Java program. Somewhere in the program, you need to interact with a database. Using standard library routines, you open a connection to the database. You then use JDBC to send your SQL code to the database, and process the results that are returned. When you are done, you close the connection. For your convenience, all of the code for this article is included in the jdbc-tutorial.zip file.
As we said earlier, before a database can be accessed, a connection must be opened between our Java program (client) and the database (server). This involves two steps:
Why would we need this step? To ensure portability and code reuse, the JDBC API was designed to be as independent of the version or the vendor of a database as possible. The differences between different DBMS's are encapsulated by each DBMS's driver, and we need to tell the Java DriverManager the correct driver to load. A MySQL driver is loaded using the following code:
Once the driver is loaded and ready for a connection to be made, you may create an instance of a Connection object using:
Okay, let's see what this jargon is. The first string is the URL for the database including the protocol (jdbc), the vendor (mysql), and the server port (//localhost:3306/) and your database instance name (DatabaseName). Of course, you need to replace DatabaseName with the name of your database. The username and passwd are your username and password, the same as you would enter into MySQL to access your account.
That's it! The connection returned in the last step is an open connection which we will use to pass SQL statements to the database. In this code snippet, con is an open connection, and we will use it below.
A JDBC Statement object is used to send your SQL statements to the DBMS, and should not to be confused with an SQL statement. A JDBC Statement object is associated with an open connection, and not any single SQL Statement. You can think of a JDBC Statement object as a channel sitting on a connection, and passing one or more of your SQL statements (which you ask it to execute) to the DBMS.
An active connection is needed to create a Statement object. The following code snippet, using our Connection object con, does it for you:
At this point, a Statement object exists, but it does not have an SQL statement to pass on to the DBMS. We learn how to do that in a following section.
Sometimes, it is more convenient or more efficient to use a PreparedStatement object for sending SQL statements to the DBMS. The main feature which distinguishes it from its superclass Statement, is that unlike Statement, it is given an SQL statement right when it is created. This SQL statement is then sent to the DBMS right away, where it is compiled. The advantage offered is that if you need to use the same, or similar query with different parameters multiple times, the statement can be compiled and optimized by the DBMS just once. Contrast this with a use of a normal Statement where each use of the same SQL statement requires a compilation all over again.
PreparedStatements are also created with a Connection method. The following snippet shows how to create a parameterized SQL statement with three input parameters:
Before we can execute a PreparedStatement, we need to supply values for the parameters. This can be done by calling one of the setXXX methods defined in the class PreparedStatement. Most often used methods are setInt, setFloat, setDouble, setString etc. You can set these values before each execution of the prepared statement.
Continuing the above example, we would write:
Executing SQL statements in JDBC varies depending on the ``intention'' of the SQL statement. DDL (data definition language) statements such as table creation and table alteration statements, as well as statements to update the table contents, are all executed using the method executeUpdate. Notice that these commands change the state of the database, hence the name of the method contains ``Update''.
The following snippet has examples of executeUpdate statements.
Note here that we are reusing the same Statement object rather than having to create a new one.
When executeUpdate is used to call DDL statements (like CREATE TABLE), the return value is always zero, while data modification statement (like INSERT) executions will return a value greater than or equal to zero, which is the number of tuples affected in the relation.
While working with a PreparedStatement, we would execute such a statement by first plugging in the values of the parameters (as seen above), and then invoking the executeUpdate on it.
As opposed to the previous section statements, a query is expected to return a set of tuples as the result, and not change the state of the database. Not surprisingly, there is a corresponding method called executeQuery, which returns its results as a ResultSet object:
ResultSet rs = stmt.executeQuery("SELECT * FROM Sells");
while (rs.next()) {
bar = rs.getString("bar");
beer = rs.getString("beer");
price = rs.getFloat("price");
System.out.println(bar + " sells " + beer + " for " +
price + " Dollars.");
}
The bag of tuples resulting from the query are contained in the variable rs which is an instance of ResultSet. A set is of not much use to us unless we can access each row and the attributes in each row. The ResultSet provides a cursor to us, which can be used to access each row in turn. The cursor is initially set just before the first row. Each invocation of the method next() causes it to move to the next row, if one exists and return true, or return false if there is no remaining row.
We can use the getXXX method of the appropriate type to retrieve the attributes of a row. In the previous example, we used getString and getFloat methods to access the column values. Notice that we provided the name of the column whose value is desired as a parameter to the method. Also note that the VARCHAR type bar, beer have been converted to Java String, and the REAL to Java float.
Equivalently, we could have specified the column number instead of the column name, with the same result. Thus the relevant statements would be:
While working with a PreparedStatement, we would execute a query by first plugging in the values of the parameters, and then invoking the executeQuery on it.
The truth is errors always occur in software programs. Often, database programs are critical applications, and it is imperative that errors be caught and handled gracefully. Programs should recover and leave the database in a consistent state. Rollback-s used in conjunction with Java exception handlers are a clean way of achieving such a requirement.
The client (program) accessing a server (database) needs to be aware of any errors returned from the server. JDBC give access to such information by providing two levels of error conditions: SQLException and SQLWarning. SQLExceptions are Java exceptions which, if not handled, will terminate the application. SQLWarnings are subclasses of SQLException, but they represent nonfatal errors or unexpected conditions, and as such, can be ignored.
In Java, statements which are expected to "throw" an exception or a warning are enclosed in a try block. If a statement in the try block throws an exception or a warning, it can be "caught" in one of the corresponding catch statements. Each catch statement specifies which exceptions it is ready to "catch".
Here is an example of catching an SQLException:
stmt.executeUpdate("CREATE TABLE Sells (bar VARCHAR(40), beerVARHAR(40), price REAL)");
stmt.executeUpdate("INSERT INTO Sells VALUES('Bar Of Foo', 'BudLite', 2.00)");
} catch (SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
}
In this case, an exception is thrown because beer is defined as VARHAR which is a mis-spelling. Since there is no such data type in our DBMS, an SQLException is thrown. Alternatively, if your datatypes were correct, an exception might be thrown in case your database size goes over space quota and is unable to construct a new table.
SQLWarnings can be retrieved from Connection objects, Statement objects, and ResultSet objects. Each only stores the most recent SQLWarning. So if you execute another statement through your Statement object, for instance, any earlier warnings will be discarded. Here is a code snippet which illustrates the use of SQLWarnings:
SQLWarning warn = stmt.getWarnings();
if (warn != null) System.out.println("Message: " + warn.getMessage());
SQLWarning warning = rs.getWarnings();
if (warning != null) System.out.println("Message: " + warning.getMessage());
SQLWarnings (as opposed to SQLExceptions) are actually rather rare -- the most common is a DataTruncation warning. The latter indicates that there was a problem while reading or writing data from the database.
Hopefully, by now you are familiar enough with JDBC to write serious code. Here is the jdbc-tutorial.zip which provides the code that ties all the ideas in the tutorial together. The program connects to the database TEST using the username "cs144" with empty password.
Notes on CLASSPATH
Since we are accessing MySQL through JDBC, we need a "driver" for MySQL JDBC interface. This driver file is available in our VM as mysql-connector-java.jar in /usr/share/java/ directory. Since this is a third-party jar library file that is not part of the standard Java Runtime environment, the Java compiler and runtime engine are NOT aware of this file and may generate "class not found" error when you try to compile and run your code that depends on JDBC API. To avoid this error you have to make sure one of the following:
javac -classpath ".:/usr/share/java/*.jar" YourClass.javaand
java -classpath ".:/usr/share/java/*.jar" YourClass