Programming With JDBC Apache Sample Programs
Programming With JDBC Apache Sample Programs
In this chapter, we will create Java programs which will work with the Derby database.
The examples are executed in the Derby embedded mode. We use the derby.jar library to
connect to the Derby engine in the embedded mode.
JDBC
JDBC is an API for the Java programming language that defines how a client may access a
database. It provides methods for querying and updating data in a database. JDBC is oriented
towards relational databases. From a technical point of view, the API is as a set of classes in the
java.sql package. To use JDBC with a particular database, we need a JDBC driver for that
database.
In the first example, we will create a CARS table and insert 8 rows into it. Drop the CARS table
from the database if it is already created before running the example.
package zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
st = con.createStatement();
st.executeUpdate("CREATE TABLE CARS(ID INT PRIMARY KEY,"
+ "NAME VARCHAR(30), PRICE INT)");
st.executeUpdate("INSERT INTO CARS VALUES(1, 'Audi', 52642)");
st.executeUpdate("INSERT INTO CARS VALUES(2, 'Mercedes',
57127)");
st.executeUpdate("INSERT INTO CARS VALUES(3, 'Skoda', 9000)");
st.executeUpdate("INSERT INTO CARS VALUES(4, 'Volvo', 29000)");
st.executeUpdate("INSERT INTO CARS VALUES(5, 'Bentley',
350000)");
st.executeUpdate("INSERT INTO CARS VALUES(6, 'Citroen', 21000)");
st.executeUpdate("INSERT INTO CARS VALUES(7, 'Hummer', 41400)");
st.executeUpdate("INSERT INTO CARS VALUES(8, 'Volkswagen',
21600)");
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
The example connects to the Derby in embedded mode. Creates a CARS table and adds 8 rows
into it. Finally, it shuts down Derby.
This is the URL to connect to the testdb database. In the embedded mode and USER12 schema.
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
A connection to the Derby database is created. When the connection is created, the Derby
database is booted.
We execute the SQL statements which create the database and fill it with some data. For
INSERT, UPDATE and DELETE statements and DDL statements like CREATE TABLE we use
the executeUpdate() method.
DriverManager.getConnection("jdbc:derby:;shutdown=true");
We catch the SQLException. We use the Logger class to log the error message.
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
When the Derby engine is shut down, an SQLException is thrown. We catch this exception and
log an information message.
} finally {
try {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
$ javac zetcode/CreateCars.java
$ java -cp .:lib/derby.jar zetcode.CreateCars
Feb 17, 2012 11:34:02 PM zetcode.CreateCars main
INFO: Derby shut down normally
java.sql.SQLException: Derby system shutdown.
...
We compile and run the example. The shut down of Derby will end in an SQLException. This is
a feature of the Derby database.
Retrieving data
Next we will show, how to retrieve data from a database table. We get all data from the CARS
table.
package zetcode;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
st = con.createStatement();
rs = st.executeQuery("SELECT * FROM USER12.CARS");
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(" ");
System.out.print(rs.getString(2));
System.out.print(" ");
System.out.println(rs.getString(3));
}
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} finally {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
We get all the cars from the CARS table and print them to the console.
st = con.createStatement();
rs = st.executeQuery("SELECT * FROM USER12.CARS");
We execute a query that selects all columns from the CARS table. We use the executeQuery()
method. The method executes the given SQL statement, which returns a single ResultSet object.
The ResultSet is the data table returned by the SQL query. Also note that since we have not
specified the user name in the URL, we have to explicitly mention the schema name in the SQL
statement.
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(" ");
System.out.print(rs.getString(2));
System.out.print(" ");
System.out.println(rs.getString(3));
}
The next() method advances the cursor to the next record of the result set. It returns false, when
there are no more rows in the result set. The getInt() and getString() methods retrieve the value
of the designated column in the current row of this ResultSet object; an int/String in the Java
programming language.
$ javac zetcode/SelectAllCars.java
$ java -cp .:lib/derby.jar zetcode.SelectAllCars
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600
Feb 18, 2012 12:33:31 AM zetcode.SelectAllCars main
INFO: Derby shut down normally
java.sql.SQLException: Derby system shutdown.
...
We compile and run the example. We have a list of all cars from the CARS table of the testdb
database.
Properties
It is a common practice to put the configuration data outside the program in a separate file. This
way the programmers are more flexible. We can change the user, a password or a connection url
without needing to recompile the program. It is especially useful in a dynamic environment,
where is a need for a lot of testing, debugging, securing data etc.
In Java, the Properties is a class used often for this. The class is used for easy reading and saving
of key/value properties.
db.url=jdbc:derby:testdb;user=USER12
db.user=USER12
db.passwd=34klq*
db.syshome=/home/janbodnar/programming/derby/dbs
We have a database.properties file, in which we have four key/value pairs. These are
dynamically loaded during the execution of the program.
package zetcode;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
in = new FileInputStream("database.properties");
props.load(in);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
Logger lgr =
Logger.getLogger(PropertiesExample.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
try {
System.setProperty("derby.system.home",
props.getProperty("db.syshome"));
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
Logger lgr =
Logger.getLogger(PropertiesExample.class.getName());
lgr.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
}
We connect to the testdb and select all authors from the AUTHORS table. The configuration data
for the example is read from the database.properties file.
try {
in = new FileInputStream("database.properties");
props.load(in);
The Properties class is created. The data is loaded from the file called database.properties, where
we have our configuration data.
Prepared statements
Now we will concern ourselves with prepared statements. When we write prepared statements,
we use placeholders instead of directly writing the values into the statements. Prepared
statements increase security and performance.
package zetcode;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} finally {
try {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
These are the values that are going to be set to the prepared statement. These values could come
from a user and everything coming from users should be considered potentionally dangerous.
Here we create a prepared statement. When we write prepared statements, we use placeholders
instead of directly writing the values into the statements. Prepared statements are faster and
guard against SQL injection attacks. The ? is a placeholder, which is going to be filled later.
pst.setInt(1, price);
pst.setInt(2, id);
pst.executeUpdate();
The prepared statement is executed. We use the executeUpdate() method of the statement object
when we don't expect any data to be returned. This is when we create databases or execute
INSERT, UPDATE, DELETE statements.
$ javac zetcode/Prepared.java
$ java -cp .:lib/derby.jar zetcode.Prepared
Feb 18, 2012 11:08:47 AM zetcode.Prepared main
SEVERE: Database 'testdb' shutdown.
java.sql.SQLNonTransientConnectionException: Database 'testdb' shutdown.
...
1 row selected
We compile the example. Run it. And check the outcome with the ij tool.
Column headers
Next we will show, how to print column headers with the data from the database table. We refer
to column names as MetaData. MetaData is data about the core data in the database.
package zetcode;
import java.sql.*;
import java.util.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
String query = "SELECT NAME, TITLE From AUTHORS, "
+ "Books WHERE AUTHORS.ID=BOOKS.AUTHOR_ID";
pst = con.prepareStatement(query);
rs = pst.executeQuery();
while (rs.next()) {
Formatter fmt2 = new Formatter();
fmt2.format("%-21s", rs.getString(1));
System.out.print(fmt2);
System.out.println(rs.getString(2));
}
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
In this program, we select authors from the AUTHORS table and their books from the BOOKS
table. We print the names of the columns returned in the result set. We format the output. The
SQL file to create the tables is located in the first chapter of this tutorial.
To get the column names we need to get the ResultSetMetaData. It is an object that can be used
to get information about the types and properties of the columns in a ResultSet object.
We print the column names to the console. The Formatter object formats the data.
while (rs.next()) {
Formatter fmt2 = new Formatter();
fmt2.format("%-21s", rs.getString(1));
System.out.print(fmt2);
System.out.println(rs.getString(2));
}
We print the data to the console. We again use the Formatter object to format the data. The first
column is 21 characters wide and is aligned to the left.
$ javac zetcode/ColumnHeaders.java
$ java -cp .:lib/derby.jar zetcode.ColumnHeaders
NAME TITLE
Jack London Call of the Wild
Jack London Martin Eden
Honore de Balzac Old Goriot
Honore de Balzac Cousin Bette
Lion Feuchtwanger Jew Suess
Emile Zola Nana
Emile Zola The Belly of Paris
Truman Capote In Cold blood
Truman Capote Breakfast at Tiffany
Feb 18, 2012 12:15:21 PM zetcode.ColumnHeaders main
INFO: Derby shut down normally
java.sql.SQLException: Derby system shutdown.
...
Writing images
Some people prefer to put their images into the database, some prefer to keep them on the file
system for their applications. Technical difficulties arise when we work with millions of images.
Images are binary data. Derby has a special data type to store binary data called BLOB (Binary
Large Object).
We create a new table called IMAGES for this and the following example.
The DATA column has the BLOB type. There we will insert the encoded binary data.
package zetcode;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
con = DriverManager.getConnection(url);
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
In this example, we read a jpg image from the current working directory and insert in into the
IMAGES table.
We create a File object for the image file. To read bytes from this file, we create a
FileInputStream object.
The binary stream is set to the prepared statement. The parameters of the setBinaryStream()
method are the parameter index to bind, the input stream and the number of bytes in the stream.
pst.executeUpdate();
We execute the statement.
Reading images
In the previous example, we have inserted an image into the database table. Now we are going to
read the image back from the table.
package zetcode;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
System.out.println(System.getProperty("user.dir"));
con = DriverManager.getConnection(url);
fos.write(buf, 0, len);
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (IOException ex) {
Logger lgr = Logger.getLogger(ReadImage.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
The FileOutputStream object is created to write to a file. It is meant for writing streams of raw
bytes such as image data.
We get the image data from the DATA column by calling the getBlob() method.
int len = (int) blob.length();
We find out the length of the blob data. In other words, we get the number of bytes.
The getBytes() method retrieves all bytes of the BLOB object, as an array of bytes.
fos.write(buf, 0, len);
The bytes are written to the output stream. The image is created on the filesystem.
Transaction support
A transaction is an atomic unit of database operations against the data in one or more databases.
The effects of all the SQL statements in a transaction can be either all committed to the database
or all rolled back.
When a connection is created, it is in autocommit mode. This means that each individual SQL
statement is treated as a transaction and is automatically committed right after it is executed. This
is true for all JDBC drivers, including the Derby's one. To start a new transaction, we turn the
autocommit off.
In direct SQL, a transaction is started with BEGIN TRANSACTION statement and ended with
END TRANSACTION/COMMIT statement. In Derby these statements are BEGIN and
COMMIT. However, when working with drivers these statements are omitted. They are handled
by the driver. Exact details are specific to the driver. For example psycopg2 Python driver starts
a transaction after the first SQL statement. If we want the autocommit mode, we must be set the
autocommit property to True. In constrast, JDBC driver is by default in the autocommit mode.
And to start a new transaction, the autocommit must be turned off.
package zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
st = con.createStatement();
con.setAutoCommit(false);
con.commit();
DriverManager.getConnection("jdbc:derby:;shutdown=true");
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
if (con != null) {
try {
con.rollback();
} catch (SQLException ex1) {
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
}
} finally {
try {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
In this program, we want to change the name of the author in the first row of the AUTHORS
table. We must also change the books associated with this author. If we change the author and do
not change the author's books, the data is corrupted.
con.setAutoCommit(false);
To work with transactions, we must set the autocommit to false. By default, a database
connection is in autocommit mode. In this mode each statement is committed to the database, as
soon as it is executed. A statement cannot be undone. When the autocommit is turned off, we
commit the changes by calling the commit() or roll it back by calling the rollback() method.
The third SQL statement has an error. There is no TITL column in the BOOKS table.
con.commit();
If there is no exception, the transaction is committed. If the autocommit is turned off, we must
explicitly call the commit() method.
if (con != null) {
try {
con.rollback();
} catch (SQLException ex1) {
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
}
In case of an exception other than the Derby system shutdown, the transaction is rolled back. No
changes are committed to the database.
$ javac zetcode/Transaction.java
$ java -cp .:lib/derby.jar zetcode.Transaction
Feb 18, 2012 3:02:05 PM zetcode.Transaction main
SEVERE: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.
java.sql.SQLSyntaxErrorException: 'TITL' is not a column in table or VTI
'USER12.BOOKS'.
...
ij> SELECT NAME, TITLE FROM AUTHORS, BOOKS WHERE
> AUTHORS.ID = BOOKS.AUTHOR_ID;
NAME |TITLE
------------------------------------------------------------
Jack London |Call of the Wild
Jack London |Martin Eden
Honore de Balzac |Old Goriot
Honore de Balzac |Cousin Bette
Lion Feuchtwanger |Jew Suess
Emile Zola |Nana
Emile Zola |The Belly of Paris
Truman Capote |In Cold blood
Truman Capote |Breakfast at Tiffany
9 rows selected
The execution fails with the "'TITL' is not a column in table" message. An exception was thrown.
The transaction was rolled back and no changes took place.
package zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
st = con.createStatement();
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (SQLException ex) {
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
} finally {
try {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
We have the same example. This time, without the transaction support.
$ javac zetcode/NonTransaction.java
$ java -cp .:lib/derby.jar zetcode.NonTransaction
Feb 18, 2012 3:13:45 PM zetcode.NonTransaction main
SEVERE: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.
java.sql.SQLSyntaxErrorException: 'TITL' is not a column in table or VTI
'USER12.BOOKS'.
...
9 rows selected
An exception is thrown again. Leo Tolstoy did not write Martin Eden. The data is corrupted.
Batch updates
When we need to update data with multiple statements, we can use batch updates. Batch updates
are available for INSERT, UPDATE, DELETE statements as well as for CREATE TABLE and
DROP TABLE statements.
package zetcode;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
try {
System.setProperty("derby.system.home",
"/home/janbodnar/programming/derby/dbs");
con = DriverManager.getConnection(url);
con.setAutoCommit(false);
st = con.createStatement();
con.commit();
if (((ex.getErrorCode() == 50000)
&& ("XJ015".equals(ex.getSQLState())))) {
} else {
if (con != null) {
try {
con.rollback();
} catch (SQLException ex1) {
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
}
} finally {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
This is an example program for a batch update. We delete all rows from the CARS table and
insert nine rows into it.
con.setAutoCommit(false);
After adding all commands, we call the executeBatch() to perform a batch update. The method
returns an array of committed changes.
con.commit();
$ javac zetcode/BatchUpdates.java
$ java -cp .:lib/derby.jar zetcode.BatchUpdates Committed 10 updates
Feb 18, 2012 11:14:53 PM zetcode.BatchUpdates main
INFO: Derby shut down normally
java.sql.SQLException: Derby system shutdown.
...