<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<article id="jdbc">
  <articleinfo>
    <title>An Introduction to Basic JDBC</title>
    <author>
      <firstname>Alan</firstname>
      <othername>P</othername>
      <surname>Sexton</surname>
      <affiliation>
        <address><email>aps@cs.bham.ac.uk</email></address>
      </affiliation>
    </author>
    <copyright>
      <year>2005</year>
      <holder role="mailto:aps@cs.bham.ac.uk">Alan P. Sexton</holder>
    </copyright>
    <revhistory>
      <revision>
        <revnumber>1.0</revnumber>
        <date>27 July 2001</date>
        <authorinitials>aps</authorinitials>
        <revremark>
          This is the first version of a JDBC tutorial. Currently this talks only about using an Access database through a JDBC-ODBC bridge. Later versions shall cover using PostgreSQL on Solaris, Linux and Windows. I assume the use of the Standard Edition JDK 1.3.1.
        </revremark>
      </revision>
    </revhistory>
  </articleinfo>

  <sect1 id="jdbc-basic"><title>Basic JDBC</title>
    <para>
      JDBC (the Java Database Connection) is the standard method of accessing databases from Java. Sun developed the JDBC library after considering Microsoft's ODBC. Their aims were to get something similar but easier to learn and use: ODBC is complex because it has a few very complex calls. JDBC has split up this complexity into many more calls, but with each of them being relatively simple.
    </para>

    <para>Accessing a database using JDBC involves a number of steps:</para>

    <orderedlist>
      <listitem><para>Get a <emphasis>Connection</emphasis> object connected to a database</para></listitem>
      <listitem>
        <para>Get a <emphasis>Statement</emphasis> object from an open <emphasis>Connection</emphasis>object</para>
      </listitem>
      <listitem><para>Get a ResultSet from a <emphasis>Statement</emphasis>'s query execution</para></listitem>
      <listitem><para>Process the rows from the <emphasis>ResultSet</emphasis></para></listitem>
    </orderedlist>

    <para>
      While the above is the standard pattern, there are variations: Instead of a <emphasis>Statement</emphasis>, you can get a <emphasis>PreparedStatement</emphasis> (which allows a query to be pre-compiled for extra performance when executed repetitively). An update query may only return a count of the number of rows updated, inserted or deleted instead of a <emphasis>ResultSet</emphasis>. There are calls to get information about the database and about <emphasis>ResultSet</emphasis> objects (e.g. the number, names and types of columns). There are calls to support transactional updates to the database.
    </para>

    <sect2 id="jdbc-basic-packages"><title>Packages</title>
      <para>The main package used is</para>

      <programlisting>import java.sql.* ;</programlisting>

      <para>
        This is in the Standard Edition JDK. There is an extension package from the Enterprise Edition JDK. However, this is rarely necessary and even then only for the use of some advanced features such as connection pooling:
      </para>

      <programlisting>import javax.sql.* ;</programlisting>
    </sect2>

    <sect2 id="jdbc-basic-drivers"><title>Drivers</title>
      <para>
        Individual database management systems require a JDBC driver to be accessed via JDBC. Sun provides, as part of the Standard Edition JDK, a JDBC-ODBC bridge driver, which lets you connect to any ODBC database. This driver is intended for test and study purposes and is not recommended for commercial use (it is slow, missing some features and is somewhat buggy). Most DBMS vendors supply their own drivers with their products and there are many third party drivers available (both commercial and free)
      </para>

      <para>
        The first step in accessing the database is to load a driver. First your driver should be installed on the system (usually this requires having the driver jar file available and its path name in your classpath. If you are using the JDBC-ODBC bridge driver, then you have to register your database with ODBC (on windows, use the &quot;ODBC data sources&quot; tool in the control panel). The class name of the JDBC-ODBC bridge driver is <emphasis>sun.jdbc.odbc.JdbcOdbcDriver</emphasis>. When a driver is loaded, it registers itself with Drivermanager which is then used to get the Connection.
      </para>

      <para>There are a number of alternative ways to do the actual loading::</para>

      <orderedlist>
        <listitem>
          <para>
            Use <emphasis>new</emphasis> to explicitly load the Driver class. This hard codes the driver and (indirectly) the name of the database into your program and is not recommended as changing the driver or the database or even the name or location of the database will usually require recompiling the program.
          </para>
        </listitem>

        <listitem>
          <para>
            Class.forName takes a string class name and loads the necessary class dynamically at runtime. This is a safe method that works well in all Java environments although it still requires extra coding to avoid hard coding the class name into the program.
          </para>
        </listitem>

        <listitem>
          <para>
            The System class has a static Property list. If this has a Property <emphasis>jdbc.drivers</emphasis> set to a ':' separated list of driver class names, then all of these drivers will be loaded and registered automatically. Since there is support for loading property lists from files easily in Java, this is a convenient mechanism to set up a whole set of drivers. When a connection is requested, all loaded drivers are checked to see which one can handle the request and an appropriate one is chosen. Unfortunately, support for using this approach in servlet servers is patchy so we will stay with method 2 above but use the properties file method to load the database url and the driver name at runtime:
          </para>

          <programlisting>
Properties props = new Properties() ;
FileInputStream in = new FileInputStream(&quot;Database.Properties&quot;) ;
props.load(in) ;
     
String drivers = props.getProperty(&quot;jdbc.drivers&quot;) ;
Class.forName(drivers) ;</programlisting>
        </listitem>
      </orderedlist>

      <para>The Database.Properties file contents look like this:</para>

      <programlisting>
# Default JDBC driver and database specification
jdbc.drivers      =  sun.jdbc.odbc.JdbcOdbcDriver
database.Shop  =  jdbc:odbc:Shop</programlisting>
    </sect2>

    <sect2 id="jdbc-basic-getting-a-connection"><title>Getting a Connection</title>
      <para>
        To get a connection, we need to specify a url for the actual database we wish to use. The form of this url is specific to the driver we are using. With the driver loaded, we can use the properties file above to get the database url. Using the Sun JDBC-ODBC bridge driver, the url of the database is <emphasis>jdb:odbc:xxx</emphasis> where xxx is the ODBC data source name registered for your database. (The name of the property we use is unimportant)
      </para>

      <programlisting>
String database = props.getProperty(&quot;database.Shop&quot;) ;
Connection con = DriverManager.getConnection(database) ;</programlisting>

      <warning>
        <para>
          Microsoft Access, accessed through the JDBC-ODBC bridge driver, is not thread safe. This means that multiple different concurrent connections to Access will not necessarily work in a correct manner. In consequence, no application should have multiple Connection objects open at the same time and, therefore, use of Microsoft Access for Web Database work is severely limited (although it is still fine for educational purposes).
        </para>
      </warning>
    </sect2>

    <sect2 id="jdbc-basic-statements"><title>Using Statements</title>
      <para>A Statement is obtained from a Connection:</para>

      <programlisting>Statement stmt = con.createStatement() ;</programlisting>

      <para>
        Once you have a Statement, you can use it to execute, and control the execution of, various kinds of SQL queries.
      </para>

      <itemizedlist>
        <listitem>
          <para>
            Use <emphasis>stmt.executeUpdate</emphasis> with a string argument containing the text of an SQL update query (INSERT, DELETE or UPDATE). This returns an integer count of the number of rows updated.
          </para>
        </listitem>

        <listitem>
          <para>
            Use <emphasis>stmt.executeQuery</emphasis> with a string argument containing the text of an SQL SELECT query. This returns a <emphasis>ResultSet</emphasis> object which is used to access the rows of the query results.
          </para>
        </listitem>

        <listitem>
          <para>
            You can use <emphasis>stmt.execute</emphasis> to execute an arbitrary SQL statement which may be of any type, but extracting the results, whether an integer or a <emphasis>ResultSet</emphasis>, is less convenient. This is usually only used where you want a generalized access to the database that allows programmatic generation of queries.
          </para>

          <programlisting>
int count = stmt.executeUpdate(&quot;INSERT INTO Customers &quot; +
                               &quot;(CustomerFirstName, CustomerLastName, CustomerAddress) &quot;
                               &quot;VALUES ('Tony', 'Blair', '10 Downing Street, London')&quot;) ;
ResultSet rs = stmt.executeQuery(&quot;SELECT * FROM Customers&quot;) ;
// do something with count and RS
          </programlisting>
        </listitem>
      </itemizedlist>

      <note>
        <para>
          The syntax of the SQL string passed as an argument must match the syntax of the database being used. In particular, appropriate quoting of special characters must be used. For example, if a name, <emphasis>O'Neill</emphasis>, is to be inserted, it has to be entered as
        </para>

        <programlisting>
ResultSet rs = stmt.executeQuery(&quot;SELECT * FROM Customers&quot; +
                                 &quot;WHERE CustomerLastName = 'O''Neill'&quot;) ;</programlisting>
      </note>
    </sect2>

    <sect2 id="jdbc-basic-resultsets"><title>Working with ResultSets</title>
      <para>
        If you do not know exactly the table structure (the schema) of the <emphasis>ResultSet</emphasis>, you can obtain it via a <emphasis>ResultSetMetaData</emphasis> object.
      </para>

      <programlisting>
ResultSetMetaData rsmd = rs.getMetaData() ;
int colCount = rsmd.getColumnCount() ;

for (int i = 1 ; i &lt;= colCount ; i++)
{
 if (i &gt; 1)
  out.print(&quot;, &quot;);
 out.print(rsmd.getColumnLabel(i)) ;
}
out.println() ;</programlisting>

      <para>
        Once a <emphasis>ResultSet</emphasis> has been obtained, you can step through it to obtain its rows, or, more specifically, the fields of its rows:
      </para>

      <programlisting>
while (rs.next())
{
 for (int i = 1 ; i &lt;= colCount ; i++)
 {
  if (i &gt; 1)
   out.print(&quot;, &quot;);
  out.print(rs.getObject(i)) ;
 }
 out.println() ;
}</programlisting>
      
      <para>
        Note that the column numbers start at 1, not 0 as in Java arrays. More conveniently, although somewhat less efficiently, there is a <emphasis>getObject</emphasis> method for <emphasis>ResultSet</emphasis> which takes a String argument containing the column name. There are also <emphasis>getxxx</emphasis> methods that take the <emphasis>String</emphasis> name of the column instead of the column number. Thus the above code could have been written:
      </para>

      <programlisting>
while (rs.next())
{
 out.println( rs.getObject(&quot;CustomerID&quot;)   + &quot;, &quot; +
   rs.getObject(&quot;CustomerFirstName&quot;) + &quot;, &quot; +
   rs.getObject(&quot;CustomerLastName&quot;)  + &quot;, &quot; +
   rs.getObject(&quot;CustomerAddress&quot;) ) ;
}</programlisting>

      <para>
        Instead of <emphasis>getObject</emphasis>, you can use type specific methods, <emphasis>getInt</emphasis>, <emphasis>getString</emphasis>, etc. However, these have a major disadvantage: if the field is of primitive type such as int, float etc., then if the field is actually null in the database, then there is no value that can be returned that is indistinguishable from some valid value. There is a mechanism for finding out whether the last value obtained was really null or not: <emphasis>wasNull</emphasis>, but this must be called immediately after the <emphasis>getXxx</emphasis> method and before the next such call. If you use <emphasis>getObject</emphasis>, then if the field was null then the object value returned will be null so you can pass this value around and check for it at your convenience. Note also that printing is the most common thing to do with retrieved values, and passing a null to print will print the string &quot;null&quot;. Thus for many cases no extra processing of nulls will be necessary.
      </para>
    </sect2>

    <sect2 id="jdbc-basic-perpared-statements"><title>Prepared Statements</title>

      <para>
        Rather than <emphasis>Statement</emphasis> objects, <emphasis>PreparedStatement</emphasis> objects can be used. This have the advantages over plain <emphasis>Statement</emphasis> objects of:
      </para>

      <itemizedlist>
        <listitem>
          <para>
            For repetitive queries that are very similar except for some parameter values, they are considerably more efficient because the SQL is compiled once and then executed many times, with the parameter values substituted in each execution
          </para>
        </listitem>

        <listitem>
          <para>
            The mechanism for inserting parameter values takes care of all necessary special character quoting in the correct manner for the connected database
          </para>
        </listitem>
      </itemizedlist>

      <para>
        The <emphasis>PreparedStatement</emphasis> has its SQL text set when it is constructed. The parameters are specified as '?' characters. After creation, the parameters can be cleared using <emphasis>clearParameters</emphasis> and set using <emphasis>setInt</emphasis>, <emphasis>setString</emphasis>, etc. methods (parameter positions start at 1) and the statement can then be executed using <emphasis>execute</emphasis>, <emphasis>executeUpdate</emphasis> or <emphasis>executeQuery</emphasis> methods as for <emphasis>Statement</emphasis> and with the same return types but with no arguments (as the SQL text has already been set when the statement was created):
      </para>

      <programlisting>
PreparedStatement pstmt = con.prepareStatement(
                              &quot;INSERT INTO Customers &quot; +
                              &quot;(CustomerFirstName, CustomerLastName, CustomerAddress) &quot;+
                              &quot;VALUES (?, ?, ?)&quot;) ;

pstmt.clearParameters() ;
pstmt.setString(1, &quot;Joan&quot;) ;
pstmt.setString(2, &quot;D'Arc&quot;) ;
pstmt.setString(3, &quot;Tower of London&quot;) ;
count = pstmt.executeUpdate() ;
System.out.println (&quot;\nInserted &quot; + count + &quot; record successfully\n&quot;) ;

pstmt.clearParameters() ;
pstmt.setString(1, &quot;John&quot;) ;
pstmt.setString(2, &quot;D'Orc&quot;) ;
pstmt.setString(3, &quot;Houses of Parliament, London&quot;) ;
count = pstmt.executeUpdate() ;
System.out.println (&quot;\nInserted &quot; + count + &quot; record successfully\n&quot;) ;</programlisting>
    </sect2>

    <sect2 id="jdbc-basic-transactions"><title>Transactions</title>
      <para>
        Transactions are a mechanism to group operations together so that either all of them complete together successfully or none of them do. This avoids database consistency problems that can occur if some groups of operations are only partly completed. Think of a bank transfer that requires withdrawing money from one account to deposit in another. If the withdraw is completed but the deposit fails then the customer is likely to be very unhappy. If the deposit succeeds but the withdraw fails then the bank is likely to be very unhappy. Actually, transactions handle other aspects of consistency as well. For example, ensuring that a second transaction sees the database as if either the first transaction has completely finished or as if it has not started yet but <emphasis role="strong">not</emphasis> as if some of the first transaction's operations have completed but not others - even if both transactions are running simultaneously.
      </para>

      <para>
        When a <emphasis>Connection</emphasis> is obtained, by default its AutoCommit property is set to true. This means that every query execution is committed immediately after it is executed and before the next one is executed. To enable grouping of operations in transactions, you have to switch the AutoCommit property off:
      </para>

      <programlisting>con.setAutoCommit(false) ;</programlisting>

      <para>
        Now you have to obtain new statement objects from the connection (the old ones won't work), and query or update as usual. When all operations that you want to group together have completed, you <emphasis role="strong">must</emphasis> commit the updates to the database:
      </para>

      <programlisting>con.commit() ;</programlisting>

      <para>
        At this point you can continue with more operations which will be grouped into a new transaction or you can switch AutoCommit back on:
      </para>

      <programlisting>con.setAutoCommit(true) ;</programlisting>

      <para>
        If anything goes wrong during a transaction (e.g. an Exception is thrown or an error means that you cannot complete your group of operations) then you have to undo all operations in your transaction so far:
      </para>

      <programlisting>con.rollBack() ;</programlisting>

      <note>
        <para>
          If the database or the machine crashes, rollBack will (essentially) be called for you automatically to clean up uncommitted transactions when the database is restarted.
        </para>
      </note>

      <para>
        You should make every effort to minimise the length of time that you have open transactions running as they hold expensive resources and, in particular, locks in the database system which may stop any other competing transactions from proceeding. In many cases, careful design may obviate many needs to use anything other than the standard AutoCommit mode in all except a very few cases.
      </para>

      <para>
        Getting all this working correctly requires careful attention to your Exception handling. You must embed a transaction in a try clause so that any exception will trigger a rollback. If you do have to handle an exception (and therefore rollback) in a method that normally closes open connections before returning, make sure that this does not create a loop hole that allows the method to return without closing the connection. You can use a <emphasis>finally</emphasis> block to ensure that this is handled correctly.
      </para>

      <para>
        Finally, note that when you modify the AutoCommit status of a connection, all operations by any thread using that connection object are run in the same transaction. Therefore you have to be very careful about sharing connection objects between different threads (particularly important in servlet and JSP code). The simple rule is that you can share without problems a connection which has been set to auto commit. Do not share non auto committing connections unless you use some other mechanism to make sure that you don't end up merging different transactions into one - with consequences for committing and roll backs. An appropriate mechanism would be connection pooling: an advanced topic that is not discussed in this tutorial.
      </para>
    </sect2>

    <sect2 id="jdbc-basic-exceptions-and-warnings"><title>Exceptions and Warnings</title>
      <para>
        <emphasis>SQLExceptions</emphasis>, thrown when something goes wrong in JDBC processing, has a slight peculiarity compared to other types of exceptions. When an <emphasis>SQLException</emphasis> is caught, it may have a chain of further <emphasis>SQLExceptions</emphasis> attached to it. This is because a number of problems may occur as part of one overall problem and you may need information about all of them to handle it correctly. To process the chain of exceptions, you should have code similar to the following:
      </para>

      <programlisting>
try
{

 // do some JDBC calls
}
catch (SQLException e)
{
 do

System.out.println(e.getMessage()) ;
 while ((e = e.getNextException()) != null) ;
}

catch (Exception e)

{

 // handle non-SQLException exceptions

}
      </programlisting>

      <para>
        There is another object related to exceptions that can give information about various kinds of problems: this is an <emphasis>SQLWarning</emphasis>. They are not exceptions and do not interrupt the normal flow of control but a <emphasis>Connection</emphasis>, <emphasis>Statement</emphasis> or <emphasis>ResultSet</emphasis> object can be queried for warnings after they have been used. The most common type of warning is a <emphasis>Data Truncation</emphasis> error. Otherwise many programmers don't query them at all.
      </para>
    </sect2>

    <sect2 id="jdbc-basic-sample-database"><title>Sample Database</title>
      <para>
        The database we use for the sample program and the exercise is a (seriously) simplified version of a Microsoft Access database suitable for handling on-line book orders. We omit details that are necessary in practice but do not add anything qualitative to the programming exercises. Thus, for example, there is no information kept about authors, ISBN, delivery charges or options, etc.
      </para>

      <para>The following is a picture of the relationship structure showing the tables, columns and attributes:</para>

      <mediaobject>
        <imageobject>
          <imagedata fileref="ShopRels.png" format="PNG" align="center"/>
        </imageobject>
        <textobject><phrase>A picture of the relationship structure showing the tables, columns and attributes:</phrase></textobject>
      </mediaobject>

      <para>By way of explanation:</para>

      <itemizedlist>
        <listitem><para>A Customer can have many Orders.</para></listitem>

        <listitem><para>An Order can have many OrderDetail records.</para></listitem>

        <listitem><para>Each OrderDetail record refers to precisely one Book.</para></listitem>

        <listitem>
          <para>
            All the primary keys ID fields (in each case the first column name in the corresponding table in the figure above) are AutoNum generated integers and therefore the primary key of each table does not need to be included when inserting new records.
         </para>
        </listitem>

        <listitem>
          <para>
            Referential integrity rules are maintained on all foreign keys (ID fields which not in the first position in the tables of the figure above). Hence, for example, it will cause an error to try to insert an Order record with an Orders.CustomerID for which there is no corresponding Customers record.
          </para>
        </listitem>

        <listitem><para>OrderDetail.Quantity is an integer.</para></listitem>

        <listitem>
          <para>
            Books.BookPrice is a double (it should really be Currency which is MSAcess's equivalent to SQL's Numeric, which in turn is handled in Java via the java.math.BigDecimal class. However, since that adds an extra level of complication that does not contribute anything significant to the exercise, it is left as double here).
          </para>
        </listitem>

        <listitem>
          <para>
            <emphasis>CustomerFirstName</emphasis>, <emphasis>CustomerLastName</emphasis> <emphasis>CustomerAddress</emphasis>, and <emphasis>BookName</emphasis> are Strings.
          </para>
        </listitem>

        <listitem>
          <para>
            Orders.OrderDate is a Date/Time object in MSAccess which is handled as a JDBC TIMESTAMP object which in turn is handled as a <emphasis>java.sql.Timestamp</emphasis> object.
          </para>
        </listitem>
      </itemizedlist>

      <para><emphasis>Timestamp</emphasis> objects can be set to the current time as follows:</para>

      <programlisting>
long millisecs = System.currentTimeMillis() ;
Timestamp ts = new java.sql.Timestamp(millisecs) ;</programlisting>
      
      <para>They can be set to a specific value using the <emphasis>valueOf</emphasis> method with a string argument:</para>

      <programlisting>ts = Timestamp.valueOf(&quot;2001-07-06 14:25:29.9&quot;) ;</programlisting>

      <para>
        As should be expected, <emphasis>ResultSet</emphasis> has the usual <emphasis>getTimestamp</emphasis>, <emphasis>putTimestamp</emphasis> and <emphasis>updateTimestamp</emphasis> methods and <emphasis>PreparedStatement</emphasis> has a <emphasis>setTimestamp</emphasis> method.
      </para>

      <para>
        <emphasis>BigDecimal</emphasis> works rather similarly, see the online API documentation for details.
      </para>
    </sect2>
    
    <sect2 id="jdbc-basic-sample-program"><title>Sample JDBC Program</title>
      <para>
        The following program shows a full program using each of the features discussed above: <ulink url="files/Customer.java">Customer.java</ulink> You also need the <ulink url="files/Database.Properties">Database.Properties</ulink> file and the <ulink url="files/Shop.mdb">Shop.mdb</ulink> database in the same directory. Finally, you need to create an entry in your <emphasis>ODBC data sources</emphasis> (from the control panel) for Shop.mdb
      </para>
    </sect2>

    <sect2 id="jdbc-basic-exercises"><title>JDBC Exercises</title>
      <orderedlist>
        <listitem>
          <para>List books application</para>
          <orderedlist>
            <listitem>
              <para>Write an application that takes a string from the command line and lists book details for books whose titles start with this string.</para>
            </listitem>
          </orderedlist>
        </listitem>

        <listitem>
          <para>Add books to an order application</para>
          <orderedlist>
            <listitem>
              <para>Write an application that takes a customer firstname, a customer lastname and a book title prefix from the command line.</para>
            </listitem>

            <listitem>
              <para>It should check that the customer exists in in database and exit with an error message otherwise.</para>
            </listitem>

            <listitem>
              <para>If there is no open order for that customer then a new open order is created.</para>
            </listitem>

            <listitem>
              <para>All books that match the book title prefix should be added to the open order.</para>
            </listitem>

            <listitem>
              <para>Print to <emphasis>System.out</emphasis> a listing of the customer's details and the customer's open order.</para>
            </listitem>

            <listitem>
              <para>
                (Optional) Rather than simply adding new OrderDetail records for each matching book to the existing open order, simply increment the Quantity for OrderDetail records that already refer to the book(s) being added and only add new OrderDetail records for books that do not already appear in the open order
              </para>
            </listitem>
          </orderedlist>
        </listitem>
      </orderedlist>
    </sect2>
  </sect1>
</article>

