Appendix A: Rejected design choices
A.1 Use Holder types rather than get/set methods.
In earlier drafts of JDBC we used a mechanism of "Holder" types to pass parameters and to
obtain results. This mechanism was an attempt to provide a close analogue to the use of pointers
to variables in ODBC. However as we tried to write test examples we found the need to
create and bind Holder types fairly irksome, particularly when processing simple row results.
We therefore came up with the alternative design using the getXXX and setXXX methods that
is described in Sections 7.2 and 7.1. After comparing various example programs we decided
that the getXXX/setXXX mechanism seemed to be simpler for programmers to use. It also removed
the need to define a dozen or so Holder types as part of the JDBC API. So we decided
to use the getXXX/setXXX mechanism and not to use Holders.
A.1.1 Using Holder types to pass parameters
As part of the java.sql API, we define a set of Holder types to hold parameters to SQL statements.
There is an abstract base class Holder, and then specific subtypes for different Java
types that may be used with SQL. For example, there is a StringHolder to hold a String parameter
and a ByteHolder to hold a byte parameter.
To allow parameters to be passed to SQL statements, the java.sql.Statement class allows you
to associate Holder objects with particular parameters. When the statement is executed any IN
or INOUT parameter values will be read from the corresponding Holder objects, and when the
statement completes, then any OUT or INOUT parameters will get written back to the corresponding
Holder objects.
An example of IN parameters using Holders:
An example of OUT parameters using Holders:
A.1.2 Getting row results using Holder objects
Before executing a statement, we can allow the application programmers to bind Holder objects
to particular columns. After the statement has executed, the application program can iterate
over the ResultSet using ResultSet.next() to move to successive rows. As the application
moves to each row, the Holder objects will be populated with the values in that row. This is
similar to the SQLBindColumn mechanism used in ODBC.
Here's a simple example:
A.2 Design Alternative: Don't use types such as fooHolder, instead use foo[]
At some point in the future we would probably like to add support for some form of column-wise
binding, so that a bunch of rows can be read at once. When we were using the Holder design,
we considered the following design alternative that would allow for column-wise binding.
Holder objects are capable of holding single instances of various Java types. However an array
of a single element could instead be used as a holder. This approach has several disadvantages,
but one major advantage.
The first disadvantage is that people may be confused if they read "foo f[] = new foo[1];". The
corresponding holder declaration "fooHolder f = new fooHolder();" gives a better clue as to
what f is and why we are allocating it.
The second disadvantage is that we would have to replace the single method Statement.bindColumn
with a distinct method for each array type. This is because all our Holder types inherit
from java.sql.Holder and can therefore be passed as arguments to a generic method that takes
a java.sql.Holder argument. (On the other hand at least we avoid defining the dozen or so holder
classes.)
The last disadvantage is that using foo[] only gives us the raw Java type information. By defining
a specific set of holder types for use with SQL, we can define extra fields and/or semantics,
e.g. for the CurrencyHolder type.
The corresponding major advantage is that if we use foo[1] as the container for a parameter
then it is very natural to allow foo[x] as a way of binding multiple rows of a table in column-
wise binding. This would let us add support for column-wise binding without having to remodel
the interface.
If we use arrays instead of Holders, them the bindColumn mechanism makes it easier to scale
up to column-wise binding.
A.3 Support for retrieving multiple rows at once
Currently we provide methods for retrieving individual columns within individual rows, a field
at a time. We anticipate that drivers will normally prefetch rows in larger chunks so as to reduce
the number of interactions with the target database. However, it might also be useful to allow
programmers to retrieve data in larger chunks through the JDBC API.
The easiest mechanism to support in Java would probably be to support some form of column-
wise binding where a programmer can specify a set of arrays to hold (say) the next 20 values
in each of the columns, and then read all 20 rows at once.
However we do not propose to provide such a mechanism in the first version of JDBC. We do
recommend that drivers should normally prefetch rows in suitable chunks.
A.4 Columns numbers in ResultSet.get methods
In an earlier version of the JDBC spec, the various "get" methods took no arguments, but merely
returned the next column value in left-to-right order. We (re)introduced a column number
argument because we were unsatisfied with the readability of the resulting example code. We
frequently found ourselves having to count through the various "get" calls in order to match
them up with the columns specified in the SELECT statement.
A.5 Method overloading for set methods
In an earlier version of the design we used method overloading so that rather than having methods
with different names such as setByte, setBoolean, etc., all these methods were simply
called setParameter, and were distinguished only by their different argument types. While this
is a legal thing to do in Java, several reviewers commented that it was confusing and was likely
to lead to error, particularly in cases where the mapping between SQL types and Java types is
ambiguous. On reflection we agreed with them.
A.6 That wretched registerOutParameter method
We dislike the need for a registerOutParameter method. During the development of JDBC we
made a determined attempt to avoid it and instead proposed that the drivers should use database
metadata to determine the OUT parameter types. However reviewer input convinced us that for
performance reasons it was more appropriate to require the use of registerOutParameter to
specify OUT parameter types.
A.7 Support for large OUT parameters.
We don't currently support very large OUT parameters. If we were to provide a mechanism for
very large OUT parameters, it would probably consist of allowing programmers to register java.io.OutputStreams
into which the JDBC runtimes could send the OUT parameter's data
when the statement executes. However, this seems to be harder to explain than it is worth, given
that there is a already a mechanism for handling large results as part of ResultSet.
A.8 Support for GetObject versus getXXX
Because of the overlap between the various get/set methods and the generic getObject/setObject
methods we looked at discarding our get/set methods and simply using getObject/setObject.
However for the simple common cases where a programmer know the SQL types, the
resulting casts and extracts are extremely tedious:
int i = ((Integer)r.getObject(1, java.sql.Types.INTEGER)).intValue()
We therefore decided to bend our minimalist principles a little in this case and retain the various
get/set methods as the preferred interface for the majority of applications programmers, while
also adding the getObject/setObject methods for tool builders and sophisticated applications
A.9 isNull versus wasNull
We had some difficulty in determining a good way of handling SQL NULLs. However by
JDBC 0.50 we had designed a ResultSet.isNull method that seemed fairly pleasant to use. The
isNull method could be called on any column to check for NULL before (or after) reading the
column.
Unfortunately, harsh reality intervened and it emerged that "isNull" could not be implemented
reliably on all databases. Some databases have no separate means for determining if a column
is null other than reading the column and they would only permit a given column to be read
once. We looked at reading the column value and "remembering" it for later use, but this
caused problems when data conversions were required.
After examining a number of different solutions we reluctantly decided to replace the isNull
method with the wasNull method. The wasNull method merely reports whether the last value
read from the given ResultSet (or CallableStatement) was SQL NULL.
A.10 Use of Java type names v SQL type names.
In an earlier version of the spec we used getXXX and setXXX method for retrieving results and
accessing parameters, where the XXX was a SQL type name. In revision 0.70 of JDBC we
changed to use getXXX and setXXX methods where the XXX was a Java type name.
Thus for example, getChar was replaced by getString and setSmallInt by setShort.
The new methods have essentially the same semantics as the methods that they replace. However
the use of Java type names makes the meaning of each of the methods clearer to Java programmers.
Contents | Prev | Next
jdbc@wombat.eng.sun.com
or
jdbc-odbc@wombat.eng.sun.com
Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved.