Spec-Zone .ru
спецификации, руководства, описания, API
|
NOTE: The material in this chapter is based on JDBCtm API Tutorial and Reference, Second Edition: Universal Data Access for the Javatm 2 Platform, published by Addison Wesley as part of the Java series, ISBN 0-201-43328-1.
The PreparedStatement
interface inherits from Statement
and differs from it in two ways:
PreparedStatement
contain an SQL statement that has already been compiled. This is what makes a statement "prepared."
PreparedStatement
object may have one or more IN parameters. An IN parameter is a parameter whose value is not specified when the SQL statement is created. Instead, the statement has a question mark ("?"
) as a placeholder for each IN parameter. The "?" is also known as a parameter marker. An application must set a value for each question mark in a prepared statement before executing the prepared statement.
Because PreparedStatement
objects are precompiled, their execution can be faster than that of Statement
objects. Consequently, an SQL statement that is executed many times is often created as a PreparedStatement
object to increase efficiency.
Being a subclass of Statement
, PreparedStatement
inherits all the functionality of Statement
. In addition, it adds a set of methods that are needed for setting the values to be sent to the database in place of the placeholders for IN parameters. Also, the three methods execute
, executeQuery
, and executeUpdate
are modified so that they take no argument. The Statement
forms of these methods (the forms that take an SQL statement parameter) should never be used with a PreparedStatement
object.
The following code fragment, where con is a Connection
object, creates a PreparedStatement
object containing an SQL update statement with two placeholders for IN parameters:
PreparedStatement pstmt = con.prepareStatement( "UPDATE table4 SET m = ? WHERE x = ?");
The object pstmt now contains the statement "UPDATE table4 SET m = ? WHERE x = ?"
, which has already been sent to the DBMS and been prepared for execution.
As with Statement
objects, it is possible to create a PreparedStatement
object that contains a query rather than an update statement; in fact, this is often done to improve efficiency for SQL statements that are executed many times. Using the new version of the method prepareStatement
included in the JDBC 2.0 core API, the PreparedStatement
object can produce ResultSet
objects that are scrollable and updatable. For example, the following code fragment creates a PreparedStatement
object such that each time it is executed, it will produce a ResultSet
object that is scrollable and updatable.
PreparedStatement pstmt2 = con.prepareStatement( "SELECT a, b, c FROM Table1", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt2.executeQuery();
The object that rs represents is a result set with all the values stored in columns a
, b
, and c
of Table1
, and rs is scrollable and can be updated. Each time pstmt2 is executed, it will produce a result set that is scrollable and updatable.
Before a PreparedStatement
object is executed, the value of each ?
parameter must be set. This is done by calling a setXXX
method, where XXX
is the appropriate type for the parameter. For example, if the parameter is of type long
in the Java programming language, the method to use is setLong
. The first argument to the setXXX
methods is the ordinal position of the parameter to be set, with numbering starting at 1. The second argument is the value to which the parameter is to be set. For example, the following code sets the first parameter to 123456789
and the second parameter to 100000000
:
pstmt.setLong(1, 123456789); pstmt.setLong(2, 100000000);
Once a parameter value has been set for a given statement, it can be used for multiple executions of that statement until it is cleared by a call to the method clearParameters
or until a new value is set.
When a connection has its auto-commit mode enabled, each statement is committed automatically when it is completed. Some database systems do not retain prepared statements across commits, so for them, the driver will have to recompile the prepared statement after each commit. This means that for these DBMSs, it may actually be less efficient to use a PreparedStatement
object in place of a Statement
object that is executed many times.
Using pstmt, the PreparedStatement
object created above, the following code illustrates setting values for the two parameter placeholders and executing pstmt 10 times. In this example, the first parameter is set to "Hi
" and remains constant. The second parameter is set to a different value each time around the for
loop, starting with 0
and ending with 9
.
pstmt.setString(1, "Hi"); for (int i = 0; i < 10; i++) { pstmt.setInt(2, i); int rowCount = pstmt.executeUpdate(); }
New features in the JDBC 2.0 API make it possible to set a parameter placeholder with an SQL3 data type, as shown in the following example, where statistics is a Blob
object representing an SQL BLOB
value, and departments is an Array
object representing an SQL ARRAY
value.
PreparedStatement pstmt = con.prepareStatement( "UPDATE Table3 SET Stats = ? WHERE Depts = ?"); pstmt.setBlob(1, statistics); pstmt.setArray(2, departments);
The XXX
in a setXXX
method is a type in the Java programming language. It also implicitly specifies a JDBC type because the driver will map the Java type to its corresponding JDBC type (following the mapping specified in "Java Types Mapped to JDBC Types" on page 106) and send that JDBC type to the database. For example, the following code fragment sets the second parameter of the PreparedStatement
object pstmt to 44
, with a Java type of short
:
pstmt.setShort(2, 44);
The driver will send 44
to the database as a JDBC SMALLINT
, which is the standard mapping from a Java short
.
It is the programmer's responsibility to make sure that the type in the Java programming language for each IN parameter maps to a JDBC type that is compatible with the JDBC data type expected by the database. Consider the case where the database expects a JDBC SMALLINT
. If the method setByte
is used, the driver will send a JDBC TINYINT
to the database. This will probably work because many database systems convert from one related type to another, and generally a TINYINT
can be used anywhere a SMALLINT
is used. However, for an application to work with the most database systems possible, it is best to use types in the Java programming language that correspond to the exact JDBC types expected by the database. If the expected JDBC type is SMALLINT
, using setShort
instead of setByte
will make an application more portable. The table "Java Types Mapped to JDBC Types" in the chapter "Mapping SQL and Java Types" can be used to determine which setXXX
method to use.
A programmer can explicitly convert an input parameter to a particular JDBC type by using the method setObject
. This method can take a third argument, which specifies the target JDBC type. The driver will convert the Object
in the Java programming language to the specified JDBC type before sending it to the database.
If no JDBC type is given, the driver will simply map the Java Object
to its default JDBC type and then send it to the database. This is similar to what happens with the regular setXXX
methods; in both cases, the driver maps the Java type of the value to the appropriate JDBC type before sending it to the database. The difference is that the setXXX
methods use the standard mapping, whereas the setObject
method uses the mapping to object types.
The capability of the method setObject
to accept any Java object allows an application to be generic and accept input for a parameter at run time. In this situation the type of the input is not known when the application is compiled. By using setObject
, the application can accept any Java object type as input and convert it to the JDBC type expected by the database.
The JDBC 2.0 core API includes a new implementation of the method setObject
that applies to a user-defined type (UDT) that has been custom mapped to a class in the Java programming language. The custom mapping of an SQL UDT is specified in a class that implements the SQLData
interface. When a UDT instance is retrieved from the database via the method getObject
, it will be mapped to an instance of the Java class that implemented SQLData
for it. When that custom mapped instance is passed to the method setObject
, setObject
will call the SQLOutput.writeObject
method that is defined in the appropriate SQLData
implementation, thereby converting the instance of a Java class back to an SQL UDT.
The details of custom mapping are hidden from the user. When an application invokes the method setObject
, the value being stored will automatically be custom mapped if there is a custom mapping for it. As a result, code in which the method setObject
performs a custom mapping looks identical to code in which setObject
uses the standard mapping. UDTs can only be stored using the setObject
method, which is a way of ensuring that UDTs with a custom mapping are mapped appropriately.
In all of the cases discussed so far, the value passed to the method setObject
was originally an SQL data type that was retrieved from a table column. Before returning it to the database, the driver needed to convert it back to its SQL data type. If a database is one of the new generation of Java-aware DBMSs, called a Java relational DBMS, it can store an instance of a class defined in the Java programming language as well as values defined in SQL. A class instance may be stored as a serialized Java object or in some other format defined by the DBMS.
The following example shows the use of the method setObject
to store emp, an instance of the class Employee
. After the salary field of emp is increased by 50 per cent, emp is sent back to the database. The column EMPLOYEE
in the table PERSONNEL
stores instances of Employee
.
emp.salary = emp.salary * 1.5; PreparedStatement pstmt = con.prepareStatement( "UPDATE PERSONNEL SET EMPLOYEE = ? WHERE EMPLOYEE_NO = 300485"); pstmt.setObject(1, emp); pstmt.executeUpdate();
Note that the syntax in this example is the same as that in the JDBC 1.0 API and is also the same as that used to store instances of UDTs that have been custom mapped.
The setNull
method allows a programmer to send a JDBC NULL
(a generic SQL NULL
) value to the database as an IN parameter. Note, however, that one must still specify the JDBC type of the parameter.
A JDBC NULL
will also be sent to the database when a Java null
value is passed to a setXXX
method (if it takes Java objects as arguments). The method setObject
, however, can take a null
value only if the JDBC type is specified.
The methods setBytes
and setString
are capable of sending unlimited amounts of data. Sometimes, however, programmers prefer to pass in large blobs of data in smaller chunks. This can be accomplished by setting an IN parameter to a Java input stream. When the statement is executed, the JDBC driver will make repeated calls to this input stream, reading its contents and transmitting those contents as the actual parameter data.
The JDBC 1.0 API provides two methods for setting IN parameters to input streams: setBinaryStream
for streams containing uninterpreted bytes and setAsciiStream
for streams containing ASCII characters. A third method, set-UnicodeStream
for streams containing Unicode characters, has been deprecated; the new JDBC 2.0 core API method setCharacterStream
should be used in its place. These stream methods take one more argument than the other setXXX
methods because the total length of the stream must be specified. This is necessary because some database systems need to know the total transfer size before any data is sent.
The following code illustrates using a stream to send the contents of a file as an IN parameter.
java.io.File file = new java.io.File("/tmp/data"); int fileLength = file.length(); java.io.InputStream fin = new java.io.FileInputStream(file); java.sql.PreparedStatement pstmt = con.prepareStatement( "UPDATE Table5 SET stuff = ? WHERE index = 4"); pstmt.setBinaryStream (1, fin, fileLength); pstmt.executeUpdate();
When the statement executes, the input stream fin
will get called repeatedly to deliver up its data.
Another way to send large IN parameters to the database is to use SQL3 types like BLOB
and CLOB
. This is different from using streams in that BLOB
and CLOB
values are originally retrieved from the database, where they were created as SQL types. Using streams makes it possible to send the contents of a file written in the Java programming language to the database.
The JDBC 2.0 core API provides the ability to send multiple updates to the database for execution as a batch. The Statement
method addBatch
is given an SQL update statement as a parameter, and the SQL statement is added to the Statement
object's list of commands to be executed in the next batch. The interface PreparedStatement
has its own version of the method addBatch
, which adds a set of parameters to the batch, as shown in the following code fragment.
PreparedStatement pstmt = con.prepareStatement( "UPDATE Table4 SET History = ? WHERE ID = ?"); pstmt.setClob(1, clob1); pstmt.setLong(2, 350985839); pstmt.addBatch(); pstmt.setClob(1, clob2); pstmt.setLong(2, 350985840); pstmt.addBatch(); int [] updateCounts = pstmt.executeBatch();
When the PreparedStatement
object in pstmt is executed, it will be executed twice, once with the parameters clob1
and 350985839, and a second time with the parameters clob2
and 350985840. If either update command returns anything other than a single update count, the method executeBatch
will throw an exception.