1. Trang chủ
  2. » Công Nghệ Thông Tin

Ivor Horton’s Beginning Java 2, JDK 5 Edition phần 10 ppt

150 222 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Talking to Databases
Trường học Not Available
Chuyên ngành Computer Science
Thể loại Bài giảng
Năm xuất bản Not Available
Thành phố Not Available
Định dạng
Số trang 150
Dung lượng 1,85 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

import javax.swing.JTextArea; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollPane; import javax.swing.JTable; import java.

Trang 1

array, you store a reference to it in dataRows After all the rows from the resultset have been stored, you callthe fireTableChanged()method that the ResultsModelclass inherits from the base class This methodnotifies all listeners for the JTableobject for this model that the model has changed, so the JTableobjectshould redraw itself from scratch The argument to the fireTableChanged()method is a reference to anobject of type TableModelEventthat you can use to record the parts of the model that have changed, andthis is passed to the listeners You pass a nullhere, as you want to invalidate the whole table.

The method to return the number of columns is now very easy to implement:

public int getColumnCount() {return columnNames.length;

}The column count is the number of elements in the columnNamesarray

Supplying the row count is just as easy:

public int getRowCount() { return dataRows == null ? 0 : dataRows.size();

}The number of rows corresponds to the size of the Vector<>object, dataRows You check to verify thatthe value of the dataRowsvector is not nullto ensure that the initialization of the InteractiveSQLGUI can take place even when the vector has not been initialized

The next method you need to define provides access to the data values You can implement this as lows:

fol-public String getValueAt(int row, int column) {return dataRows.elementAt(row)[column];

}The elementAt()method returns the element in the Vector<>object at the position specified by theargument This will be a reference to an array of type String[]so you just index this with the value ofthe columnparameter to select the element to be returned

The last method you must add to the class is getColumnName(), which will return the column namegiven a column index You can implement this as:

public String getColumnName(int column) {return columnNames[column] == null ? “No Name” : columnNames[column];

}You take the precaution here of dealing with a nullcolumn name by supplying a default column name

in this case

Trang 2

The Application GUI

Figure 24-9 shows the user interface for the InteractiveSQLtool The text field at the top provides an entry area for typing in the SQL statement and will be implemented using a JTextFieldcomponent The results display provides a scrollable area for the results of the executed SQL command This will be implemented using a JScrollPanecomponent A status line, implemented as a JTextAreacomponent, provides the user with the number of rows returned from the query, or the text of any SQLException object generated by the query

Figure 24-9

Figure 24-9 also shows the menu items in the File menu and the tooltip prompt for the SQL input area The Clear query menu item will just clear the input area where you enter an SQL query

Try It Out Defining the GUI

You will derive the InteractiveSQLclass from the JFrameclass and make this the foundation for the application Its constructor will be responsible for loading the JDBC driver class, creating a connection to the database, and creating the user interface The code is as follows:

import java.awt.BorderLayout;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import javax.swing.JFrame;

import javax.swing.JTextField;

Trang 3

import javax.swing.JTextArea;

import javax.swing.JMenu;

import javax.swing.JMenuBar;

import javax.swing.JMenuItem;

import javax.swing.JScrollPane;

import javax.swing.JTable;

import java.sql.DriverManager;

import java.sql.Connection;

import java.sql.Statement;

import java.sql.SQLException;

public class InteractiveSQL extends JFrame { public static void main(String[] args) { // Create the application object InteractiveSQL theApp = new InteractiveSQL(“sun.jdbc.odbc.JdbcOdbcDriver”,

“jdbc:odbc:technical_library”,

“guest”,

“guest”);

} public InteractiveSQL(String driver, String url,

String user , String password) { super(“InteractiveSQL”); // Call base constructor setBounds(0, 0, 400, 300); // Set window bounds setDefaultCloseOperation(DISPOSE_ON_CLOSE); // Close window operation addWindowListener(new WindowAdapter() { // Listener for window close

// Handler for window closing event public void windowClosing(WindowEvent e) { dispose(); // Release the window resources System.exit(0); // End the application

} } );

// Add the input for SQL statements at the top command.setToolTipText(“Key SQL commmand and press Enter”);

getContentPane().add(command, BorderLayout.NORTH);

// Add the status reporting area at the bottom status.setLineWrap(true);

status.setWrapStyleWord(true);

getContentPane().add(status, BorderLayout.SOUTH);

// Create the menubar from the menu items JMenu fileMenu = new JMenu(“File”); // Create File menu fileMenu.setMnemonic(‘F’); // Create shortcut fileMenu.add(clearQueryItem); // Add clear query item fileMenu.add(exitItem); // Add exit item menuBar.add(fileMenu); // Add menu to the menubar setJMenuBar(menuBar); // Add menubar to the window // Establish a database connection and set up the table

try { Class.forName(driver); // Load the driver connection = DriverManager.getConnection(url, user, password);

statement = connection.createStatement();

Trang 4

model = new ResultsModel(); // Create a table modelJTable table = new JTable(model); // Create a table from the modeltable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // Use scrollbarsresultsPane = new JScrollPane(table); // Create scrollpane for tablegetContentPane().add(resultsPane, BorderLayout.CENTER);

} catch(ClassNotFoundException cnfe) {System.err.println(cnfe); // Driver not found} catch(SQLException sqle) {

System.err.println(sqle); // error connection to database}

pack();

setVisible(true);

}

JTextField command = new JTextField(); // Input area for SQL

JTextArea status = new JTextArea(3,1); // Output area for status and errorsJScrollPane resultsPane;

JMenuBar menuBar = new JMenuBar(); // The menu bar

JMenuItem clearQueryItem = new JMenuItem(“Clear query”); // Clear SQL item

JMenuItem exitItem = new JMenuItem(“Exit”); // Exit item

Connection connection; // Connection to the database

Statement statement; // Statement object for queriesResultsModel model; // Table model for resultset

}

You can try running the application as it is, and you should see the basic application interface displayed

in the window with a working close operation

How It Works

The constructor is passed the arguments required to load the appropriate driver and create a

Connectionto a database The first executable statement in this constructor calls the constructor for theJFrameclass, passing a default window title to it The constructor then creates and arranges the userinterface components Most of this should be familiar to you, but let’s pick out a few things that are new,

or are worthy of a second look

You can see how you add a tooltip for the JTextFieldcomponent command— the input area for an SQLstatement Don’t forget that you can add a tooltip for any Swing component in the same way

You define the JTextAreaobject, status, so that it can display three lines of text The first argument tothe JTextAreaconstructor is the number of lines of text, and the second argument is the number ofcolumns Some of the error messages can be quite long, so you call both the setLineWrap()method tomake lines wrap automatically, and the setWrapStyleWord()method to wrap a line at the end of aword — that is, on whitespace — rather than in the middle of a word In both cases the trueargumentswitches the facility on

You create the JTableobject using the default ResultsModelobject, which will contain no data tially Since the number of columns in a resultset will vary depending on the SQL query that is executed,you wrap the JTableobject in a JScrollPaneobject to provide automatic scrolling as necessary Thescrollbars will appear whenever the size of the JTableobject is larger than the size of the scroll pane By

Trang 5

ini-default, a JTableobject will resize the width of its columns to fit within the width of the JTableponent To inhibit this and allow the scroll pane scrollbars to be used, you call the

com-setAutoResizeMode()method with the argument as JTable.AUTO_RESIZE_OFF.This not only inhibits the default resizing action when the table is displayed, but also allows you tochange the size of a column when the table is displayed without affecting the size of the other columns.You change the size of a column by dragging the side of the column name using the mouse There areother values defined in the JTableclass that you can pass to the setAutoResizeMode()method todetermine how resizing is handled:

AUTO_RESIZE_ALL_COLUMNS Adjusts the sizes of all columns to take up the

change in width of the column being resized Thismaintains the overall width of the table

AUTO_RESIZE_NEXT_COLUMN Adjusts the size of the next column to provide for

the change in the column being altered in order tomaintain the total width of the table

AUTO_RESIZE_LAST_COLUMN Adjusts the size of the last column to provide for

the change in the column being altered in order tomaintain the total width of the table

AUTO_RESIZE_SUBSEQUENT_COLUMNS Adjusts the size of the columns to the right to

pro-vide for the change in the column being altered inorder to maintain the total width of the table

Handling Events

Of course, the program doesn’t do anything because there’s no code in the program to respond to SQLcommands that you enter in the text field or to menu item selection The first step is to make theInteractiveSQLclass implement the ActionListenerinterface Change the first line of the class definition to:

public class InteractiveSQL extends JFrame implements ActionListener {You can define the actionPerformed()method in the InteractiveSQLclass like this:

public void actionPerformed(ActionEvent e) {Object source = e.getSource();

if(source == command) { // Enter key for text field inputexecuteSQL();

} else if(source == clearQueryItem) { // Clear query menu itemcommand.setText(“”); // Clear SQL entry} else if(source == exitItem) { // Exit menu itemdispose(); // Release the window resourcesSystem.exit(0); // End the application

} }

Trang 6

This method is handling events from the text field and the menu items, so the action to be carried outdepends on the object that originated the event You determine which object originated the event bycomparing the reference returned by the getSource()method for the event object with the three fields

in the InteractiveSQLobject If it’s the text field, you call the executeSQL()method that you’ll addnext; this will execute the SQL command that was entered If it’s the clearQueryItemmenu item, youcall the setText()method for the JTextFieldobject to reset the contents to an empty string If it’s theexitItemmenu item, you just exit the program after releasing the window resources

You can define the method that will enter the SQL command that was entered like this:

public void executeSQL() {

String query = command.getText(); // Get the SQL statementif(query == null ||query.length() == 0) { // If there’s nothing we are donereturn;

}try {model.setResultSet(statement.executeQuery(query));

status.setText(“Resultset has “ + model.getRowCount() + “ rows.”);

} catch (SQLException sqle) {status.setText(sqle.getMessage()); // Display error message}

}

Calling the getText()method for the JTextFieldobject returns a reference to the string that wasentered If it’s nullor an empty string, there’s nothing to be done, so the method returns immediately Ifit’s not null, you pass the query string to the executeQuery()method for the Statementobject Thiswill return a reference to a ResultSetobject containing the results of the query, and you pass this to thesetResultSet()method for the ResultsModelobject This sets the new resultset in the model andcauses the JTableobject to redisplay itself with the new data from the table model Finally, you displaythe number of rows returned by the query in the text area below the table

Of course, you still have to identify the InteractiveSQLobject as the listener for the text field and thetwo menu items, so add the following code to the constructor after the code that sets up the menu:

menuBar.add(fileMenu); // Add menu to the menubarsetJMenuBar(menuBar); // Add menubar to the window// Add listeners for text field and menu items

Trang 7

Handling Command-Line Arguments

All you need to do is to alter the main()method to accept up to four command-line arguments; thesewill be the values for the user name, password, database URL, and JDBC driver

Try It Out Using Command-Line ParametersYou need to modify the code in main()to:

public static void main(String[] args) {// Set default values for the command line argsString user = “guest”;

String password = “guest”;

String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

// Up to 4 arguments in the sequence database url,driver url, user ID, passwordswitch(args.length) {

case 4: // Start here for four argumentspassword = args[3];

// Fall through to the next casecase 3: // Start here for three argumentsuser = args[2];

// Fall through to the next casecase 2: // Start here for two argumentsdriver = args[1];

// Fall through to the next casecase 1: // Start here for one argumenturl = args[0];

}InteractiveSQL theApp = new InteractiveSQL(driver, url, user, password);

}

How It Works

Now the program enables you to optionally specify the JDBC URL, the JDBC driver, the user name, andthe password on the command line If you don’t supply any command-line arguments, the programworks as before, accessing the technical_librarydatabase

The mechanism that handles the optional parameters is pretty simple The switchstatement tests thenumber of parameters that were specified on the command line If one parameter was passed, it is inter-preted as the JDBC URL If two parameters were passed, the second parameter is assumed to be thedriver URL, and so on There are no breakstatements, so control always drops through from the start-ing case to include each of the following cases

Summar y

In this chapter you’ve been introduced to JDBC programming and seen it in action The important pointscovered in this chapter include the following:

Trang 8

❑ The fundamental classes in JDBC are as follows:

❑ DriverManagermanages the loading of JDBC drivers and connections to client cations

appli-❑ Connectionprovides a connection to a specific data source

❑ Statementprovides a context for executing SQL statements

❑ ResultSetprovides a means for accessing data returned from an executedStatement

❑ The essential JDBC program has the following basic sequence when writing:

❑ Import the necessary classes

❑ Load the JDBC driver

❑ Identify the data source

❑ Allocate a Connectionobject

❑ Allocate a Statementobject

❑ Execute a query using the Statementobject

❑ Retrieve data from the returned ResultSetobject

❑ Close the ResultSet

❑ Close the Statementobject

❑ Close the Connectionobject

❑ The JTablecomponent provides an easy and convenient way to display the results of databasequeries

❑ A table model can provide the data to be displayed by a JTablecomponent A table model is anobject of a class that implements the TableModelinterface

3. Modify the InteractiveSQLprogram to allow the user to specify which database to use

4. Modify the InteractiveSQLprogram to provide separate input areas for each part of aSELECTstatement — one for the table columns, one for the table, one for a possible WHEREclause, and so on

Trang 9

The JDBC in Action

In this chapter I’ll expand on the topics that I introduced in the previous chapter, and go into moredetail on the Java Database Connectivity (JDBC) application program interface (API) In this chap-ter you’re going to learn more about:

❑ How you map relational data onto Java objects

❑ The mapping between SQL and Java data types

❑ How you limit the data created in a resultset

❑ How you constrain the time spent executing a query

❑ How you use a PreparedStatementobject to create a parameterized SQL statement

❑ How you can execute database update and delete operations in your Java programs

❑ How you can get more information from SQLExceptionobjects

❑ What an SQLWarningobject is and what you can do with it

Data Types and JDBC

In all of the examples so far, all of the data extracted from a resultset was retrieved as a String.You’ll certainly need to get other types of data, and as you saw in the previous chapter, theResultSetprovides a number of methods for retrieving different data types To use these effec-tively, you need to look at the SQL data types and understand how they map to the Java datatypes in your program

Mapping between Java and SQL Data Types

The SQL-92 standard defines a set of data types that don’t map one-for-one with those in Java Asyou write applications that move data from SQL to Java and back, you’ll have to take account ofhow JDBC performs that mapping That is, you need to know the Java data type you need to rep-resent a given SQL data type, and vice versa

Trang 10

The Typesclass in the java.sqlpackage defines constants of type intthat represent each of the ported SQL types The name given to the data member storing each constant is the same as that of thecorresponding SQL type For example, when you retrieve the SQL type of a table column by calling thegetColumnType()method for a ResultSetMetaDataobject, the SQL type is returned as one of theconstants defined in the Typesclass.

sup-When you’re retrieving data from a JDBC data source, the ResultSetimplementation will map the SQLdata onto Java data types The following table shows the SQL-to-Java mappings:

Conversely, when you are relating Java-to-SQL data types, the following mappings apply:

Trang 11

Java Data Type SQL Data Type

Mapping Relational Data onto Java Objects

In the previous chapter, you saw how you could get the basic attribute data from a JDBC ResultSetobject Since Java is object-oriented, in many cases you won’t want to deal with individual data itemssuch as the authors’ names and IDs — you’ll want to work with Authorobjects that represent theauthors That’s what I’ll focus on now, and in the process, you’ll get some more experience with theStatementand ResultSetinterfaces

The way that information is handled at the object level is usually different from the way that data isstored in a relational database In the world of objects, the underlying principle is to make those objectsexhibit the same characteristics (information and behavior) as their real-world counterparts — in otherwords, objects function at the level of the conceptual model Relational databases, on the other hand,

Note that some databases implement INTEGERdata types as NUMERIC When ing INTEGERelements through the JDBC, it is important to associate the JDBC data type with the internal data type that is actually stored in the database.

Trang 12

access-work at the data model level As you saw in the previous chapter, relational databases store informationusing normalized forms, where conceptual objects like invoices and customers can be decomposed into

a number of tables So how do you deal with the problem of mapping objects to relational data models?

Sometimes there is a straightforward relationship between the columns in a table and the member ables in an object In that case, the mapping task consists simply of matching the data types of thedatabase with those of Java Figure 25-1 shows this simple application-level SQL-to-object mapping

vari-Figure 25-1

Try It Out A Simple Mapping from SQL Rows to Java Objects

The authorstable in the sample database is a good example of a simple mapping To recap, this tablehas the following definition:

Application

JDBC

Databaseauthors table

Authorobject

Call JDBC method to specifySQL SELECT statement

The application extracts datadescribing an author from thedatabase as a ResultSet object

The application creates theAuthor object from datacontained within the ResultSet

ResultSetobject

TabledataSQL

SELECT

Trang 13

Column Data Type Description

Let’s define a Java class to encapsulate an author Take a look back at the table that shows you how tomap SQL to Java data types Based on those mappings, you can define the member variables for anAuthorclass, and add a constructor, member access methods, and a toString()method:

public class Author {public Author(int authid, String lastname, String firstname,

String address[], String city, String state,String postcode, String country,

String phone, String fax, String email){

}public String getLastName() {return lastname;

}public String getFirstName() {return firstname;

}public String[] getAddress() {return address;

}public String getCity() {return city;

}

Trang 14

public String getState() {

public String toString() {

return new String(“author ID: “ + Integer.toString(authid) +

“\nname : “ + lastname + “,” + firstname +

Trang 15

try {SQLtoJavaExample = new TrySimpleMapping();

SQLtoJavaExample.listAuthors();

} catch(SQLException sqle) {System.err.println(sqle);

} catch(ClassNotFoundException cnfe) {System.err.println(cnfe);

}}public TrySimpleMapping() throws SQLException, ClassNotFoundException {Class.forName (driverName);

connection = DriverManager.getConnection(sourceURL, user, password);

}public void listAuthors() throws SQLException {Author author = null;

String query = “SELECT authid, lastname, firstname, address1,”+

“address2, city, state_prov, postcode, country,”+

“phone, fax, email FROM authors”;

Statement statement = connection.createStatement();

ResultSet authors = statement.executeQuery(query);

while(authors.next()) {int id = authors.getInt(1);

String lastname = authors.getString(2);

String firstname = authors.getString(3);

String[] address = { authors.getString(4), authors.getString(5)};

String city = authors.getString(6);

String state = authors.getString(7);

String postcode = authors.getString(8);

String country = authors.getString(9);

String phone = authors.getString(10);

String fax = authors.getString(11);

String email = authors.getString(12);

author = new Author(id, lastname, firstname,

address, city, state, postcode,country, phone, fax, email);

System.out.println(“\n” + author);

}authors.close();

connection.close();

Trang 16

Connection connection;

String driverName = “sun.jdbc.odbc.JdbcOdbcDriver”;

String sourceURL = “jdbc:odbc:technical_library”;

String user = “guest”;

String password = “guest”;

con-as the column names in the SQL query To display the data for each Authorobject, you simply callSystem.out.println()and pass the Authorobject reference to it This will automatically invoke thetoString()method for the object Notice that in the output, the literal nullappears where there arenullvalues in the database

This example uses the JDBC-ODBC Bridge driver with a data source that does not

require a user name or password If you need a user name and password to access that

data source, simply modify the code in the TrySimpleMappingconstructor to use the

appropriate driver, URL, and getConnection()method of the DriverManager.

Trang 17

A Better Mapping Strategy

As you saw, the simple strategy described in the previous section does in fact transfer the data betweenthe relational database and the Java objects successfully (and this approach can be used in reverse to getdata back to the database, as you’ll see shortly) It does, however, leave quite a lot to be desired becausethe movement of data between the database and the Java object is left completely to the application code

A better, more object-oriented strategy would be to make the Authorclass handle its own data tion from a ResultSetobject To do this, you could add to the Authorclass a staticfactory method (amethod that manufactures Authorobjects) that will synthesize Authorobjects from data in the

extrac-database The code calling the factory method must still do the work of creating the ConnectionandStatementobjects and use the Statementobject to execute the query that retrieves the data It will alsoneed to ensure that the ResultSetcontains the columns required for populating the Authorobject.You need to establish an implied “contract” between this factory method and any code that calls it:

❑ The current row of the ResultSetobject that is passed to the factory method must be tioned at a valid row

posi-❑ The ResultSetmust contain all the columns from the authorstable

You can implement the factory method in the Authorclass as:

public static Author fromResults(ResultSet authors) throws SQLException {String[]address = {

authors.getString(“address1”),authors.getString(“address2”)};

return new Author(

authors.getInt(“authid”),authors.getString(“lastname”),authors.getString(“firstname”),address,

authors.getString(“city”),authors.getString(“state_prov”),authors.getString(“postcode”),authors.getString(“country”),authors.getString(“phone”),authors.getString(“fax”),authors.getString(“email”));

}Here you access the columns by name, so there is no dependency on the order in which they areretrieved in the query This gives a little added flexibility to the application — to use the wildcard nota-tion, for example The only requirement is that all the columns should be present in the ResultSetobject If any are not, an exception of type SQLExceptionwill be thrown, and this will need to be caught

by the calling method Of course, the Author.javafile must now have importstatements added forthe ResultSetand SQLExceptionnames from the java.sqlpackage

You can see this in action with another example

Trang 18

Try It Out Encapsulated Mapping of SQL Rows to Java Objects

You just need to create the application class This class is nearly identical to TrySimpleMapping, exceptthat there’s less code in the listAuthors()method:

public class TryEncapsulatedMapping {

public static void main (String[] args) {

TryEncapsulatedMapping SQLtoJavaExample;

try {SQLtoJavaExample = new TryEncapsulatedMapping();

SQLtoJavaExample.listAuthors();

} catch(SQLException sqle) {System.err.println(sqle);

} catch(ClassNotFoundException cnfe) {System.err.println(cnfe);

}}

public TryEncapsulatedMapping() throws SQLException,

ClassNotFoundException {Class.forName (driverName);

connection = DriverManager.getConnection(sourceURL, user, password);

}

public void listAuthors() throws SQLException {

Author author;

String query = “SELECT authid, lastname, firstname, address1,”+

“address2, city, state_prov, postcode, country,”+

“phone, fax, email FROM authors”;

Statement statement = connection.createStatement();

ResultSet authors = statement.executeQuery(query);

while(authors.next())System.out.println(“\n” + Author.fromResults(authors));

authors.close();

connection.close();

}

Connection connection;

String driverName = “sun.jdbc.odbc.JdbcOdbcDriver”;

String sourceURL = “jdbc:odbc:technical_library”;

String user = “guest”;

String password = “guest”;

}

When you run the example, you should get results that are exactly the same as those from the previousexample

Trang 19

How It Works

All you’ve really done in this example is push the work of extracting Java types from the ResultSettothe class that is using the data Instead of reading from the ResultSetand instantiating a new Authorobject for each row in the listAuthors()method, you just call the static fromResults()method ofthe Authorclass, which will create a new Authorobject from the data in the current row of theResultSet

This approach is better than the previous example because the class itself is responsible for ensuring thatthe correct mapping is performed between the database and the Java object That way, applications don’thave to duplicate that logic and don’t have the opportunity to attempt bad mappings (such as convert-ing an SQLREALtype to an int) The mapping is also independent of the sequence of columns in theresultset Encapsulation of the mapping from the database data to the class object is important for ensur-ing that classes can be reused easily within and between applications; therefore, although it’s a littlemore work than the simple mapping method, it’s well worth it

The Statement and PreparedStatement Interfaces

In this section you’re going to look in more detail at the Statementand PreparedStatementinterfaces.You’ll start with the Statementinterface, where you’ll learn about the methods that allow you to con-strain the query and how to handle data definition and data manipulation Next, you’ll look at thePreparedStatement, explore the differences between static and dynamic statements, and work withthe PreparedStatementinterface

The Statement Interface

You were introduced to the Statementinterface in the previous chapter The Statementinterfacedefines a set of methods that are implemented by an object returned to your program when you call thecreateStatement()method for the Connectionobject:

try {Statement queryStatement = connection.createStatement();

//

} catch(SQLException sqle) {System.err.println(sqle);

}Like pretty much every other method defined by JDBC, this code must be within a tryblock andinclude a catchblock for SQLException

Once the Statementinterface has been created, defining the query is as simple as building a Stringcontaining a valid SQL statement and passing that statement as the argument to the executeQuery()method of the Statementobject The SQL query can be a literal, or it can be a Stringvalue that youbuild at run time, as was the case in the InteractiveSQLapplication in the previous chapter, where theapplication obtains the SQL string from the text field just before the statement is executed

Trang 20

Constraining the Resultset

In general, you won’t normally know how much data will be returned from executing a query In thetechnical_libraryexample, there isn’t any possibility of getting into difficulties because of the vol-ume of data, but with production databases you may need some controls Getting a million rows backfrom a SELECToperation could be an embarrassment, not only because a substantial amount of time will

be involved, but also because a large amount of memory will be needed to store the data The

Statementinterface allows you to set constraints on the consequences of executing a query You canlimit the number of rows in the resultset that are returned, as well as specify the maximum field size.You can also limit the amount of time that is allowed for a query to execute

Maximum Number of Rows

The JDBC driver may impose a limitation on how many rows may be returned by a query, and you maywish to impose your limit on how many rows are returned in a resultset The Statementinterfacedefines the getMaxRows()and setMaxRows()methods that allow you to query and set the maximumnumber of rows returned in the ResultSetobject An argument value 0is defined as no limit

A particular JDBC driver may default to a practical limit on the number of rows in a resultset or mayeven have implementation restrictions that limit the number of rows that are returned To determine therow limit in effect, you can call the getMaxRows()method for your Statementobject:

Statement statement = connection.createStatement();

int maxRows = statement.getMaxRows();

When you wish to limit the number of rows returned from a query in an application, to prevent anextremely lengthy query process, for example, you call setMaxRows()to limit the number of rowsreturned:

SQLStatement.setMaxRows(30);

Maximum Field Size

The Statementinterface also enables you to query and set the maximum field size that applies to allcolumn values returned in a ResultSet Querying this value will tell you if the JDBC driver imposes apractical or absolute limit on the size of the columns returned The value 0is defined as no limit

To determine the maximum field size for statement results, simply call the getMaxFieldSize()method

of the Statement:

Statement statement = connection.createStatement();

int maxFieldSize = statement.getMaxFieldSize();

It’s important to realize that when the maximum row count is set to a non-zero value

(zero being unlimited), you won’t get any indication when the data that would have

been returned has been truncated If the total number of rows exceeds the maximum

value, the maximum number of rows are returned in the resultset, and any

remain-ing rows that meet the query criteria will be silently left behind.

Trang 21

The value returned is the maximum number of bytes permitted for any field returned in a resultset Likethe maximum row method pair, there is a corresponding setMaxFieldSize()method to set the maxi-mum field size:

SQLStatement.setMaxFieldSize(4096);

Note that the setMaxFieldSize()method applies only to columns with the following SQL data types:

Any bytes in a field in excess of the maximum you have set will be silently discarded

Query Time-Out

Depending on your JDBC driver and the database to which it is attached, there may be an executiontimeout period after which a query will fail and the executeQuery()method will throw an exception.You can check the value for the time-out period with the getQueryTimeout()method for a Statementobject; you can also set the time-out period (for example, if you want a query to fail after a fixed timeperiod) using the setQueryTimeout()method The time-out period is defined in seconds, and a time-out value of 0indicates that there is no limit on the time that a query can take

Here’s a simple program that will test the default query constraints for your JDBC driver You can tute an appropriate URL and driver name if you have other JDBC drivers available

substi-Try It Out Query ConstraintsSince this program is tiny, you’ll incorporate everything into the main()method:

try {String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String username = “guest”;

String password = “guest”;

Trang 22

// Put each method call in a separate try block to execute them alltry {

System.out.print(“\nMaximum rows :”);

int maxRows = statement.getMaxRows();

System.out.print(maxRows == 0 ? “ No limit” : “ “ + maxRows);

} catch (SQLException sqle) {System.err.print(sqle);

}try {System.out.print(“\nMax field size :”);

int maxFieldSize = statement.getMaxFieldSize();

System.out.print(maxFieldSize == 0 ? “ No limit” : “ “ + maxFieldSize);} catch (SQLException sqle) {

System.err.print(sqle);

}try {System.out.print(“\nTimeout :” );

int queryTimeout = statement.getQueryTimeout();

System.out.print(queryTimeout == 0 ? “ No limit” : “ “ + queryTimeout);} catch (SQLException sqle) {

System.err.print(sqle);

}}

}

Running this with Access, I got the following output:

Driver : sun.jdbc.odbc.JdbcOdbcDriver

Maximum rows : No limit

Max field size :java.sql.SQLException: [Microsoft][ODBC Microsoft Access

Driver]Optional feature not implemented

Timeout :java.sql.SQLException: [Microsoft][ODBC Microsoft Access

Driver]Optional feature not implemented

You can see that the underlying ODBC driver doesn’t support time-out or field size constraints — moresophisticated drivers are likely to support these methods

How It Works

This code is pretty simple It creates a Connectionusing a URL defining an Access database calledtechnical_library Once the connection is established, a Statementobject is created, and using thatthe values for the query time-out period, the maximum column size, and the maximum number of rowscan be executed All three methods providing this information will throw an exception of type

SQLExceptionif the information is not available — as is the case with the Microsoft Access driver Tomake sure that you do call all three methods, even when an exception is thrown, each method call is in aseparate tryblock

Executing DDL and DML

As you know, the executeQuery()method is used to execute an SQL query statement — a statementthat is expected to return some results in a resultset As I indicated in the previous chapter, there are other

Trang 23

types of SQL statements that do not return results These statements fall into two primary categories:Data Definition Language (DDL) statements and Data Manipulation Language (DML) statements.

❑ DDL statements are those that change the structure of a database, such as CREATE TABLEandDROP TABLE

❑ DML statements are those that change the contents of the database, such as INSERT, UPDATE,and DELETEstatements

So far, all of the examples you have seen, including the InteractiveSQLapplication, have used theexecuteQuery()method If you tried to execute an SQL statement that didn’t produce a resultset withthe InteractiveSQLprogram, such as any DDL or DML, you would see an exception message reported

on the status line This is shown in Figure 25-2

Figure 25-2

The exception containing the message “No ResultSet was produced” is thrown because theexecuteQuery()method expects only an SQL statement that generates results (note that even though an exception was thrown in this case, the SQL statement was still executed)

The Statementinterface provides the executeUpdate()method to execute statements that change the contents of the database rather than return results Like executeQuery(), the executeUpdate()method accepts a single argument of type Stringspecifying the SQL statement that is to be executed.You can use the executeUpdate()method to execute UPDATE, INSERT, or DELETESQL statements You

Trang 24

can also use it to execute DDL statements The method returns a value of type intthat indicates thenumber of rows affected by the operation when the database contents are changed, or 0for statementsthat do not alter the database.

The code fragment below illustrates use of the executeUpdate()method to add a row to the authorstable:

I split the SQL command into two strings simply because it will not fit on a single line in the book

Using the executeUpdate()method, it is pretty easy to write a utility to create and populate a table.The next example does exactly that In fact, this example is similar to the build_tablesutility includedwith the book’s source code, except that the latter reads an SQL statement from an external file

Try It Out Executing DDL and DML

Again, this is a small example, so the code will all be contained in the main()method The URL and thedriver are identified by the urland driverstrings:

import java.sql.DriverManager;

import java.sql.Connection;

import java.sql.Statement;

import java.sql.SQLException;

public class BuildTables {

public static void main(String[] args) {

try {String username = “guest”;

String password = “guest”;

String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String[] SQLStatements = {

“CREATE TABLE online_resources (pub_id int, name char(48), url char(80))”,

“INSERT INTO online_resources VALUES(1, ‘Wrox Home Page’,” +

Class.forName(driver);

Trang 25

Connection connection = DriverManager.getConnection(url, username, password);Statement statement = connection.createStatement();

for (String SQLStatement : SQLStatements) {statement.executeUpdate(SQLStatement);

System.out.println(SQLStatement);

}} catch (ClassNotFoundException cnfe) {System.err.println(cnfe);

} catch (SQLException sqle) {System.err.println(sqle);

}}}The SQLStatements Stringarray contains the DDL and DML that will be executed by this program.The forloop simply iterates through each statement in the array and executes it using the

executeUpdate()method of the Statement.You can check the results by running the InteractiveSQLapplication on the table you created andobserving that the rows were inserted To do this, start that application and execute the SQL statement:

SELECT * FROM online_resourcesAfter you’re satisfied with your results, feel free to delete the table Using a new instance of theInteractiveSQLapplication, execute the statement:

DROP TABLE online_resourcesInteractiveSQLwill complain that no ResultSetis produced, but will dispose of the table nevertheless

How It Works

The BuildTablesprogram is very simple The Stringarray SQLStatementscontains all of the SQLstatements that you want to execute with executeUpdate()— one statement in each element of thearray Note that you concatenate two Stringliterals for three of the array element values just to makethe presentation clearer on the page here The forloop iterates through that array and executes andprints each statement in turn As usual, the code is inside a tryblock to catch any exceptions that might

be thrown

The only differences between this example and the other examples you’ve seen are that the SQL ments are executed with the executeUpdate()method instead of the executeQuery()method, andinstead of a ResultSetbeing returned, the method returns the number of rows affected by the operation

state-The PreparedStatement Interface

Earlier in this chapter, you saw that you can build SQL strings on the fly and execute them with theexecuteQuery()method of a Statement That is one way to introduce parameters into an SQL state-ment, but it is not the only way, nor is it necessarily the most convenient The PreparedStatementinterface provides an alternative mechanism that enables you to define an SQL statement with

placeholdersfor arguments Placeholders are tokens that appear in the SQL statement that are replaced

Trang 26

by actual values before the SQL statement is executed This is usually much easier than building an SQLstatement with specific values by concatenating strings.

Like a Statementobject, you create a PreparedStatementobject by calling a method for a

Connectionobject Instead of calling the createStatement()method of the Connectionobject, youcall the prepareStatement()method when you want to create a PreparedStatementobject Whileyou can use a Statementobject to execute any number of different SQL statements, a

PreparedStatementobject executes only one predefined SQL statement that has placeholders for thevariable parts of the statement You specify the SQL statement that a PreparedStatementobject repre-sents by an argument of type Stringto the prepareStatement()method The argument specifies theSQL statement with each placeholder for a value represented by a ?character For example:

String newLastName = “UPDATE authors SET lastname = ? WHERE authid = ?”;

PreparedStatement updateLastName = connection.prepareStatement(newLastName);

The first statement defines a Stringobject specifying an UPDATEstatement with placeholders for thelast name and the author ID values This string is passed as the argument to the prepareStatement()method call that creates the PreparedStatementobject, updateLastName This will allow any valuefor lastnameto be set for any authid

Setting Query Parameters

You must set values for all the placeholders that appear in the statement encapsulated by the

updateLastNamereference before the statement can be executed You supply the value for each holder by calling one of the setXXX()methods of the PreparedStatementinterface:

These methods accept, minimally, a position argument that identifies which placeholder you are ring to and a value argument that is the value to be substituted for the placeholder Placeholders areindexed in sequence from left to right starting at 1, so you reference the leftmost placeholder with a posi-tion index of 1, and any placeholders that follow with index values of 2, 3, and so on

refer-The method that you call for a particular placeholder for an input value depends of the SQL type of thedestination column You must select the method that corresponds to the field type, so you would usesetInt()for type INTEGER, for example After calling the appropriate setXXX()method for eachplaceholder in the PreparedStatementobject, you can execute the SQL statement that the

PreparedStatementobject represents either by calling its executeQuery()method if the statementgenerates a resultset or by calling its executeUpdate()method to update or otherwise alter thedatabase Neither method requires an argument since the PreparedStatementobject already has itsSQL statement defined

Trang 27

Once all the placeholders have values set and you have executed the statement, you can update any orall of the placeholders (or even none) before re-executing the statement The following code fragmentshows the PreparedStatementplaceholder value replacement in action:

// Create a PreparedStatement to update the lastname field for an authorString changeLastName = “UPDATE authors SET lastname = ? WHERE authid = ?”;

PreparedStatement updateLastName = connection.prepareStatement(changeLastName);updateLastName.setString(1,”Martin”); // Set lastname placeholder valueupdateLastName.setInt(2,4); // Set author ID placeholder valueint rowsUpdated = updateLastName.executeUpdate(); // execute the update

Note that placeholders for string arguments are not quoted — they are just a ?character, and thePreparedStatementautomatically sets up the empty placeholder Also, it’s perfectly okay to setparameters in whatever order you choose — you don’t have to set the first placeholder first, the secondone next, and so forth, just so long as they are all set before the statement is executed

Let’s try the code fragment above in an example that will change the last name of the author whoseauthidis 4

Try It Out Using a PreparedStatement ObjectTry out the following code — only the bits that are of particular interest are shaded here:

String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String user = “guest”;

String password = “guest”;

Class.forName(driver);

Connection connection = DriverManager.getConnection(url);

String changeLastName = “UPDATE authors SET lastname = ? WHERE authid = ?”;PreparedStatement updateLastName =

connection.prepareStatement(changeLastName);updateLastName.setString(1,” Martin”); // Set lastname placeholder valueupdateLastName.setInt(2,4); // Set author ID placeholder valueint rowsUpdated = updateLastName.executeUpdate(); // execute the updateSystem.out.println(“Rows affected: “ + rowsUpdated);

Trang 28

This should produce the following output:

Rows affected: 1

How It Works

The PreparedStatementobject is created from the Connectionobject by calling the

prepareStatement()method The statement is also defined with the placeholders marked as questionmarks Those placeholders, for the last name and author ID columns, respectively, are then filled withvalues at run time by calling the setString()and setInt()methods of the PreparedStatementinterface

The statement is executed by calling the executeUpdate()method, which returns the number of rowsaffected by the update operation No arguments are passed to the method since the SQL statement wasdefined when the PreparedStatementobject was created

Note that if no change was made to the database, which would occur for example if the primary keyvalue, authid, did not exist in the authorstable, the 0 would be returned as the number of rowsaffected If you want to verify that the change was made, you can use the InteractiveSQLprogram toinspect the list of authors

Statement versus PreparedStatement

There will be times where the choice between using a Statementobject or a PreparedStatementobject may not be entirely clear PreparedStatementobjects are great when:

❑ You need to execute the same statement several times and need to change only specific values

❑ You are working with large chunks of data that make concatenation unwieldy

❑ You are working with a large number of parameters in the SQL statement that make string catenation unwieldy

con-Conversely, Statementobjects work well when you have simple statements; and of course, you have nooption if your JDBC driver doesn’t support the PreparedStatementinterface

Working with Input Streams

One of the most intriguing features of the PreparedStatementinterface is the ability to use a stream asthe source of data to be inserted in a statement in place of a placeholder It’s very often more convenient

to deal with streams when you’re working with data types like LONGVARCHARand LONGVARBINARY Forexample, an application storing binary images can very efficiently populate a LONGVARBINARYcolumn

by creating a FileInputStreamobject representing the source file

The PreparedStatementinterface provides three methods for extracting data from input streams:

Trang 29

Method Description

setAsciiStream() Use for columns with the SQL type LONGVARCHARsetUnicodeStream() Use for columns with the SQL type LONGVARCHARsetBinaryStream() Use for columns with the SQL type LONGVARBINARY

Each of these methods requires three argument values that indicate the placeholder position, theInputStreamobject that is the source of the data, and the number of bytes to be read from the stream If

an end of file is encountered before the designated number of bytes have been read, the methods throw

an exception of type SQLException.The next example is a simple illustration of using the setAsciiStream()method of thePreparedStatementto store Java source code in a database It opens a Java source code file as anInputStreamobject and uses that InputStreamobject to populate a column in the database Accessdoes not support the LONGVARCHARSQL type, and you have to use LONGTEXTas the type for the fieldthat will store the source code in the CREATE TABLEcommand

Try It Out PreparedStatement and Input StreamsThe program starts out with the usual code for establishing a connection to the database Then aFileInputStreamobject is created from the source code file for this program The number of bytes con-tained by the file is obtained by calling the available()method:

String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String user = “guest”;

String password = “guest”;

FileInputStream fis = new FileInputStream(“TryInputStream.java”);

Trang 30

String ins = “INSERT INTO source_code VALUES(?,?)”;

PreparedStatement statement = connection.prepareStatement(ins);

// Set values for the placeholdersstatement.setString(1, “TryInputStream”); // Set first fieldstatement.setAsciiStream(2, fis, fis.available()); // Stream is sourceint rowsUpdated = statement.executeUpdate();

System.out.println(“Rows affected: “ + rowsUpdated);

connection.close();

} catch (Exception e) {System.err.println(e);

}}

}

The code can throw exceptions of types IOException, ClassNotFoundException, and

SQLException, and they all need to be caught The FileInputStreamconstructor and the

setAsciiStream()method of the PreparedStatementinterface can throw IOExceptionexceptions.Since all you’ll do in each case is output the exception to the error stream, you can economize on thecode by catching them all in the same catchblock that uses Exceptionas the type This works becauseall exception objects have the Exceptionclass as a base

You might want to check your results by running the InteractiveSQLapplication to verify that thetable was created and the rows were inserted Start that application and execute the SQL statement:

SELECT * FROM source_code

After you’re satisfied with your results, feel free to delete the table Having restarted the

InteractiveSQLapplication, execute the statement:

DROP TABLE source_code

Note that once the table exists, executing the CREATE TABLEcommand will fail, so if you want to run theexample more than once be sure to delete the table each time

How It Works

This program is very similar to the previous example AFileInputStreamobject is created from thefile TryInputStream.java Since the setXXXStream()methods need to know how many bytes toread from the stream, you have to get the file size of the TryInputStream.javafile by calling theavailable()method of the FileInputStreamobject

Trang 31

You first create the table by executing the CREATE TABLESQL command using the StatementobjectcreateTable Then the PreparedStatementobject statementis created, and the placeholder value forthe first column is set by calling the setString()method for the statementobject The real magic hap-pens in the setAsciiStream()method — all you have to do is supply the method with the placeholderposition, the InputStream, and the number of bytes to be read — returned by the available()methodfor the FileInputStreamobject When the SQLINSERTstatement is executed, the bytes are read fromthe stream and stored in the second column of the row inserted in the database table source_code.When your JDBC applications will be dealing with large chunks of data, the Streammethods of thePreparedStatementinterface are a real help.

The ResultSet

Now that you have a good understanding of the capabilities of the StatementandPreparedStatementinterfaces, it’s time to dig a little deeper into the details of getting the data backfrom the query In this section, you’ll add to what you learned about the ResultSetobject in the lastchapter You’ll explore the getXXX()methods in more depth and look at some of the special SQL datatypes and how they are handled You’ll also look at how to use streams with a ResultSetobject

Retrieving Column Data for Specified Data Types

So far you’ve retrieved data from a resultset as type Stringbecause data of any SQL type can beretrieved in this way As you saw briefly in the previous chapter, like the StatementandPreparedStatementinterfaces, the ResultSetinterface provides methods for working with a variety

of data types and retrieving data as a Java type that is more consistent with the original SQL type

Most of these methods work in a similar way and come in two overloaded forms One form specifies thecolumn by the column name:

xxxType resultSet.getXXX(String columnName)The other specifies the column name by its index position, the first position index being 1:

xxxType resultSet.getXXX(int columnPosition)The mechanics of calling these methods is quite straightforward, but to use these methods effectively,you need to understand the possible mappings between Java data types and SQL data types in bothdirections

Trang 32

The table in Figure 25-3 illustrates the mappings between SQL data types and the appropriate

ResultSet getXXX()methods To decide which getXXX()method you should use, look in the table forthe method that maps the column data type to the Java type you’ll use The “preferred” method for a

type is indicated with the Y character That means that it is the closest mapping to the SQL type Other methods that may also work are indicated by the ± symbol.

Figure 25-3

Working with Null Values

As I’ve said, NULLis a special value in the world of SQL NULLis not the same thing as an empty stringfor text columns, nor is it the same thing as zero for a numeric field NULLmeans that no data is defined

ResultSet Method to SQL Data Type Mapping

Trang 33

for a column value within a relation For example, recall the authorstable, which has several valuesthat may or may not have values assigned, including the emailcolumn To determine which authors donot have an e-mail address recorded, you could use the following query:

SELECT authid FROM authors WHERE email = NULLThis query will return the ID for each author without an e-mail address

The ResultSetinterface provides a method for testing a column value within a resultset to determine if

it is null The wasNull()method returns a booleanvalue that is trueif the last column read from theResultSetobject was a null, and falseif it was some other value

You’ll need to use the capability to detect a nullvalue for a field in your code unless you created yourtables with every column defined as NOT NULL, which tells the database system that it must never allow

a nullvalue in any column However, that’s not always a practical or desirable way to design tables.Let’s consider a simple example that selects and displays the author ID, last name, first name, and e-mailaddress for each row in the authorstable If any of these values are not assigned a value, the code couldthrow a NullPointerExceptionwhen the program attempts to display the value To avoid that sort ofbad program behavior, this example will use the wasNull()method of the ResultSetto check forempty fields Notice that the wasNull()method is called after the value is retrieved from theResultSet

Try It Out Testing for Null Values in the ResultSetHere’s the code for the example:

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String theStatement = “SELECT authid, lastname, firstname, email FROM authors”;try {

Class.forName(driver);

Connection connection = DriverManager.getConnection(url, “guest”, “guest”);Statement queryAuthors = connection.createStatement();

ResultSet results = queryAuthors.executeQuery(theStatement);

String lastname, firstname, email;

Trang 34

if(results.wasNull()) {email = “no email”;

}System.out.println(Integer.toString(id) + “, “ +

lastname.trim() + “, “ +firstname.trim() +”, “ +email.trim());

}queryAuthors.close();

} catch (Exception e) {System.err.println(e);

}}

}

Running this code produces the following results:

1, Gamma, Erich, no email

2, Helm, Richard, no email

3, Johnson, Ralph, no email

4, Horton, Ivor, no email

How It Works

In TestNullValues, the SQL statement is executed, and the values for the author ID, last name, firstname, and e-mail address are extracted into local variables The rows in the resultset are ordered byauthidbecause of the ORDER BYclause in the SQL query Because the value for emailcan be nullinthe table, you call the wasNull()method immediately after retrieving that column from resultsto test

if the value read was a nullvalue If so, you replace the literal string referenced by email, so outputtingthe report will work without throwing an exception Since the authid, lastname, and firstnamecolumns are required, there’s no need to test those column values for nullvalues

Working with Special Data Types

The JDBC java.sqlpackage defines some special data types to accommodate the characteristics of ticular SQL types that don’t readily map to a standard Java data type AResultSetclass object hasmethods for accessing data of these special types The ResultSetclass also defines methods that accessvalues of SQL types that map to Java data types defined in the java.mathpackage that are designed tohandle numbers with a large number of digits of precision These types are as follows:

❑ AtoString()method that formats the value of the date as a string in the form yyyy-mm-dd

❑ A static valueOf()method that converts a string representation (yyyy-mm-dd form) of a date

into a java.sql.Dateobject

Trang 35

❑ AsetTime()method that accepts an argument of type longthat is a millisecond value relative

to 1st January 1970, 00:00:00 GMT, and sets the date value encapsulated by the currentjava.sql.Dateobject to this value

Time

Like java.sql.Date, the java.sql.Timeclass wraps the java.util.Dateclass as a subclass Theclass defines a static valueOf()method that returns a Timeobject from a string representation

(hh:mm:ss form) of time into a Timeobject and a toString()method that returns a string representation

of the time encapsulated by the object in the form hh:mm:ss.

Timestamp

The java.sql.Timestampclass also subclasses java.util.Date, but provides additional support forSQL timestamps with support for nanoseconds (java.util.Datesupports time only to the nearest mil-lisecond) The static valueOf()method creates a Timestampobject from a string representation (yyyy-

and after()— to support nanoseconds

Big Numbers

The SQLNUMERICand DECIMALtypes are mapped to the Java BigDecimalclass type This class isdefined in the java.mathpackage along with the BigIntegerclass that defines objects that encapsu-late integers of arbitrary precision, with negative values in 2’s complement form ABigDecimalobjectdefines a decimal value of arbitrary precision that can be positive or negative A BigDecimalobject isimplemented as an arbitrary precision signed integer — a BigIntegerobject, plus a scale value thatspecifies the number of digits to the right of the decimal point Thus, the value of a BigDecimalobject isthe integer value divided by 10scale To read a column value of either NUMERICor DECIMALSQL type as aJava BigDecimalobject, you use the getBigDecimal()method for the ResultSetobject

The BigIntegerand BigDecimalclasses are very useful for applications that require a large number ofdigits of precision, such as security keys, very large monetary values, and so forth The BigIntegerandBigDecimalclasses provide mathematical methods for addition, subtraction, multiplication, and divi-sion, as well as comparison methods and methods for returning their value as standard Java types.Additionally, BigIntegerobjects support bitwise and shift operations The BigDecimalclass providesmethods for tailoring the rounding behavior in arithmetic operations

Like Java Stringobjects, the value of a BigIntegeror BigDecimalobject is immutable That is, once

an object has been created, you can’t change its value When you apply arithmetic operations toBigIntegerand BigDecimalobjects using their methods, such as multiply()and divide(), youalways get a new object as a result, in much the same way as you get a new Stringobject when you usethe concat()or substring()methods of String

You can get an idea of the usefulness of these classes if you consider the difficulties you would have ifyou had to use type doublefor computations where which you needed to maintain a great deal of accu-racy Suppose you had to calculate the product of the following two floating-point numbers:

98765423462576235623562346234623462.35632456234567890and

9898234523235624664376437634674373436547.34586558

Trang 36

If you were to calculate the product of two variables of type doublethat you have initialized with thesevalues, you would probably be very disappointed when your code produced the result:

9.776033242192577E74

Considering the number of digits of precision you entered originally for the factors, you could not claim that accuracy has been maintained Of course, the problem is that the precision for values of typedoubleis fixed and limited to the number of digits that you see in the preceding result Enter theBigDecimalclass Let’s see how it would work with that

Try It Out The BigDecimal Class

You can do the calculation and produce the sort of result that you want using BigDecimalobjects asfollows:

import java.math.BigDecimal;

public class TestBigDecimal {

public static void main(String[] args) {

BigDecimal bn1 = new BigDecimal(

How It Works

The BigDecimalclass has remarkable capabilities It can support numbers of virtually limitless

precision The precision and scale are both 32-bit signed integer values, so they can be as large as2,147,483,647 digits — and that’s a huge number of decimal digits! In the example, you create twoBigDecimalobjects, bn1and bn2, representing the original values that you want to multiply You multi-ply them using the multiply()method for bn1and store the reference to the BigDecimalobject that isreturned containing the result in bn3 You can use this in a println()method call since the

BigDecimalclass implements the toString()method

The BigDecimalclass defines methods that implement all of the usual arithmetic operators, add(),subtract(), multiply(), divide(), and remainder(), as well as a range of methods supportingother operations, including max(), min(), pow(), and compareTo() Just about anything that you can

do with a primitive numeric type you can also do with BigDecimalobjects

The BigIntegerclass is just as impressive — it provides the same arbitrary precision characteristics forinteger values Of course, there is a price to pay for that precision Computations using the BigIntegerand BigDecimalclasses are notably slower than their counterparts using native Java types

Trang 37

The BigIntegerand BigDecimalclasses manage digits as objects in a vector, so to get the flexibility ofunlimited precision, you have to trade off computing time for operations on the numbers Nonetheless,this class is invaluable for many applications.

Working with Streams

Earlier, you looked at using streams to populate LONGVARCHARand LONGVARBINARYcolumns becauseit’s frequently much easier to use streams when you’re working with large objects

The ResultSetinterface provides three methods for retrieving data from a database as a stream Thesemethods are:

getCharacterStream() Use for LONGVARCHARcolumnsgetBinaryStream() Use for LONGVARBINARYcolumns

Each of these methods requires an argument to be supplied that indicates the column either by name or

by index position The getCharacterStream()method returns a reference to a Readerobject, whereasthe other two methods return a reference to an InputStreamobject from which you can read the datafor the column

The next example shows a simple example of using the getAsciiStream()method of the ResultSet.This example extends the TryInputStream.javaprogram that you saw in the previous chapter

Try It Out ResultSet Columns as StreamsHere’s a version of the TryInputStream.javaprogram that uses a stream:

String url = “jdbc:odbc:technical_library”;

String driver = “sun.jdbc.odbc.JdbcOdbcDriver”;

String user = “guest”;

String password = “guest”;

FileInputStream fis = new FileInputStream(“TryInputStream2.java”);

Class.forName(driver);

Trang 38

Connection connection = DriverManager.getConnection(url, user, password);Statement createTable = connection.createStatement();

createTable.executeUpdate(

“CREATE TABLE source_code (name char(20), source LONGTEXT)”);

String ins = “INSERT INTO source_code VALUES(?,?)”;

PreparedStatement statement = connection.prepareStatement(ins);

statement.setString(1, “TryInputStream2”);

statement.setAsciiStream(2, fis, fis.available());

int rowsUpdated = statement.executeUpdate();

System.out.println(“Rows affected: “ + rowsUpdated);

// Create a statement object and execute a SELECTStatement getCode = connection.createStatement();

ResultSet theCode = getCode.executeQuery(

“SELECT name,source FROM source_code”);

BufferedReader reader = null; // Reader for a columnString input = null; // Stores an input linewhile(theCode.next()) { // For each row

// Create a buffered reader from the stream for a columnreader = new BufferedReader(

new InputStreamReader(theCode.getAsciiStream(2)));// Read the column data from the buffered reader

while((input = reader.readLine()) != null) { // While there is a lineSystem.out.println(input); // display it

}}connection.close();

} catch (Exception e) {System.err.println(e);

}}

}

Make sure that the source_codetable doesn’t exist before you run the program If it does, you candelete it using the InteractiveSQLprogram, as you’ve seen before You should see the text of thissource code printed out After you’re satisfied with your results, you can delete the table using theInteractiveSQLapplication

How It Works

Most of this code sets up and populates the source_codetable with the data from the program sourcefile, TryInputStream2.java Once that is done you get a Statementobject from the connection thatyou’ll use to retrieve the data from the table The whileloop iterates through all the rows in the resultsetthat contains the result of the SQL query in the way that you are now very familiar with

Using the ResultSetobject that is returned from executeQuery(), you get an InputStreamobjectcorresponding to the second column in the current row by calling its getAsciiStream()method withthe position index argument as 2 The InputStreamobject that is returned is used to create an

InputStreamReaderobject, which in turn is used to create a BufferedReaderobject that providesbuffered stream input for the column data The readLine()method for the BufferedReaderobject

Trang 39

returns a Stringobject containing a line of input When the end of the stream is reached, thereadLine()method returns nullso the inner whileloop will then terminatẹ

Calling Procedures

Many database systems support stored procedures, which are predefined sequences of SQL commands

that you can call when you want to execute the function that the stored procedure defines This is a verypowerful facility with a lot of ramifications, so I’ll only touch on the basics of how to use this here, just

so that you are aware of it JDBC provides support for this sort of capability through thejavạsql.CallableStatementinterface, which is derived from the PreparedStatementinterfacẹYou can obtain a CallableStatementreference corresponding to a stored procedure call by calling theprepareCall()method for a Connectionobject

The argument to the prepareCall()method is a Stringobject that defines a string in a format

described as SQL escape syntax The purpose of SQL escape syntax is to enable the driver to determine

that this is not an ordinary SQL statement and needs to be transformed into a form that will be stood by the database system This enables the idiosyncrasies of how stored procedures are called in dif-ferent database systems to be accommodated, since it is up to the driver to recognize that the string isSQL escape syntax and transform it to the format required by the underlying databasẹ Let’s first con-sider the simplest possible form for a string to call a stored procedure:

under-“{call procedureName}”

The braces are always present The procedure to be called has the name procedureNamẹ Given that youhave a Connectionobject connection, you could call the procedure with the following code:

CallableStatement call = connection.prepareCall(“{call procedureName}”);

ResultSet result = call.executeQuery(); // Execute the procedure callNow you can obtain the data from the ResultSetobject that is returned by the procedure usinggetXXX()methods in the usual waỵ This code assumes that the stored procedure produces a singleresultset Of course, it is possible that a procedure may produce multiple resultsets, in which case youwould use the execute()method to execute the procedure and getResultSet()to retrieve the result-set If the procedure updated the database, you would call the executeUpdate()method to execute it.Stored procedures can have arguments that specify input values (called INparameters) to the operation

In this case you specify the parameter list between parentheses following the procedure namẹ Eachparameter is denoted by ?, as for a PreparedStatementcommand, and you set the values for theparameters using the setXXX()methods in the way you have seen for PreparedStatementobjects Forexample:

CallableStatement call = connection prepareCall(“{call getMonthDatẳ, ?)}”);call.setInt(1, 6); // Set first argument valuecall.setInt(2,1999); // Set second argument valueResultSet result = call.executeQuery(); // Execute the procedure call

As you have seen, each parameter is identified by an index value, the first parameter having the indexvalue 1

Trang 40

Procedures can also have parameters for returning a result — referred to as OUTparameters — and youcan set these, toọ The placeholder for an OUTparameter is ?— no different from an INparameter — butobviously the process for setting the parameter is significantly different For each OUTparameter, youmust identify the type of the output value as one of the types defined in the javạsql.Typesclass bycalling the registerOutParameter()method for the CallableStatementobject The first argument

is the index position of the OUTparameter, and the second argument is the typẹ For example:

call.registerOutParameter(2, Types.INTEGER); // Second parameter OUT and INTEGER

Of course, if you don’t want to qualify the type constants from the Typesclass in the code, you canalways use a static importstatement to import the names into your source filẹ

Once the OUTparameters have been registered, you then execute the procedure call in the way you haveseen If a resultset is returned, you can access the data from that in the usual waỵ To get the value foreach OUTparameter, you must call the getXXX()method for the CallableStatementobject that corre-sponds to the parameter type, so for the preceding example you would write:

int value = call.getInt(2); // Read second parameter value

Procedures can also have parameters that serve as both input and output, so-called INOUTparameters

In this case you just combine what I’ve discussed for INand OUTparameters, using setXXX()for theinput value, registerOutParameter()to set the type for the output, and getXXX()to retrieve theoutput valuẹ

Finally, a stored procedure may return a value — not as part of the parameter list but as a return value asfor a method In this case you specify it as follows:

CallableStatement call = connection.prepareCall(“{? = call getDatẳ, ?)}”);

This has two parameters plus a return value specified by the first ?— preceding the =in the string Thisplaceholder is at index position 1, and the two other parameters will be at index position 2 and 3 Younow need to register the type of the value that is returned by the procedure before you execute the pro-cedure call You do this in the same way as for any other OUTparameter:

warn-Unfortunately, life is a bit less predictable than that, and you need to take some extra steps in your JDBCapplications to handle conditions that generate warnings or errors In this section, you’ll see how tobuild mechanisms to trap errors, how to use the extra facilities built into JDBC to get detailed warning

Ngày đăng: 13/08/2014, 18:20

TỪ KHÓA LIÊN QUAN