by Shelley Powers
IN THIS CHAPTER
The Java Database Connectivity (JDBC) API was demonstrated in Chapter 43, "Getting
Started with JDBC," but none of those examples showed how to use the JDBC to
build a database-accessing applet. This chapter explains how to use the JDBC to build
applets; this chapter also covers the restrictions on applets and provides examples
of some typical uses.
This chapter also demonstrates probably the number one reason for using Java development tools such as Symantec Café or Microsoft Visual J++: it's easy to send information to and from the database using the JDBC but it's not so easy using the AWT (Abstract Window Toolkit) classes. The development tools automate much of the AWT class use and make it much easier to create database forms as shown in the examples.
Before the release of the JDK version 1.1, accessing databases from an applet that was downloaded from an HTTP server was restricted in two ways: The applet could not access native methods (that is, it could not access nonJava drivers on the client), and the applet could not access a database that was not located on the same network server as the Web server.
Not being able to access native methods--including database drivers written in C or C++--effectively eliminated the use of the JDBC-ODBC bridge for an applet. The restriction on the database location also seriously hampered most professional uses of this technology. Many companies do not have their database and Web server on the same computer--or even in the same physical location.
USING THE JDBC-ODBC BRIDGE WITH VISUAL J++
So, you created a Java applet or application using Visual J++ and the JDBC-ODBC bridge. When you ran it, however, you got a java.lang.UnsatisfiedLinkError. Before you start a long involved process of debugging, note that the first release of the JDBC-ODBC bridge does not work with the first release of Visual J++. Visual J++ implements native methods in a different manner than the JDBC-ODBC bridge and the two techniques clash.How to work around the problem? Compile the application with Visual J++, but run it with the java.exe application that comes with JDK 1.1. Alternatively, compile and run the applet or application with the JDK, just make sure that javac cannot "see" the Microsoft classes, or you will get a compile time error.
Another limitation for applets (rather than a restriction) is that the original JDBC classes were not included as part of the core Java classes. Therefore, they were not included by the major browsers that allow Java applets. Most browsers prohibit downloading classes from a directory that begins with java (to prevent hackers from creating a bad extension of one of the standard Java classes and then overwriting the class in the client when the class is downloaded). Unfortunately, this "security feature" also prevents the downloading of the JDBC classes, turning the original limitation into yet another restriction: the client machine must already have the JDBC classes if any JDBC applet is to work. A workaround that some of the JDBC driver developers created was to copy the JDBC classes to another subdirectory and code their own classes accordingly.
Of course, with the release of the JDK 1.1, several of these restrictions have been either eased or eliminated.
The JDBC classes are now a part of the core Java classes, which means that anyone using a JDK 1.1-compliant browser can have access to the JDBC classes with no additional downloading.
With the new security features in JDK 1.1, an applet can be digitally signed as described in Chapter 16, "The Security Package," and Chapter 34, "Java Security." When an applet is digitally signed, it becomes known as a trusted applet, which means that it is no longer bound (or as tightly bound) by the traditional applet securities. A trusted applet can, for example, access native methods or access a database located somewhere other than on the Web server.
If restrictions are not an issue with an applet, what happens when the applet class is downloaded? When an applet that uses the JDBC is downloaded, nothing unusual happens until it tries to connect to a database. The first bit of code to accomplish that feat is the code to load the JDBC driver:
Class.forName("imaginary.sql.iMsqlDriver");
At this point, the browser or application tries to load the driver classes from the local CLASSPATH. If the driver classes are not found in the designated CLASSPATH, the browser or application attempts to access the driver classes using the ClassLoader(). When ClassLoader() is defined for the implemented object, it accesses the classes it requires from the network as an array of bytes, which are then translated to the necessary classes on the client side.
For example, if an applet is downloaded from a site that uses the mSQL-JDBC driver classes, and the driver is not located on the client, the HTTP log file for the server might look something like this:
usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:23 -0500] "GET /book/sample 5.html HTTP/1.0" 200 212 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:27 -0500] "GET /book/sample 5.class HTTP/1.0" 200 5725 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:35 -0500] "GET /book/imagin ary/sql/iMsqlDriver.class HTTP/1.0" 200 2350 ip3.pdx1.pacifier.com - - [02/Feb/1997:16:56:37 -0500] "GET /pbug/member/company /../index.htm HTTP/1.0" 200 2699 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:38 -0500] "GET /book/imagin ary/sql/iMsqlConnection.class HTTP/1.0" 200 3661 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:47 -0500] "GET /book/imagin ary/sql/msql/MsqlException.class HTTP/1.0" 200 350 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:47 -0500] "GET /book/imagin ary/sql/msql/Msql.class HTTP/1.0" 200 350 usr1-dialup22.seattle.mci.net - - [02/Feb/1997:16:56:47 -0500] "GET /book/imagin ary/sql/iMsqlResultSet.class HTTP/1.0" 200 350
As you can seen, the first access is to the page containing the applet; the second access is to the applet itself; all the accesses that follow are the results of the ClassLoader() pulling in the driver classes.
The advantage of this approach is that the person who accesses the applet does not have to take any heroic efforts to run the applet. The disadvantage, of course, is the additional time it takes to download the classes before the applet can run properly. However, once the classes are downloaded, most browsers cache the classes locally, eliminating the need to download the classes again on the next access.
As you have learned, using the JDBC classes from an applet is no different than using them from a Java application--as long as restrictions are not an issue. For example, to convert some of the examples from Chapter 43, "Getting Started with JDBC," all you have to do is add the applet code to the top of the file (replacing the main() method) and provide an area in which you can access and display information.
The first example in Chapter 43 displayed information from a purchase order table stored in a Sybase SQL Anywhere table. The first step in converting this program is to replace the main() method and to redefine the class to extend the Applet class. Here is the original code:
import java.sql.*; import sun.jdbc.odbc.*; public class sample1 { public static void main(String arg[]) {
Here is the code after it has been converted to an applet:
import java.applet.*; import java.awt.*; import java.awt.event.*; import java.sql.*; public class sample1 extends Applet implements ActionListener
{
Note that the import sections in these examples are different: The code for the applet has to include the Applet class and the AWT class in addition to the JDBC and driver classes.
The init() method is essential for the applet. This method is where the components necessary for accessing or displaying data are created and laid out (see Listing 44.1; the code is also on the CD-ROM that accompanies this book).
// set background // create layout // and place applet components public void init() { // set background color and font setBackground(new Color(255,255,255)); setFont(new Font("Helvetica", Font.PLAIN, 12)); // create layout using GridBagLayout GridBagLayout gridbag = new GridBagLayout(); setLayout(gridbag); GridBagConstraints Constraints = new GridBagConstraints(); // define constraints for objects to be added Constraints.weightx=1.0; Constraints.weighty=0.0; Constraints.insets = new Insets(4, 2, 4, 2); Constraints.anchor=GridBagConstraints.CENTER; Constraints.fill = GridBagConstraints.NONE; Constraints.gridwidth = GridBagConstraints.REMAINDER; // create search button and add searchButton = new Button("Get all POs"); gridbag.setConstraints(searchButton, Constraints); add(searchButton); searchButton.addActionListener(this); // create results text area textResult = new TextArea(7,80); // add with constraints to layout // change Y constraint to take up remainder of // height allocated to applet Constraints.weighty=1.0; gridbag.setConstraints(textResult,Constraints); add(textResult);
}
Now you can see why Java tools are becoming so popular. Connecting to the database and accessing the data are fairly trivial compared to creating the display. (And this example is a fairly simple one!)
After the display is created, you code an event handler that captures the click of the searchButton button created in the init() method. The handler calls a method that finally connects to the database:
// handleEvent // // Overriding handleEvent allows us to // capture the search button push // // call super.handleEvent to allow other // events to be processed correctly public void actionPerformed(ActionEvent event) { if (event.getSource() == searchButton) { processRequest(); }
}
The handler must invoke the ancestor code for all events it does not process directly. The processRequest() method shown in the event-handler code connects to the database, accesses the data, and displays it as shown in Listing 44.2 (the code is also located on the CD-ROM that accompanies this book). The output is literally a direct copy of the original code, except that the query output is displayed in a field rather than to system output.
// processRequest // // This method will load the database driver, // connect to the database, create the retrieval statement // and output the results. private synchronized void processRequest() { String stringResult = ""; String result; try { //connect to ODBC database Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String url = "jdbc:odbc:Zoo"; // connect Connection con = DriverManager.getConnection(url,"dba", "sql"); // create Statement object Statement stmt = con.createStatement(); String sqlselect = "Select company_id, order_dt, po_amount, status_cd" + " from dba.purchase_order"; // run query ResultSet rs = stmt.executeQuery(sqlselect); // process results - could have processed in a loop while(rs.next()) { result = ""; result = rs.getString(1) + "\t"; result+= rs.getString(3) + "\t"; result+= rs.getDate(2) + "\t"; result+= rs.getString(4) + "\n"; stringResult+=result; } textResult.setText(stringResult); // close result set and statement rs.close(); stmt.close(); } // catch all exceptions and print out exception message catch (Exception e) { textResult.setText(e.getMessage()); }
}
What's the main difference between this method and the same processing approach in the original code in Listing 44.1? Aside from using different result set access methods, the difference is in one line:
textResult.setText(stringResult);
compared to:
System.out.println("Values are: " + result);
If you run the applet using the applet viewer utility that comes with the JDK, your results will look similar to those shown in Figure 44.1. Note that to use this utility, you must embed the applet into an HTML file and run that file. The samples in this chapter all come with associated HTML files.
Figure 44.1.
A simple database query applet.
To convert a Java database application to a Java applet, follow these steps:
You can find more information about creating applets in Chapter 8, "Introduction to Applet Programming"; you can find more information about using the interface components of the AWT in Chapter 22, "Creating User Interface Components."
If you want your applet to make a modification to the data in the database, you must provide components for the user to use to input data.
In this section, you create a sample applet that accesses data from the user and updates the retail_item table from the sample Sybase SQL Anywhere database, Zoo, included on the CD-ROM that accompanies this book.
The retail_item table contains the following fields:
Field | Description |
item_nbr | Integer field containing item identifier |
item_desc | Text field containing item description |
qty_per_pkg | Integer field containing the number of items contained in each package sold |
wholesale_cost | Numeric field containing the wholesale cost of the item |
retail_cost | Numeric field containing the retail cost of the item |
company_id | Integer identifier of the company that makes the item |
color | Optional color of the item |
size | Optional size of the item |
company_item_identifier | Not used in this example |
private Button updateButton; private TextField itemNbr; private TextField itemDesc; private TextField itemQty; private TextField itemWholesale; private TextField itemRetail; private TextField itemCompany; private TextField itemColor; private TextField itemSize; private TextField textResult;
The fields are defined as private only because they are not available to any other class; however, they could just as easily have been defined as public.
Next, the components must be created and laid out. The init() method of the sample class looks similar to Listing 44.3 if we limit our AWT layout classes to the BorderLayout and FlowLayout classes. This code can also be found on the accompanying CD-ROM.
// set background // create layout // and place applet components public void init() { // set background color and font setBackground(new Color(255,255,255)); setFont(new Font("Helvetica", Font.PLAIN, 12)); // create borderlayout setLayout(new BorderLayout(5,5)); // create upperpanel Panel upperpanel = new Panel(); upperpanel.setLayout(new BorderLayout()); // create top panel of upper panel Panel uppertoppanel = new Panel(); uppertoppanel.setLayout(new FlowLayout(1,5,5)); uppertoppanel.add(new Label("Item Nbr:")); itemNbr = new TextField("",5); uppertoppanel.add(itemNbr); uppertoppanel.add(new Label("Item Desc:")); itemDesc = new TextField("", 25); uppertoppanel.add(itemDesc); upperpanel.add("North", uppertoppanel); // create bottom panel of upper panel Panel upperbotpanel = new Panel(); upperbotpanel.setLayout(new FlowLayout(1,5,5)); upperbotpanel.add(new Label("Item Qty:")); itemQty = new TextField("",3); upperbotpanel.add(itemQty); upperbotpanel.add(new Label("Item Wholesale Cost:")); itemWholesale = new TextField("", 7); upperbotpanel.add(itemWholesale); upperpanel.add("South", upperbotpanel); // add to main borderlayout add("North", upperpanel); // create middlepanel Panel middlepanel = new Panel(); middlepanel.setLayout(new BorderLayout()); // create top panel of middle panel Panel middletoppanel = new Panel(); middletoppanel.setLayout(new FlowLayout(1,5,5)); middletoppanel.add(new Label("Item Retail:")); itemRetail = new TextField("",7); middletoppanel.add(itemRetail); middletoppanel.add(new Label("Item Company:")); itemCompany = new TextField("", 3); middletoppanel.add(itemCompany); middlepanel.add("North", middletoppanel); // create bottom panel of middle panel Panel middlebotpanel = new Panel(); middlebotpanel.setLayout(new FlowLayout(1,5,5)); middlebotpanel.add(new Label("Item Color:")); itemColor = new TextField("",8); middlebotpanel.add(itemColor); middlebotpanel.add(new Label("Item Size:")); itemSize = new TextField("", 8); middlebotpanel.add(itemSize); middlepanel.add("South", middlebotpanel); add("Center", middlepanel); // add bottom layer Panel botpanel = new Panel(); botpanel.setLayout(new FlowLayout(1,5,5)); updateButton = new Button(" Update "); updateButton.addActionListener(this); botpanel.add(updateButton); textResult = new TextField("", 25); botpanel.add(textResult); add("South", botpanel);
}
The BorderLayout container class allows you to insert components using the geographical place names "North", "South", "East", "West", and "Center". Alternatively, the FlowLayout container class allows you to add components that are placed one after another in a linear manner. Both layout managers are relatively simple to use. In Listing 44.3, notice the use of the panel object. The panel object allows you to implement a different layout container, which is then inserted into another layout container, and so on. This capability allows developers to use a variety of layouts to display components.
Once the layout is created, you create the event handler for the update button:
// handleEvent // // Overriding handleEvent allows us to // capture the search button push // // call super.handleEvent to allow other // events to be processed correctly public void actionPerformed(ActionEvent event) { if (event.getSource() == updateButton) { processRequest(); } }
Finally, the code to process the database update is created, as shown in Listing 44.4. This file can also be found on the accompanying CD-ROM.
// processRequest // // This method will load the database driver, // connect to the database, create the retrieval statement // and output the results. private synchronized void processRequest() { String stringResult = ""; String result; String stringNbr, stringDesc, stringQty, stringWholesale, stringRetail, stringCompany, stringColor, stringSize; try { //connect to ODBC database Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String url = "jdbc:odbc:Zoo"; // connect Connection con = DriverManager.getConnection(url,"dba", "sql"); textResult.setText("Connected..."); // create Statement object Statement stmt = con.createStatement(); // get insert values stringNbr = itemNbr.getText(); stringDesc = itemDesc.getText(); stringQty = itemQty.getText(); stringWholesale = itemWholesale.getText(); stringRetail = itemRetail.getText(); stringCompany = itemCompany.getText(); stringColor = itemColor.getText(); stringSize = itemSize.getText(); String updatestring = "INSERT INTO RETAIL_ITEM(item_nbr, item_desc, qty_per_pkg, " + "wholesale_cost, retail_cost, company_id, color, size) " + "VALUES(" + stringNbr + ",'" + stringDesc + "'," + stringQty + "," + stringWholesale + "," + stringRetail + "," + stringCompany + ",'" + stringColor + "','" + stringSize + "')"; // run query textResult.setText("Working..."); int ct = stmt.executeUpdate(updatestring); textResult.setText("completed"); // close statement stmt.close(); } // catch all exceptions and print out exception message catch (Exception e) { textResult.setText(e.getMessage()); }
}
As you can see from this example, the values are accessed from the AWT components and then used to create an update string. Once the update string is created and executed, the results of the update are output to the results field. Notice that the code also outputs text strings that inform the user of the progress of the update. This notification is essential, especially for longer transactions. If you don't have such a notification scheme, your user will wonder whether an error has occurred.
The example just shown is simpler than you would find in a real-world business application. The example does no checking to make sure that mandatory fields are filled in and does no checking to see whether the user entered appropriate values. Also, a production-quality applet would probably use a drop-down list for the company identifier field and would pull in values from the database and load them to the list when the applet is initialized.
If you run this sample applet using the applet viewer, you should see something similar to Figure 44.2 when the applet loads. In the figure, behind the applet is the DOS window containing the class activity of the applet. If you type some values, the applet looks similar to Figure 44.3.
Figure 44.2.
The sample2 applet after being loaded.
After you enter the values and click the Update button, the applet runs. In Figure 44.4, note that the result field shows the value completed. This notification means that the record was successfully entered into the database.
Now that you have added data to the database, you may find it handy to review the data for accuracy. The next section creates a simple query-based applet to do just that.
Figure 44.3.
The sample2 applet after data has been typed into AWT components.
Figure 44.4.
The sample2 applet after the record insert is completed.
Once you have successfully created an applet that enters data into a database, you will want to create an applet that enables one or more people to view the data.
Creating an applet to display data is no different than creating one to update a database. AWT components that must be accessed by both the init() method and the database-processing method are usually defined as members of the Applet class. In the next example, the retail_item table from the preceding example is used as the basis for a query. The user can view all the records of the table or can choose to view retail items of a certain color, size, or both. Based on these criteria, two components must be created to access the two query values of color and size. Additional components are a button that runs the query and a text field that displays the results. Following is the definition of the members for the new applet:
Choice colorChoice; Choice sizeChoice; Button searchButton; TextArea resultsTextArea;
For this applet, I decided to use drop-down list boxes (also called choice components) for the two query values. This arrangement assists users by providing a list of valid query values. The init() method uses a combination of BorderLayout, FlowLayout, and GridBagLayout container layout classes (see Listing 44.5 and the accompanying CD-ROM). I used the GridBagLayout class because it allows the developer to define a component that takes up the remainder of the space allocated to the applet (both vertically and horizontally).
// init() // // method will instantiate the applet // // the applet objects will be created and placed // in this method // public void init() { Panel layout_area; Panel search_area; Panel result_area; // set background to white setBackground(new Color(255,255,255)); // create border layout as default setLayout(new BorderLayout()); layout_area = new Panel(); layout_area.setLayout(new BorderLayout()); // add layout_area to top of background panel add("North",layout_area); // add top label to layout setFont(new Font("Helvetica",Font.BOLD,12)); layout_area.add("North",new Label("Enter value(s) to search on:")); setFont(new Font("Helvetica",Font.PLAIN,12)); // create gridlayout search panel // add to background layout { search_area = new Panel(); search_area.setLayout(new FlowLayout(1,5,5)); layout_area.add("Center",search_area); // create color Choice object // add to layout with label // create Choice object colorChoice // add colors to Choice object colorChoice = new Choice(); search_area.add(colorChoice); colorChoice.addItem(""); colorChoice.addItem("Red"); colorChoice.addItem("Orange"); colorChoice.addItem("Navy"); colorChoice.addItem("Green"); colorChoice.addItem("Yellow"); colorChoice.addItem("White"); colorChoice.addItem("Black"); colorChoice.addItem("Blue"); colorChoice.addItem("Purple"); search_area.add(new Label("Color:")); // create Choice object sizeChoice // add sizes to Choice object sizeChoice = new Choice(); search_area.add(sizeChoice); sizeChoice.addItem(""); sizeChoice.addItem("T"); sizeChoice.addItem("J"); sizeChoice.addItem("S"); sizeChoice.addItem("M"); sizeChoice.addItem("L"); search_area.add(new Label("Size:")); search_area.add(new Label(" ")); searchButton = new Button(" Search "); searchButton.addActionListener(this); search_area.add(searchButton); } // create another panel for the result set // make the type of layout GridBagLayout { result_area = new Panel(); // add results label setFont(new Font("Helvetica",Font.BOLD,12)); add("Center", new Label("Results:")); setFont(new Font("Courier",Font.PLAIN,12)); add("South",result_area); GridBagLayout gridbag = new GridBagLayout(); result_area.setLayout(gridbag); GridBagConstraints Constraints = new GridBagConstraints(); Constraints.weightx=1.0; Constraints.weighty=1.0; Constraints.anchor=GridBagConstraints.CENTER; Constraints.fill = GridBagConstraints.BOTH; Constraints.gridwidth = GridBagConstraints.REMAINDER; // create results text area resultsTextArea = new TextArea(15,60); // add with constraints to layout gridbag.setConstraints(resultsTextArea,Constraints); result_area.add(resultsTextArea); resultsTextArea.setEditable(false); }
}
After each Choice component is created, data values are added to it using the addItem() method. When the user clicks the down arrow for the component, a list box opens, displaying the values that have been added to it. The user can then click any one of these options to make it the selected item for that component.
USING DIFFERENT FONTS
As you can see in the preceding code samples, you can use different fonts and sizes in the same applet. You may find it effective to use a larger font for buttons and labels and a smaller font for the displayed result. Listing 44.6 shows the rest of the query applet, which includes the event handler, the database connection method, and a method that gets the query values and builds a where clause for the query. You can also find this code on the CD-ROM that accompanies this book.
// check to see if event was search button being // pressed; // if so // connect to database // create query // send statement // process results // else // disregard // public void actionPerformed(ActionEvent event) { if (event.getSource() == searchButton) { processRequest(); } } // process_request // // connect to database // build where clause // retrieve data // output results public void processRequest() { String whereString = ""; String stringSelect = ""; String tempString = ""; String stringResult = ""; try { resultsTextArea.setText("working..."); //connect to database Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String url = "jdbc:odbc:Zoo"; // connect con = DriverManager.getConnection(url, "dba", "sql"); // create Statement stmt = con.createStatement(); whereString = buildWhere(); // execute statement stringSelect = "Select retail_item.item_nbr," + "retail_item.item_desc,retail_item.qty_per_pkg," + "retail_item.color," + "retail_item.size,retail_item.retail_cost " + "from retail_item" + whereString; rs = stmt.executeQuery(stringSelect); resultsTextArea.setText("Processing Results..."); while (rs.next()) { // get item number tempString = rs.getString(1) + "\t"; stringResult+=tempString; // get description tempString=rs.getString(2) + "\t"; stringResult+=tempString; // get qty tempString = rs.getString(3) + "\t"; stringResult+=tempString; // get color tempString = rs.getString(4) + "\t"; if (rs.wasNull()) { tempString = "\t\t"; } stringResult+=tempString; // get size tempString= rs.getString(5) + "\t"; if (rs.wasNull()) { tempString = "\t\t"; } stringResult+=tempString; // get retail cost tempString="$" + rs.getString(6) + "\n"; stringResult+=tempString; } if (stringResult == "") { stringResult = "No Data Found for Query \n" + stringSelect; } resultsTextArea.setText(stringResult); stmt.close(); rs.close(); } catch (Exception e) { resultsTextArea.setText(e.getMessage()); } } // buildWhere // // check Choice components // if an item is selected and is not // the one at position 0, access the selected item // // if choice is category, access the category code // return generated where clause public String buildWhere() { String colorString = null; String sizeString = null; String whereString = null; int colorInt = colorChoice.getSelectedIndex(); if (colorInt > 0) { colorString = colorChoice.getSelectedItem(); } int sizeInt = sizeChoice.getSelectedIndex(); if (sizeInt > 0) { sizeString = sizeChoice.getSelectedItem(); } if (colorString != null) { whereString = " Where "; whereString = whereString + " retail_item.color = '" + colorString + "'"; } if (sizeString != null) { if (whereString == null) { whereString = " Where "; } else { whereString = whereString + " and "; } whereString = whereString + " retail_item.size = '" + sizeString + "'"; } if (whereString == null) { whereString = ""; } return whereString;
}
The applet checks to see whether the user selected a color and size value and uses these to modify the query string. The results are parsed with a tab character (/t) appended to add some white space between the fields. The results are then displayed in the TextArea result field.
Also consider the processRequest() method; it uses the JDBC wasNull() method to check the code to see whether a value is returned from some fields. This function is called after the get method to see whether the returned result is null. You should make this check on any field defined as optional (that is, any field for which null values are allowed) in the database. Fig-ure 44.5 shows the newly loaded applet in the applet viewer.
Figure 44.5.
The sample3 applet after loading.
TIP: Use the result field to display the query string if no data is found. In this way, you can determine whether no data was found because there was none to match the query, or because the query did not contain all the components you assumed it would contain.
If you select all the Black retail items and run the applet, you see results similar to those shown in Figure 44.6. If you select all the Black retail items of size L (large), you see a smaller subset of records, as shown in Figure 44.7.
Figure 44.6.
The sample3 applet with all Black retail items selected.
Figure 44.7.
The sample3 applet with all Black Large retail
items selected.
This chapter introduced the techniques of using the JDBC with applets. Not all JDBC technology is accessible by an applet, although these restrictions and limitations have improved with the new security features of the JDK 1.1.
You use the JDBC from an applet no differently than you use the JDBC from an application--except that you must use the AWT component classes to provide a way to access and display data. Although this chapter did not explain the AWT, it did demonstrate how to display query results and access data for a database update.
©Copyright, Macmillan Computer Publishing. All rights reserved.