Chapter 4. Java Database Connectivity

  • Inside JDBC

  • Databases and SQL

  • Retrieving Information

  • Storing Information

  • A JDBC Version of the Featured App

Today, nearly all companies choose to store their vast quantities of information in large repositories of data. These databases are vital to the dissemination of information via the Internet. Java, as the anointed Internet language, answers the need to connect information storage to application servers using the Java Database Connectivity framework.

As we will see in these next few chapters, JDBC is a core set of APIs that enables Java applications to connect to industry standard and proprietary database management systems. Using JDBC, your applications can retrieve and store information using Structured Query Language statements as well as a database engine itself. Included in this chapter is a brief introduction to SQL and its merits.

Inside JDBC

The guidelines for creating the JDBC architecture all center on one very important characteristic—simplicity. Databases are complex beasts, and companies that rely on them generally have an army of personnel ready to administer and program them. As a result, transferring that complexity to Java via JDBC would violate the ethos of the language. Therefore, the JDBC architects developed the specification with the idea that database access would not require advanced degrees and years of training to accomplish.

Knowing full well that there are a plethora of databases in existence today, the architectural challenge for JDBC was to provide a simple front-end interface for connecting with even the most complex of databases. To the programmer, the interface to a database should be the same regardless of the kind of database to which you want to connect. Figure 4-1 shows the 50,000-foot view of our JDBC application model.

Basic JDBC application architecture.

Figure 4-1. Basic JDBC application architecture.

Database Drivers

In the world of distributed computing it is easier to understand databases if we think of them as devices rather than software. First of all, we usually install databases on separate machines that are network accessible, and second, we almost always access the database through a standardized driver rather than using native interfaces. If we think of our database as a device, the idea of a driver makes more sense due mainly to our preconceived ideas (and experiences) with having to install device drivers every time we want to add a new card or peripheral device to our workstation.

Standardized drivers for databases came about in much the same way that many other ad hoc standards get developed; in the case of databases, Microsoft developed Open Database Connectivity as a standard for Windows applications to connect to and use Microsoft databases. ODBC became so popular so fast that other database vendors saw the writing on the wall for proprietary APIs and databases whose interface was based on proprietary APIs that they quickly came out with ODBC drivers for their databases. This allowed anyone's database to be accessed from a Windows application in exactly the same way that a Microsoft database would be accessed. ODBC was designed into Windows, and the coupling between it and Microsoft databases was extremely tight and performance-oriented. Other database vendors took a slightly different approach to ODBC; they built an ODBC interface that then translated ODBC into their native API calls. This puts an extra layer between the application and the database. This type of driver is the reason that ODBC has gotten a bad rap on some database platforms.

JDBC takes a number of approaches to database connectivity, and it is important to remember that JDBC is really a published standard interface to databases similar to ODBC. There are currently four common approaches to database connectivity each with a corresponding driver type.

Type 1 Drivers.

The JDBC-ODBC bridge driver takes the simple approach of translating JDBC calls to equivalent ODBC calls and then letting ODBC do all the work. Drivers of this type require that an ODBC driver also be installed on each workstation and that some proprietary libraries (Vendor APIs) that help with the JDBC to ODBC conversion must also be installed. Although effective, these drivers provide relatively low performance due to the extra software layer(s). This driver is handy for putting together application prototypes for "early on" customer demonstrations; because you do not have to install a full blown relational database management system, this is one place where MS Access is a perfectly fine tool. There is a caveat with using MS Access databases: Always remember that an .mdb file is just that, a file (not a database management system). The ODBC driver makes .mdb files appear to be database management systems. Now here is the caveat, the ODBC drive must be able to find the .mdb file on a mapped drive (i.e., the .mdb file can be anywhere on your LAN that the ODBC driver [on the machine] you are using as your data server can find via a mapped drive). This means that, if the database is on a machine that only has TCP/IP connectivity, you are out of luck. This also means that, if you are a UNIX user, you are normally out of luck and must resort to using RDBMs even for prototypes. See Figure 4-2 for an architectural view of a type one driver application.

Type 1 JDBC/ODBC bridge.

Figure 4-2. Type 1 JDBC/ODBC bridge.

In the case of Microsoft databases like Access and SQL Server, which are designed around ODBC, the ODBC driver to database connection is direct and the only extra layer involved is the conversion from JDBC to ODBC. In the case of other vendors' databases that have their own native APIs, there can be an additional conversion from ODBC to the vendor's native API.

An additional thing we need to remember when programming for the Enterprise is that, in the case of Java applets, an applet can only make a network connection back to the machine (IP address) that it was served from. This requires that our database be running on the same machine as our Web server. This could have some serious implications from the standpoint of overall performance for a busy Web site. In most cases, the best solution to this problem is to not use a type 1 driver. Instead, use another driver type and pick a three-tier architecture rather than the two-tier approach of the type 1 driver.

Type 2 Drivers.

Drivers in this category typically provide a partial Java, partial native API interface to the database. Typical of this type of driver is the driver provided by IBM for its DB2 Universal Database (UDB). UDB provides a native driver in the form of the DB2 Client Enabler (CAE), which must be installed on each client machine. The CAE installs a rather elaborate set of driver software that allows access to any DB2 database to which the client machine has network connectivity. Along with the CAE comes a JDBC driver. The JDBC driver is placed in your virtual machine's CLASSPATH. Once loaded by the JDBC Driver Manager and a database connection is established your application has a fairly high-performance pipe to the database. Figure 4-3 illustrates this architecture.

Type 2 DB2 JDBC driver.

Figure 4-3. Type 2 DB2 JDBC driver.

DB2 (and most other modern databases) can be configured to do connection pooling at the database; this doesn't really constitute a three-tier solution, it is still a two-tier (maybe pseudo three-tier) solution.

Type 3 Drivers.

Drivers of this type are usually called network protocol drivers and convert the JDBC calls into a database independent protocol that is transmitted to a middleware server that translates the network protocol into the correct native protocol for the target database. The middleware server is usually run on an independent, high-performance machine and has the ability to convert the network protocol to the required native protocols for a number of different database vendors' products. It also is the JDBC driver source for the client driver manager. The middle tier usually uses a type 1 or 2 driver for its connectivity to the database. Because many databases are good places to store and retrieve information (but are poor connection managers), the middle-tier server often has the job of being a connection manager for the databases (i.e., when started up, a number of database connections are established and held open; the middleware then acts as a router, routing database transactions to already open database connections). The beauty of this is that the end user never incurs the penalty of establishing the connection (which is considerable) to the database. Figure 4-4 illustrates this architecture.

Type 3 driver.

Figure 4-4. Type 3 driver.

Type 4 Drivers.

Last but not least is the all Java, type 4 driver (see Figure 4-5). These drivers require no special software to be installed on client machines and are typically provided by database vendors or vendors like Intersolv and Hit Software that specialize in database drivers. Solutions that use type 4 drivers are typically two-tier, but with the connection pooling that most databases currently provide we have that previously mentioned pseudo three-tier architecture. These drivers are perfect for applet-based clients as everything required by the client is self-contained in the client download from the Web server.

Type 4 driver.

Figure 4-5. Type 4 driver.

In the desktop world, a driver enables a particular piece of hardware to interface with the rest of the machine. Similarly, a database driver gives JDBC a means to communicate with a database. Perhaps written in some form of native code but usually written in Java itself, the database drivers available for JDBC are wide and varied, addressing several different kinds of databases.

The JDBC API is available for users as part of the JDK. The JDBCODBC bridge is supplied as part of the JDK; other drivers are available from the database vendors or driver specialty companies.

The DriverManager Object.

At the heart of JDBC lies the DriverManager. Once a driver is installed, you need to load it into your Java object by using the DriverManager. It groups drivers together so that multiple databases can be accessed from within the same Java object. It provides a common interface to a JDBC driver object without having to delve into the internals of the database itself.

The driver is responsible for creating and implementing the Connection, Statement, and ResultSet objects for the specific database, and the DriverManager then is able to acquire those object implementations for itself. In so doing, applications that are written using the DriverManager are isolated from the implementation details of databases, as well as from future enhancements and changes to the implementation itself, as you can see in Figure 4-6.

The Driver abstracts the connection, statement, and ResultSet objects from the application.

Figure 4-6. The Driver abstracts the connection, statement, and ResultSet objects from the application.

Database Connection Interface.

The Connection object is responsible for establishing the link between the Database Management System and the Java application. By abstracting it from the DriverManager, the driver can isolate the database from specific parts of the implementation. It also enables the programmer to select the proper driver for the required application.

The Connection.getConnection method accepts a URL and enables the JDBC object to use different drivers depending on the situation, isolates applets from connection-related information, and gives the application a means by which to specify the specific database to which it should connect. The URL takes the form of jdbc:<subprotocol>:<subname>. The subprotocol is a kind of connectivity to the database, along the lines of ODBC, which we shall discuss in a moment. The subname depends on the subprotocol but usually allows you to configure the database that the application will look at.

Database Statement Object.

A Statement encapsulates a query written in Structured Query Language and enables the JDBC object to compose a series of steps to look up information in a database. Using a Connection, the Statement can be forwarded to the database and obtain a ResultSet.

ResultSet Access Control.

A ResultSet is a container for a series of rows and columns acquired from a Statement call. Using the ResultSet's iterator routines, the JDBC object can step through each row in the result set. Individual column fields can be retrieved using the get methods within the ResultSet. Columns may be specified by their field name or by their index.

JDBC and ODBC.

In many ways, Open Database Connectivity (ODBC) was a precursor to all that JDBC is intended to accomplish. It adequately abstracts the boring tedium of databases, and the proprietary APIs to those databases, from the application programmer; it ties many different kinds of databases together so that you only have to create one source file to access them; and it is fairly ubiquitous. Recognizing the relative acceptance of ODBC technology, JDBC offers a JDBC-to-ODBC driver free with the JDK.

With this, JDBC applications can talk to the same database access engine as non-Java applications. Furthermore, integrating JDBC into your existing business process can be done fairly easily because the bridge ensures that no additional work is required to enable Java Database Connectivity.

Note

Because of copyright restrictions, we are unable to supply these drivers on the CD-ROM, but you may visit the JDBC page on the JavaSoft Web site at java.sun.com/jdbc and get the latest information on drivers and the pointers to them.

As you can see, the JDBC application communicates with the database using the same existing OLE or COM protocol. Furthermore, any administration issues associated with the database are negligible because the existing administration strategy is still applicable. Application programmers need know only that the ODBC bridge will be used and that they should not tailor their application to it.

Installing the ODBC driver for Windows will be discussed in the next section. Because it is a Microsoft product, the process is easy, but the reliability is in doubt. Keep in mind that most mission-critical applications are run using heavy-duty, workstation-based databases. These databases are expensive and difficult to administer but they are more reliable than a Microsoft Access solution. In any event, we will show you how to write applications tailored for Microsoft because the general computing populace, and more importantly the audience of this book, will not necessarily have access to database servers like Sybase, DB2, or Oracle.

JDBC in General

Java Database Connectivity encapsulates the functionality of databases and abstracts that information from the end user or application programmer. Creating simple JDBC applications requires only minor knowledge of databases, but more complex applications may require intensive training in database administration and programming. For that reason, we have chosen several simple and fun examples to display the power of a Java solution that will more likely than not be used by mission-critical applications.

Databases and SQL

Databases are storage mechanisms for vast quantities of data. An entire segment of the computer industry is devoted to database administration, perhaps hinting that databases are not only complex and difficult but also best left to professionals. Because of this level of difficulty and of our desire to get you started in linking Java to databases, we have chosen to implement a widely available, easily administered, and simply installed database. Microsoft Access can be purchased at your local software retailer. If you want to get started, it's a good place to start. From there, you can move on to more complex databases such as Oracle and Sybase.

In this section, we intend to introduce and create a simple database. In the next section, you will create a simple Java client that accesses the database and gets information from it. We suggest that further exploration into JDBC be preceded by a serious investigation into SQL (any of the currently available texts on Relational Database Management Systems will suffice; check Amazon.com for currently available texts). The Structured Query Language enables you to create powerful instructions to access databases. Once you grasp SQL, you will be able to understand the reasoning and theories behind JDBC.

Creating an Access Database

We will need to first start Microsoft Access so that we can create a database to talk to. This is an important step, but one that those who either do not have access to or who do not wish to use Microsoft's database can tailor for their own database. After starting Access:

  1. Select "Database Wizard" so Access will help you create a database.

  2. Select the "Blank Database" icon.

  3. Name the database and then you will get a series of tabbed folders. Go to "Tables" and click on "New."

  4. You will get a spreadsheet-like view in which you can enter your data.

  5. Enter your data as shown in Figure 4-7 and then select "Save" to store the table to the database. Name your table PresidentialCandidate.

    Our database entry.

    Figure 4-7. Our database entry.

As you can see in Figure 4-7, we entered the important statistics from the last presidential election. The percentage is stored as a whole number, not as a decimal. This allows the application to determine how it will represent the information. We also store the electoral votes that each candidate received.

Simple SQL

Now that we've put the statistical data about the candidates into our database table, we can use Access to help us design the queries that we will need for our GUI. To do so, we need to know a little bit of SQL. This is by no means intended to be the be-all and end-all of SQL tutorials. This is a Java book, and as such we will minimize our discussion of SQL. Suffice it to say that, for a programming language that has no program control statements and is completely declarative, it is extremely powerful.

The most often used instruction in SQL is the Select statement. Select enables you to retrieve a copy of specific portions of a database table. As part of the Select statement, you must specify both the database table from which you want the information and a filter for the information (if required). So, when you Select From a table Where the parameters match your requirements, you get a result back.

SELECT column list FROM myTable WHERE filter

The Where clause of the Select statement may contain what is known as a filter. Filters are specified as conditionals and enable you to further tailor the match parameters for a database query. In a moment, we will query a database table for all the presidential candidates who received electoral votes in the 1996 election. From a field of three candidates, we will end up with two. Big party politics aside, our query will return a result based on the parameters we specify.

In theory, that result always will be a database table of its own. For example, given the following table of presidential election results and the accompanying SQL statement, we will receive a table in return (see Figure 4-8).

SQL statement can be made to return entire tables.

Figure 4-8. SQL statement can be made to return entire tables.

This table is like a local variable. It disappears from memory if we don't use it right away. Using JDBC, this results table is saved for us to retrieve the results data from an object called a ResultSet, which will go away (be garbage collected) when the object goes out of scope. We could just as easily include this SQL statement within another SQL statement and achieve predictable results. These are called subqueries and are another powerful tool of which SQL programmers can take advantage.

The beauty of SQL is its simplicity. Obviously, a language of such great importance has several nuances that database experts have long known, but it is still fairly easy to start writing SQL statements, as we will discover in this chapter.

Generating SQL.

In order to create the necessary queries for our Access data, we must do the following steps. This will let us call these super queries rather than being forced to specify SQL in our Java code. There are advantages and disadvantages to this approach, which we will discuss in a moment.

  1. Select the "Queries" tab in the main database view.

  2. Select "New."

  3. Select "Design View."

  4. Immediately select "Close" in the "Show Table" view.

  5. Go to the "Query" menu and select "SQL Specific" and then "Union."

Now we are presented with a little text input area in which we can enter our query. Using the limited amount of information we have just learned, we must create three queries, one for each candidate, that will retrieve the important statistics for us. We have shown the ClintonQuery in Figure 4-9, and you can see what your database will look like when all three queries are completed.

Getting statistics on Bill Clinton from the database.

Figure 4-9. Getting statistics on Bill Clinton from the database.

Note that we have limited the number of queries. You could just as easily create more complex queries, and if you know SQL pretty well, we encourage you to do so. Otherwise, it is probably best to get this "proof of concept" example down pat before proceeding.

Introduction to the ODBC Driver.

Once the database is completed, we must make it available via the database server. To do so, we must edit the ODBC Control Panel. The ODBC Control Panel assigns our database to the driver, allowing invocations on the database to pass through the driver right into the database. Unless the database is made public to the ODBC driver, this cannot happen because the system will not know about the database's existence.

To assign the database to the driver, select the driver Control Panel. The Control Panel should have been installed with Access. If it is not there, check your Microsoft Access installation instructions. Inside the Control Panel, select "MS Access 7.0" and "Select" the proper database from within the Setup dialog box (see Figure 4-10).

Starting the ODBC driver.

Figure 4-10. Starting the ODBC driver.

Once completed, the ODBC driver will be aware of the database you have created and await invocations on it. As long as the incoming queries specify the "ElectionDatabase" database, they will be dispatched to the database and from there to our SQL queries.

Summary

ODBC is a proprietary database management protocol. It enables you to access information on databases from within Microsoft Windows. Once ODBC is set up on our machine, we can get information from the database by creating Java applications that interface to it.

One thing to make sure that you remember when using Access like this is that Access is a personal database and not a full blown relational database management system like Oracle, DB2, and SQL Server. The mdb file that Access uses as its data storage element is just a file, it is the ODBC driver that makes it look and act like a database.

Retrieving Information

At first glance, you probably wonder where the "server" part for this section is. Well, we created it when we created our database! The database is the server. The beauty of JDBC clients is that they link directly with databases. In client-server terms, this is referred to as a "two-tier" model in which the client is the first tier and the database itself is the second tier. In the rest of the book we will promote the three-tier model in which clients do nothing but look good and interface with servers. The servers contain all the business logic, and the databases only store data.

In JDBC and the two-tier model, the client contains all the business logic and is responsible for contacting and accessing the database. Our JDBC client uses SQL queries to contact our Microsoft Access database. The downside to this is the complexity of the client and the scalability of the system. With potentially hundreds of clients banging on the same database, the database could get overloaded. With a three-tier model, the database is queried by only one application—the server—and the server is responsible for (and is more capable of) handling hundreds of simultaneous requests.

In any event, we will present you with a more thorough look at the advantages and disadvantages of the two- and three-tier models in Chapter 14, "Making an Architectural Decision." In this section, we show you how to go to a database and get information. In the next section, we will show you how to put information into the database.

Creating the User Interface

Our user interface should be simple and elegant. Once again, we don't want to confuse people with what we are trying to do. We will create a button for each candidate. Upon activation of the button, the client will execute SQL statements on the server and get information. Then it will display the information in the text fields provided. See Figure 4-11 for a sample GUI.

Sample GUI for the PresidentialElection application.

Figure 4-11. Sample GUI for the PresidentialElection application.

The user interface will enable us to underscore the simplicity of JDBC. We have seen how it can handle the most complex of cases, but here we once again keep our examples fun and easy.

As we proceed, we will show you how to implement the two important functions in this application. The PresidentialElection application's constructor and its corresponding action method will initialize and invoke the database, respectively.

public class PresidentialElection
{
    Button clintonButton;
    Button doleButton;
    Button perotButton;

    TextField popularField;
    TextField electoralField;

    PresidentialElection()
    {
        // create the user interface here
    }
    public void actionPerformed(ActionEvent e)
    {
        String arg = e.getActionCommand();
        if(arg.equals("Clinton"))
        {
        }
        else if(arg.equals("Dole"))
        {
        }
        else if(arg.equals("Perot"))
        {
        }
    }
}

Database Security

Because we are writing an application, handling our own security is not a requirement. However, if we wanted to write an applet, we would need to use a Security-Manager to set our access to the host database. Because Java applets are able to connect only to their host machine, our security manager is required to make sure we have access to the database on the host machine. By setting the security manager, you can check to see if you have access to the database before a query is executed. Keep in mind that the security manager deals with security as it relates to Java. Database access security (userid and password) is handled through the instantiation of the Connection object.

Using the JDBC Driver

As we discussed earlier, we must include the JDBC driver in our application. To do so, we obtain a Connection object from DriverManager. The DriverManager takes a URL and translates it into a handle for an actual database. Then we can invoke our SQL statements on the database and retrieve information. From the Connection object, we can retrieve Statement, PreparedStatement, and CallableStatement objects to help us format our SQL queries.

As JDBC gains more acceptance, database vendors will provide drivers for Java applications to use to contact their databases. Often, there will be some overlap between these different drivers. Choosing the proper driver can be a difficult task, but JDBC enables you to create a colon-separated list of drivers through which JDBC will search for the first available driver.

Here, we will use the standard ODBC driver included with JDBC. This will enable us to connect to ODBC databases such as the Microsoft Access database we just created. As long as our ODBC driver has been set up to await this kind of query, this will succeed. We will need to load the specific class for the database "manually."

import java.sql.*;

public class PresidentialElection
{
    Button clintonButton;
    Button doleButton;
    Button perotButton;

    TextField popularField;
    TextField electoralField;

    // the connection to the database
    Connection dbConnection;

    PresidentialElection()
    {
        // create the user interface here

        // create the URL representation of our database
        String url = "jdbc:odbc:PresidentialCandidate";

        // load the database driver
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        // make the connection to the database
        dbConnection = DriverManager.getConnection(
            url, "username" "password");
}
    public void actionPerformed(ActionEvent 2)
    {
        String arg = e.getActionCommand());
        if(arg.equals("Clinton"))
        {
        }
        else if(arg.equals("Dole"))
        {
        }
        else if(arg.equals("Perot"))
        {
        }
    }
}

After we created the URL representation for our database, we needed to connect to the database itself. Once that is done our application is linked to the database and can make invocations at will.

Creating Queries

Now, we must fill in the actionPerformed so that we can make the query on the database. Here, we will specify the SQL query right in the executeQuery method. We could also do this by just executing the queries we created and stored in the database itself. Since we may be beginners with JDBC, I think it is more meaningful to start out showing the queries along with the code.

import java.sql.*;

public class PresidentialElection
{
    Button clintonButton;
    Button doleButton;
    Button perotButton;

    TextField popularField;
    TextField electoralField;

    GubernatorialElection()
    {
        // create the user interface here

        // create the URL representation of our database
        String url = "jdbc:odbc:GubernatorialCandidate";

        // load the database driver
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        // make the connection to the database
        Connection connection = DriverManager.getConnection(
            url, "username" "password");
}
    public void actionPerformed(ActionEvent e)
    {
        String who = new String("");
        String arg = e.getActionCommand();
        if(arg.equals("Clinton"))    who = "Clinton";
        else if(arg.equals("Dole"))  who = "Dole";
        else if(arg.equals("Perot")) who = "Perot";
        else System.out.println("Error");
        try
        {
          Statement statement = dbConnection.createStatement();
          String s = "select percentage,electoral " +
                     "from PresidentialCandidate " +
                     "where candidate='" + who +"'";
          ResultSet result = statement.executeQuery(s);
          popularField.setText(who + " " + result.getInt(1));
          electoralField.setText(who + " " + result.getInt(2));
    }
    catch (SQLException se)
    {
          System.out.println("SQLError: " + se.toString());
     }
    }
}

In place of the Select statement, we could just as easily have executed the query that we had earlier stored in the database. As already mentioned, we choose not to help improve the learning experience.

Database and SQL Overview

Once we are able to interface with the database, we should be able to put information in it. Databases are not static entities. They are ever changing, and in keeping with that trait, Java provides some pretty cool tools to get to databases and change the data stored therein.

Storing Information

JDBC also gives you a means to store information in a table. Once again, this is done using standard Structured Query Language statements. By using SQL, JDBC makes sure that its own learning curve is pretty small. JDBC gives you much flexibility in creating statements.

Let's say that suddenly we discover that Bill Clinton is really Daffy Duck! The 700,000 people who wrote in "Daffy Duck" on their ballot as their choice for President of the United States really voted for Bill Clinton. As a result, the percentage by which Bill Clinton won the 1996 election changed. We need to create a JDBC query to modify the percentage.

Creating the Connection

The first thing we must do is create the connection as we did before. We will also add a button to change the percentage of votes for Bill Clinton. We could with a slight bit more complication and effort create a more customizable change area. It could have text fields for each entry and a submit button. Using the data in the text field, we could change the data in the table. For now, however, that is more complex than is needed.

public class PresidentialElection
{
    Button clintonButton;
    Button doleButton;
    Button perotButton;
    Button changeButton;
    TextField popularField;
    TextField electoralField;

    PresidentialElection()
    {
        // create the user interface here

        // create the URL representation of our database
        String url = "jdbc:odbc:PresidentialCandidate";

        // load the databasedriver
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        // make the connection to the database
        Connection connection = DriverManager.getConnection(
            url, "username" "password");
    }

    public boolean action(
        Event evt,
        Object obj
    )
    {
        if(evt.target == clintonButton)
        {
            . . . same as before . . .
        }
        else if(evt.target == doleButton)
        {
            . . . same as before . . .
        }
        else if(evt.target == perotButton)
        {
            . . . same as before . . .
        }
        else if(evt.target == changeButton)
        {
        }
    }
}

We also needed to insert event-handling information for the new button. As you can see, there is no change between the connection for retrieving information and the connection here for setting the information.

Forming a Statement

The burden, in JDBC, is placed on the formation of statements. As database programmers expect, there is no need to learn anything new or confusing. Java is treated as nothing more than a container for an SQL statement. The SQL statements we create here as well as when we stored information are nothing fancy, nothing special, and no more interesting than a normal SQL statement.

In order to change the information in a database, we need to use the SQL Update statement. We must specify a column and row to change. But, instead of encapsulating the SQL statement with a regular JDBC statement, instead we will use a PreparedStatement. PreparedStatements give you the ability to insert parameters within the statement itself. The following example contains two parameters, popularvote and candidate:

UPDATE PresidentialCandidate
SET popularvote = ?
WHERE candidate = ?

The popularvote field is marked as field number one, and candidate is field number two. To set the fields, we use the set methods supplied with JDBC along with the number of the field you want to change: setInt, setString, etc. To define the fields, use the question mark.

Now we can create a PreparedStatement. Note, however, that in this chapter we are not using precreated queries. Instead, we will create the query directly from JDBC. As we discussed earlier, either approach is completely acceptable. The choice is not one of effort but rather of programming approach. If your business makes heavy use of precreated queries, obviously you will choose to invoke them from JDBC. If database interaction is not as important, then there is really no need to define queries ahead of time.

public class PresidentialElection
{
    Button clintonButton;
    Button doleButton;
    Button perotButton;
    Button changeButton;
    TextField popularField;
    TextField electoralField;

    PresidentialElection()
    {
        // create the user interface here

        // create the URL representation of our database
        String url = "jdbc:odbc:PresidentialCandidate";

        // load the database driver
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        // make the connection to the database
        Connection connection = DriverManager.getConnection(
            url, "username" "password");
    }

    public boolean actionPerformed(ActionEvent e)
    {
        String arg = e.getActionCommand();
        if(arg.equals("Clinton"))
        {
            . . . same as before . . .
        }
        else if(arg.equals("Dole"))
        {
            . . . same as before . . .
        }
        else if(arg.equals("Perot"))
        {
            . . . same as before . . .
        }
        else if(arg.equals("Change"))
        {
            // create the statement
            PreparedStatement pstate =
                connection.prepareStatement(
                    "UPDATE PresidentialCandidate " +
                    "SET popularvote = ? " +
                    "WHERE candidate = ?");
            // set the parameters for the statement
            pstate.setInt(1, 50);
            pstate.setString(2, "Clinton");

            // execute the statement
            pstate.executeUpdate();
        }
    }
}

Now that we can create a simple system of clients that change information in a database, we can try to create a client for our featured application that will store and retrieve its information from a similar database. By creating this purely two-tier model, it can be compared to the applications we created in Chapter 3 for using sockets.

A JDBC Version of the Featured App

As we have seen in the previous sections, creating a JDBC interface to a database is fairly easy. The difficult parts involve setting up the database and installing the driver. Although we won't discuss the finer points of drivers or database administration, we will create the database as well as the interface to it.

Creating the Database

Once again, we will create the database and associated queries using Microsoft Access as shown in Figure 4-12. As before, our decision to use Access is due largely to its ubiquity and ease of use. We want you to be able to create interfaces to databases quickly and easily, and Microsoft Access provides a simple means to do so. As with all third-party products, if you require assistance with Access, contact Microsoft. In any event, if you have access to Sybase, Oracle, or another database, feel free to use it. You should not have to modify the code, but you will have to install a driver for the database you plan to use.

Our new database.

Figure 4-12. Our new database.

We will store our appointments in the database using two keys—the reason for the appointment and the time of the appointment. This conforms to the interface to the Network module. In a moment we will discuss how to map the network module to the database. For now, take a look at the Access table in Figure 4-13.

Table for our appointments.

Figure 4-13. Table for our appointments.

We must create our query either within the database or within our program (NetworkModule) to get appointments to the client interface. My preference, for the purpose of this text, is to code it into the program. This isn't as efficient from the standpoint of execution performance, but it keeps everything in the NetworkModule.

SELECT TIME,REASON
FROM SCHEDULE
ORDER BY TIME

We'll use the ORDER BY clause of the SELECT statement to get our appointments for the morning, afternoon, and evening grouped together like a real appointment scheduler would.

Mapping the Network Module to Database Queries

Now we need to connect the network module to the database. Remember that a driver must be installed for the database. Without it the database access queries cannot function. Our network module's interface looks like this:

public class NetworkModule
{
    public void scheduleAppointment(String reason,int time);
    public Vector getAppointments();
    public void initNetwork();
    public void shutdownNetwork();
}
					

Obviously, we will map the scheduleAppointment method to a PreparedStatement query, but we will map the getAppointment method to the GetAppointments query. We will pass our constructor's code directly to the initNetwork method, and we must close our connection to the database (the shutdownNetwork method is the logical place to include that code).

Developing the Client

The majority of our client application was developed in Chapter 3, leaving us to modify the NetworkModule. Doing so is just as simple as before. Let's first take a look at the network module's code without the modifications. We must incorporate the JDBC classes as well as the classes for the driver.

import java.sql.*;

public class NetworkModule
{

    NetworkModule()
    {
    }

    public void scheduleAppointment(
                   String reason, int time)
    {
    }

    public Vector getAppointments()
    {
    }

    public void initNetwork()
    {
    }

    public void shutdownNetwork()
    {
    }
}

Once our client is ready, we must fill in the information for each function. First, we will schedule appointments using the scheduleAppointment query that we created earlier. Essentially, the network module acts as a pass-through from the rest of the application directly to the database. Normally, we would try to incorporate some kind of middleman to handle the pass-through from our GUI to the database, but for simplicity's sake we will not develop a three-tier application here. In the future, if you desire a three-tier application, your middle-tier server would make these calls.

Establishing the Connection

First, we must create the connection to the database and link our network module to it. This ensures that we have a clear path to the database. Any errors here should be caught and thrown back. We also must make sure to load the database driver manually by specifying its entire class name.

import java.sql.*;

public class NetworkModule
{
    // create the connection to the database
    Connection dbConnection;

    NetworkModule()
    {
        // init the network connection to db
        initNetwork();
    }

    public void scheduleAppointment(
                  String reason, int time)
    {
    }

    public Vector getAppointments()
    {
    }

    public void initNetwork()
    {
        // load the database driver
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        // create the URL representation of our database
        String url = "jdbc:odbc:Schedule";
        // make the connection to the database
        dbConnection = DriverManager.getConnection(
            url, "username" "password");
    }

    public void shutdownNetwork()
    {
    }
}

Making an SQL Invocation

Invoking the database is relatively straightforward and not unlike our earlier invocation of the PresidentialElection database. Here, we substitute our own invocation. The difference in this invocation, however, is that we will retrieve a complex type from the SQL query. As a result, we must translate the complex type into the Vector that is expected as a return value for the getAppointments invocation.

import ava.sql.*;

public class NetworkModule
{
    // the connection to the database
    Connection dbConnection;

    NetworkModule()
    {
    // init the network
    initNetwork();
}

public void scheduleAppointment(
              String reason, int time)
{
}

public Vector getAppointments()
{
    // create a vector to pass back
    Vector appointmentVector = new Vector();
    try
    {
       // create the statement
       Statement statement = dbConnection.createStatement();
       String s = "SELECT TIME, REASON " +
                        "FROM SCHEDULE " +
                        "ORDER BY TIME";
       // get the result
       ResultSet result = statement.executeQuery(s));
       // walk through the result set for the information
       while(result.next())
       {
          // create a variable to hold the appointment
          AppointmentType appointment = new AppointmentType();
          // get the next appointment from the results set
          appointment.time = result.getInt("TIME");
          appointment.reason = result.getString();
          appointmentVector.addElement(appointment);
       }
   }
   catch (SQLException e)
   {
       System.out.println("Error: " + e.toString());
   }
}
public void initNetwork()
{
    // load the database driver
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    // create the URL representation of our database
    String url = "jdbc:odbc:Schedule";
    // make the connection to the database
    dbConnection = DriverManager.getConnection(
        url, "username" "password");
}
    public void shutdownNetwork()
    {
    }
}

Notice how the current invocation steps through the ResultSet and makes it into a Vector. When your applications need to handle more complex results from an SQL query, you will need to do much of the same.

Invoking SQL to Make a Change

Now we must implement the Java side to our setAppointment operation. Our setAppointment query assigns a new entry into the database.

public void scheduleAppointment(
              String reason, int time)
{
}

We must first take the reason and time variables and translate them into an SQL statement. Unlike our previous database modification example, here we must insert an element, not simply change an existing one. To do so, we need to use the SQL Insert statement.

INSERT INTO Schedule
VALUES (1, 'Meet with marketing'),

We will once again use the PreparedStatement object to put together a statement.

public void scheduleAppointment(
              String reason, int time)
{
  try
  {
   Statement insertStatement = dbConnection.createStatement();
   String insert = "INSERT INTO SCHEDULE " +
                     "VALUES('" + appointmentTime + "','"
                                + appointmentReason + "')";
   insertStatement.executeUpdate(insert);
}

Shutting Down the Connection

In JDBC, we must close the connection to our database. This ensures that the database management system has sufficient connections for other applications to connect to it. For high-availability databases, this is quite an important characteristic. The database must be available at all times, and even though our connection disappears when the application shuts down, we must still publish an interface to the database connection that will allow us to eliminate it.

public void shutdownNetwork()
{
}

Summary

Databases are storage mechanisms designed to enable you to warehouse vast quantities of data. By linking Java applications to them, you can create programs that are instantly useful. Today, there are hundreds of applications that interface with databases using outdated, archaic applications.

In the next two chapters we will explore combining Java, JDBC, and network object technology to develop enterprise class applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset