Sie sind auf Seite 1von 7

5 things you didn't know about ...

Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

5 things you didn't know about ... Java Database Connectivity


Update your relationship to the JDBC API Ted Neward, Principal, Neward & Associates Summary: JDBC, or Java Database Connectivity, is one of the most frequently used packages in the entire JDK, and yet few Java developers use it to its fullest or most up-to-date capacity. Ted Neward offers an introduction to newer JDBC features like ResultSets that automatically scroll and update on the fly, Rowsets that can work with or without an open database connection, and batch updates that can execute multiple SQL statements in one fast trip around the network. Date: 10 Aug 2010 Level: Introductory PDF: A4 and Letter (41KB | 11 pages)Get Adobe Reader Also available in: Chinese Japanese Portuguese Activity: 37722 views Comments: 6 (View | Add comment - Sign in) Average rating (34 votes) Rate this article

About this series


So you think you know about Java programming? The fact is, most developers scratch the surface of the Java platform, learning just enough to get the job done. In this series, Ted Neward digs beneath the core functionality of the Java platform to uncover little-known facts that could help you solve even the stickiest programming challenges. Many Java developers today know the Java Database Connectivity (JDBC) API by way of a data-access platform such as Hibernate or Spring. But JDBC is more than a background player in database connectivity. The more you know about it, the more efficient your RDBMS interactions will be. In this installment of the 5 things series, I'll demonstrate several of the newer features introduced between JDBC 2.0 and JDBC 4.0. Designed with modern software development challenges in mind, these features support application scalability and developer productivity two of the common challenges facing Java developers today. 1. Scalar functions Different RDBMS implementations offer irregular support for SQL and/or value-added features designed to make the developer's life easier. It's well known, for example, that SQL provides a scalar operation, COUNT(), to return the number of rows that meet a particular SQL filter criteria (that is, WHERE predicate). But beyond that, trying to modify values returned by SQL can be tricky and trying to get the current date and time from the database could drive even the most patient JDBC developer mad (and potentially bald, too). Toward that end, the JDBC specification provides for a degree of isolation/adaptation against different RDBMS implementations, via scalar functions. The JDBC specification includes a list of supported operations that JDBC drivers should recognize and adapt as necessary to their particular database implementation. Thus, for a database that supported returning the current date and/or time, it could be as simple as Listing 1: Listing 1. What time is it?
Connection conn = ...; // get it from someplace Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery({fn CURRENT_DATE()});

The full list of scalar functions recognized by the JDBC API is given in the appendix of the JDBC specification (see Resources), but the full list might not be supported by a given driver or database. You can use the DatabaseMetaData object returned from Connection to obtain the functions supported by a given JDBC implementation, as shown in Listing 2: Listing 2. What can you do for me?
Connection conn = ...; // get it from someplace DatabaseMetaData dbmd = conn.getMetaData();

The list of scalar functions is a comma-separated String returned from a variety of DatabaseMetaData methods. For example, all the numeric scalars are listed via the getNumericFunctions() call. Do a String.split() on the result and voil! instant equals()-testable list. 2. Scrollable ResultSets It's a very common procedure in JDBC to create a Connection object (or obtain an existing one) and use it to create a Statement. The Statement, being fed an SQL SELECT, returns a ResultSet. The ResultSet is then fed through a while loop (not unlike an Iterator) until the ResultSet says it's empty, with the body of the loop extracting one column at a time in left-to-right order. This whole operation is so common that is has become almost sacred: it's done that way simply because that's the way it's done. Alas, it's completely unnecessary.

1 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

Introducing the scrollable ResultSet Many developers are unaware of the fact that JDBC has been considerably enhanced over the years, even though those enhancements are reflected in new version numbers and releases. The first major enhancement, JDBC 2.0, occurred around the time of JDK 1.2. As of this writing, JDBC stands at version 4.0. One of the interesting (though frequently ignored) enhancements to JDBC 2.0 is the ability to "scroll" through the ResultSet, meaning we can go forward or backward, or even both, as need dictates. Doing so requires a bit of forward-thinking, however the JDBC call must indicate that it wants a scrollable ResultSet at the time the Statement is created.

Verifying ResultSet type


If you suspect a driver may not actually support scrollable ResultSets, despite what it says in the DatabaseMetaData, you can verify the ResultSet type by calling getType(). Of course, if you're that paranoid, you might not trust the return value of getType(), either. Suffice it to say, if getType() lies about the ResultSet returned, they really are out to get you. If the underlying JDBC driver supports scrolling, a scrollable ResultSet will be returned from that Statement, but it's best to figure out if the driver supports scrollability before asking for it. You can ask about scrolling via the DatabaseMetaData object, which can be obtained from any Connection, as described previously. Once you have a DatabaseMetaData object, a call to getJDBCMajorVersion() will determine whether the driver supports at least the JDBC 2.0 specification. Of course, a driver could lie about its level of support for a given specification, so to play it particularly safe, call the supportsResultSetType() method with the desired ResultSet type. (It's a constant on the ResultSet class; we'll talk about the values of each in just a second.) Listing 3. Can you scroll?
int JDBCVersion = dbmd.getJDBCMajorVersion(); boolean srs = dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE); if (JDBCVersion > 2 || srs == true) { // scroll, baby, scroll! }

Requesting a scrollable ResultSet Assuming your driver says yes (if it doesn't, you need a new driver or database), you can request a scrollable ResultSet by passing two parameters to the Connection.createStatement() call, shown in Listing 4: Listing 4. I want to scroll!
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet scrollingRS = stmt.executeQuery("SELECT * FROM whatever");

You have to be particularly careful when calling createStatement() because its first and second parameters are both ints. (Curse the fact that we didn't get enumerated types until Java 5!) Any int value (including the wrong constant value) will work with createStatement(). The first parameter, indicating the "scrollability" desired in the ResultSet, can be one of three accepted values:
ResultSet.TYPE_FORWARD_ONLY: This is the default, firehose-style cursor that we know and love. ResultSet.TYPE_SCROLL_INSENSITIVE: This ResultSet enables backward iteration as well as forward, but if the data in the database changes, the ResultSet won't reflect it. This scrollable ResultSet is probably the most commonly desired type. ResultSet.TYPE_SCROLL_SENSITIVE: The ResultSet created will not only allow for bidirectional iteration, but will also give a "live" view of

the data in the database as it changes. The second parameter is discussed in the next tip, so hang on. Directional scrolling Once you've obtained a ResultSet from the Statement, scrolling backward through it is just a matter of calling previous(), which goes backward a row instead of forward, as next() would. Or you could call first() to go back to the beginning of the ResultSet, or call last() to go to the end of the ResultSet, or ... well, you get the idea. The relative() and absolute() methods can also be helpful: the first moves the specified number of rows (forward if the value is positive, backward if the value is negative), and the latter moves to the specified row in the ResultSet regardless of where the cursor is. Of course, the current row number is available via getRow(). If you plan on doing a lot of scrolling in a particular direction, you can help the ResultSet by specifying that direction, by calling setFetchDirection(). (A ResultSet will work regardless of its scrolling direction but knowing beforehand allows it to optimize 3. Updateable ResultSets JDBC doesn't just support bidirectional ResultSets, it also supports in-place updates to ResultSets. This means that rather than create a new SQL its data retrieval.)

2 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

statement to change the values currently stored in the database, you can just modify the value held inside the ResultSet, and it will be automatically sent to the database for that column of that row. Asking for an updateable ResultSet is similar to the process involved in asking for a scrollable ResultSet. In fact, it's where you'll use the second parameter to createStatement(). Instead of specifying ResultSet.CONCUR_READ_ONLY for the second parameter, send ResultSet.CONCUR_UPDATEABLE, as shown in Listing 5: Listing 5. I'd like an updateable ResultSet, please
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATEABLE); ResultSet scrollingRS = stmt.executeQuery("SELECT * FROM whatever");

Assuming your driver supports updateable cursors (that's another feature of the JDBC 2.0 specification, which most "real-world" databases will support), you can update any given value in a ResultSet by navigating to that row and calling one of the update...() methods on it (shown in Listing 6). Like the get...() methods on ResultSet, update...() is overloaded for the actual column type in the ResultSet. So to change the floating-point column named "PRICE", call updateFloat("PRICE"). Doing so only updates the value in the ResultSet, however. To push the value to the database backing it, call updateRow(). If the user changes his or her mind about changing the price, a call to cancelRowUpdates() will kill all pending updates. Listing 6. A better way
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATEABLE); ResultSet scrollingRS = stmt.executeQuery("SELECT * FROM lineitem WHERE id=1"); scrollingRS.first(); scrollingRS.udpateFloat("PRICE", 121.45f); // ... if (userSaidOK) scrollingRS.updateRow(); else scrollingRS.cancelRowUpdates();

JDBC 2.0 supports more than just updates. If the user wants to add a completely new row, rather than create a new Statement and execute an call moveToInsertRow(), call update...() for each column, then call insertRow() to complete the work. If a column value isn't specified, it's assumed to be an SQL NULL (which might trigger an SQLException if the database schema doesn't allow NULLs for that column).
INSERT, just

Naturally, if the ResultSet supports updating a row, it must also support deleting one, via deleteRow(). Oh, and before I forget, all of this scrollability and updateability applies equally to PreparedStatement (by passing those parameters to the prepareStatement() method), which is infinitely preferable to a regular Statement due to the constant danger of SQL injection attacks. 4. Rowsets If all this functionality has been in JDBC for the better part of a decade, why are most developers still stuck on forward-scrolling ResultSets and disconnected access? The main culprit is scalability. Keeping database connections to a minimum is key to supporting the massive numbers of users that the Internet can bring to a company's web site. Because scrolling and/or updating ResultSets usually requires an open network connection, many developers will not (or cannot) use them. Fortunately, JDBC 3.0 introduced an alternative that lets you do many of the same things you would with a ResultSet, without necessarily needing to keep the database connection open. In concept, a Rowset is essentially a ResultSet, but one which allows for either a connected or disconnected model. All you need to do is create a Rowset, point it at a ResultSet, and when it's done populating itself, use it as you would a ResultSet, shown in Listing 7: Listing 7. Rowset replaces ResultSet
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATEABLE); ResultSet scrollingRS = stmt.executeQuery("SELECT * FROM whatever"); if (wantsConnected) JdbcRowSet rs = new JdbcRowSet(scrollingRS); // connected else CachedRowSet crs = new CachedRowSet(scrollingRS); disconnected

JDBC comes with five "implementations" (meaning extended interfaces) of the Rowset interface. JdbcRowSet is a connected Rowset implementation; the remaining four are disconnected:
CachedRowSet

is just a disconnected Rowset.

3 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

WebRowSet is a

subclass of CachedRowSet that knows how to transform its results to XML and back again. that also knows how to form the equivalent of an SQL JOIN without having to connect back to the database.

JoinRowSet is a WebRowSet FilteredRowSet

is a WebRowSet that also knows how to further filter the data handed back without having to connect back to the database.

Rowsets are full JavaBeans, meaning they support listener-style events, so any modifications to the Rowset can be caught, examined, and acted upon, if desired. In fact, Rowset can even manage the complete act against the database if it has its Username, Password, URL, and DatasourceName properties set (which means it will create a connection using DriverManager.getConnection()) or its Datasource property set (which was probably obtained via JNDI). You would then specify the SQL to execute in the Command property, call execute(), and start working with the results no further work required. Rowset Rowset

implementations are generally provided by the JDBC driver, so the actual name and/or package will depend on what JDBC driver you use. implementations have been a part of the standard distribution since Java 5, so you should be able to just create a ...RowsetImpl() and go. (In the unlikely event that your driver doesn't provide one, Sun offers a reference implementation; see Resources for the link.)

5. Batch updates Despite their usefulness, Rowsets sometimes just don't meet all your needs, and you may need to fall back to writing straight SQL statements. In those situations, particularly when you're facing a slew of work, you might appreciate the ability to do batch updates, executing more than one SQL statement against the database as part of one network round-trip. To determine whether the JDBC driver supports batch updates, a quick call to the DatabaseMetaData.supportsBatchUpdates() yields a boolean telling the story. Assuming batch updates are supported (indicated by anything non-SELECT), queue one up and release it in a blast, like in Listing 8: Listing 8. Let the database have it!
conn.setAutoCommit(false); PreparedStatement pstmt = conn.prepareStatement("INSERT INTO lineitems VALUES(?,?,?,?)"); pstmt.setInt(1, 1); pstmt.setString(2, "52919-49278"); pstmt.setFloat(3, 49.99); pstmt.setBoolean(4, true); pstmt.addBatch(); // rinse, lather, repeat int[] updateCount = pstmt.executeBatch(); conn.commit(); conn.setAutoCommit(true);

The call to setAutoCommit() is necessary because by default, the driver will try to commit every statement that it is fed. Other than that, the rest of the code is pretty straightforward: do the usual SQL thing with the Statement or PreparedStatement, but instead of calling execute(), invoke executeBatch(), which queues the call instead of sending it right away. When the whole mess of statements is ready to go, fire them all at the database with executeBatch(), which returns an array of integer values, each of which holds the same result as if executeUpdate() had been used. In the event that a statement in the batch fails, if the driver doesn't support batch updates, or if a statement in the batch returns a ResultSet, the driver will throw a BatchUpdateException. In some cases, the driver may have tried to continue executing statements after an exception was thrown. The JDBC specification doesn't mandate particular behavior, so you are advised to experiment with your driver beforehand, so that you know exactly how it behaves. (But of course you'll be running unit tests, so you'll discover the error long before it becomes a problem, right?) In conclusion As a staple of Java development, the JDBC API is something that every Java developer should know like the back of his or her hand. The funny thing is, most developers haven't kept up with enhancements to the API over the years, and so they miss out on the time-saving tricks described in this article. Whether you decide to use JDBC's newer features is up to you, of course. A key aspect to consider will be the scalability of the system you're working on. The higher it needs to scale, the more constrained your use of the database will be, and thus the more you'll need to reduce network traffic against it. Rowsets, scalar calls, and batch updates will be your friends here. Otherwise, try the scrollable and updateable ResultSets (which don't consume as much memory as Rowsets do), and measure the scalability hit. It probably won't be as bad as you expect. Coming up next in the 5 things series: command-line flags.

Resources Learn 5 things you didn't know about ... : Find out how much you don't know about the Java platform, in this series dedicated to turning Java technology trivia into useful programming tips.

4 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

"Get a head start with JDBC 4.0 using Apache Derby" (Victor J. Soderberg, developerWorks, August 2006): This tutorial demonstrates some of the functions of the JDBC 4.0 specification in conjunction with the Apache Derby database. "JDBC 4.0 enhancements in Java SE 6" (Srini Penchikala, OnJava.com, August 2006): More about time-saving additions to the most recent JDBC API version release. developerWorks Java technology zone: Hundreds of articles about every aspect of Java programming. Get products and technologies JDBC 4.0 API Specification: Get the latest JDBC specification and learn it like the back of your own hand. JDBC Rowset Implementations 1.0.1 Specification: Download the JDBC Rowset RI. Discuss Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis. About the author

Ted Neward is the principal of Neward & Associates, where he consults, mentors, teaches, and presents on Java, .NET, XML Services, and other platforms. He resides near Seattle, Washington.

Close [x]

developerWorks: Sign in
If you do not have an IBM ID and password, register here. IBM ID: Forgot your IBM ID? Password: Forgot your password? Change your password? After sign in: By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a My developerWorks profile is created for you. This profile includes the first name, last name, and display name contained in the profile you created when you registered with My developerWorks. Selected information in your My developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post. All information submitted is secure. Close [x]

Choose your display name


The first time you sign in to developerWorks a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks. Display name: (Must be between 3 31 characters.)

Note: Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email for privacy reasons. By clicking Submit, you agree to the developerWorks terms of use.

5 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

All information submitted is secure. Average rating (34 votes) 1 star 2 stars 3 stars 4 stars 5 stars 1 star 2 stars 3 stars 4 stars 5 stars

Add comment: Sign in or register to leave a comment. Note: HTML elements are not supported within comments.

1000 characters left

Total comments (6) this post was really helpful for me.. thanks.... Posted by rajasekhar911 on 22 September 2010 Report abuse

Show:

"Other than that, the rest of the code is pretty straightforward: do the usual SQL thing with the Statement or PreparedStatement, but instead of calling execute(), invoke executeBatch(), which queues the call instead of sending it right away." Shouldn't executeBatch() be addBatch() in the above sentence? Posted by AlexiJordanov on 29 August 2010 Report abuse @Rico: You're spot-on; the specification pretty clearly states (13.4.1 in the 4.0 spec) that it should read { fn CURRENT_DATE() } (or, optionally, { fn CURRENT_DATE} , since that function takes no arguments). Not sure where my head was at when I wrote that. We'll get it fixed. Posted by TedNeward on 24 August 2010 Report abuse Small syntax error in listing 1, the escape syntax to access a scalar function is: {fn <function-name> (argument list)} Good article. Posted by Rico.Neubauer on 23 August 2010 Report abuse Very informative. But you have missed the trade-offs of some of the above features, like Updateable ResultSet, as JDBC doesn't really lock the row for update. Any change to the retrieved row, by another session, can throw Value Conflict SQL Exception, hence cannot be used in Transaction processing systems. Nice work again Posted by sajidsj on 16 August 2010 Report abuse

6 sur 7

08/01/2011 13:31

5 things you didn't know about ... Java Database Connectivity

http://www.ibm.com/developerworks/java/library/j-5things10.html

Trademarks | My developerWorks terms and conditions

7 sur 7

08/01/2011 13:31