Is the JDBC ResultSet an application-level query cursor - java

The database cursor definition is strikingly resembling with the JDBC ResultSet API.
the database cursor can be forward-only just like ResultSet.TYPE_FORWARD_ONLY.
the database cursor can be scrollable and even have a sensitivity setting just like ResultSet.TYPE_SCROLL_SENSITIVE.
there is also support for holdability like ResultSet.HOLD_CURSORS_OVER_COMMIT
and even the support for positional update/delete is being replicated into JDBC ResultSet.CONCUR_UPDATABLE
But in spite of all these resembling, MySQL doesn't support database cursors:
MySQL does not support SQL cursors, and the JDBC driver doesn't
emulate them, so setCursorName() has no effect.
So, is the JDBC implementation a data access specification that mimics a database cursor implementation, even if the database doesn't really support such a feature?

What's in a name...
Indeed, a ResultSet and a database cursor are semantically similar. The SQL:2011 standard specifies:
A cursor is a mechanism by which the rows of a table may be acted on (e.g., returned to a host programming language) one at a time.
That does sound a lot like a ResultSet. Further down, the SQL:2011 standard goes on and mentions:
A cursor declaration descriptor and a result set descriptor have four properties: the sensitivity property (either SENSITIVE, INSENSITIVE, or ASENSITIVE), the scrollability property (either SCROLL or NO SCROLL), the holdability property (either WITH HOLD or WITHOUT HOLD), and the returnability property (either WITH RETURN or WITHOUT RETURN).
In other words, none of these features were "invented" by the JDBC (or ODBC) spec teams. They do exist exactly in this form in many SQL database implementations, and as with any specs, many of the above features are optional in SQL implementations as well.
You've gotten an authoritative response on the MySQL part already by Jess. I'd like to add that JDBC, like any specification on a high level, has parts that are required and parts that are optional.
Looking at the JDBC Spec, I can see the following relevant parts.
6.3 JDBC 4.2 API Compliance
A driver that is compliant with the JDBC specification must do the following:
[...]
It must implement the Statement interface with the exception of the following
optional methods:
[...]
setCursorName
[...]
It must implement the ResultSet interface with the exception of the following
optional methods:
[...]
getCursorName
[...]
The same is true for the implementation of ResultSet types. Further down in the specs, you will find:
The method DatabaseMetaData.supportsResultSetType returns true if the
specified type is supported by the driver and false otherwise.

You can certainly think of it that way. All of these concepts are inherited from ODBC so you can thank (blame?) history for things being this way. Cursors aren't widely supported by most dbs to the full extent that features are provided in APIs such as JDBC. In MySQL specifically, there is a cursor "fetch" supported as of MySQL 5.0 which means that the driver isn't forced to read the entire result, whether it's needed or not. This means that it is possible to abandon a result set early with little to no cost. However, an additional round-trip is required to request blocks of rows periodically. MySQL Connector/J doesn't enforce the FORWARD_ONLY semantics by default and buffers the entire result in the client allowing "scrollability". However, due to the implementation in the server, this does not allow for being sensitive to changes committed in other transactions. Features are typically mimicked/emulated where possible to provide the convenience of the API.

Based on my understanding about JDBC ResultSet i will say it does not depends Database which it connects, its behaviour would be same.
JDBC will always fetches default number of rows (not the entire result set) to your local memory. Once you reach at the last line of the fetched rows (say by doing next() and try to access next row) and if there are more rows in the result, then another round-trip call will be made to the database to fetch next batch of rows to local memory.
Even you can set number of rows you want fetch in local memory than usual, you may consider CachedRowSet.
When you set the fetchSize() on the Statement, you are only giving a instruction to the JDBC driver how much you want it should fetch, but JDBC driver is free to ignore your instructions. I do not know what the Oracle driver does with the fetchSize(). Most of times its observed that MySQL JDBC driver will always fetch all rows unless you set the fetchSize() to Integer.MIN_VALUE.

Related

New to SQL - Organization and Optimization of Queries

For a thick-client project I'm working on, I have to remotely connect to a database (IBM i-series) and perfom a number of SQL related tasks:
Download/Update a set of local/offline 'control' data - this data may have changed between runs unnoticed.
On command, download data from multiple (15-20) tables and store separately into a single Java object. The names of the tables are known, but the schema name changes between runs and can change inter-run (as far as I know, PreparedStatements do not allow one to dynamically insert the schema).
I had considered using joins/unions/etc to perform all of these queries as one, but the project requires me to have in-memory separations between table data (instead of one big joined lump).
Perform between 2 and 100+ repetitions of (2)
The last factor is that this needs to be run on high-latency (potentially dial-up) network connections using Java 1.5 on the oldest computers possible.
Currently I run 15-20 dynamically constructed PreparedStatements but I know this to be rather inefficient (I measured, so as to avoid premature optimization ala Knuth).
What would be the most efficient and error-tolerant method of performing these tasks?
My thoughts:
Regarding (1), I really have no idea other than checking the entire table against the new table, at which point I feel I might as well just download the new (potentially and likely unchanged) table and replace the old one, but this takes more time.
For (2): Ideally I'd be able to construct something similar to an array of SELECT statements, send them all at once, and have the database return one ResultSet per internal query. From what I understand, however, neither Statement nor PreparedStatement support returning multiple ResultSet objects.
Lastly, the best way I can think of doing (3) is to batch a number of (2) operations.
There is nothing special about having moving requirements, but the single most important thing to use when talking to most databases is having a connection pool in your Java application and use it properly.
This also applies here. The IBM i DB2/400 database is quite fast, and the database driver available in the jt400 project (type 4, no native code) is quite good, so you can pull over quite a bit of data in a short while simply by generating SQL on the fly.
Note that if you only have a single schema you can tell in the conneciton which one you need, and can then use non-qualified table names in your SQL statements. Read the JDBC properties in the InfoCenter very carefully - it is a bit tricky to get right. If you need multiple schemaes, the "naming=system" allows for library lists - i.e. a list of schemaes to look for the tables, which can be very useful when done correctly. The IBM i folks can help you here.
That said, if the connection is the limiting factor, you might have a very strong case for running the "create object from tables" Java code directly on the IBM i. You should already now prepare for being able to measure the traffic to the database - either with network monitoring tooling, using p6spy or simply going through a proxy (perhaps even a throtteling one)
Ideally, you would have the database group provide you with a set of stored procedures to optimize the access to the database.
Since you don't have access, you may want to ask them if they have timestamp data in the database at the row level to see when records were modified, this way you can select only the data that's changed since some point in time.
What #ThorbjørnRavnAndersen is suggesting is moving the database code on to the IBM host and connecting to it via RMI or JMS from the client. So the server code would be a RMI or JMS Server that accesses the database on your behalf and returns you java objects instead of bringing SQL resultsets across the wire.
I would pass along your requirements to the database team and see if they can't do something for you. I'm sure they don't want all these remote clients bringing all the data down each time, so it would benefit them as much as it would benefit you.

Does the navigation through result set depend on driver type being used?

Suppose am using a driver and am doing a DB call to fetch some data. I store the data in the resultSet.
My question is, does the traversing through the resultSet formed from the DB call, depend on the diver ?
If we could do only forward only traverse or direction insensitive traversing and all depend on the type of driver being used ?
Thank you.
Yes support for ResultSet types depends on the driver. All JDBC drivers are required to support TYPE_FORWARD_ONLY, all other ResultSet types are optional: some databases don't support scrolling and driver implementers don't always want to simulate that by retrieving all rows and keeping them in memory driverside (not to mention the potential of exhausting all available memory that way).

Can you put multiple statements in one query-string in Oracle jdbc?

I have a JDBC connection to an Oracle database. I create a Statement. The SQL query String contains multiple statements separated by a semicolon, and is provided by a different system.
Example:
connection.prepareStatement("SELECT * FROM A; SELECT * FROM B");
According to ddimitrov it isn't possible.
But all other databases I've tried support it. And JDBC even has support to retrieve multiple results.
Does anyone have either pointers to Oracle documentation explicitly stating that it is not supported or have a way to make it work (without using of stored procedures)?
For executing multiple statements:
JDBC 2.0 lets you submit multiple statements at one time with the addBatch method
See here.
No, this is not possible with the Oracle JDBC driver.
You will have to parse and split the string into their individual statements.
Btw: I think the only databases that allow this are Microsoft SQL Server and MySQL. Which also makes them vulnerable to certain kind of SQL injection attacks that would not work Oracle or PostgreSQL.
AFAIK most databases only allow you to execute / prepare one statement per execute or prepare call. Although not very explicitly expressed, the intent of the JDBC methods is to execute a single SQL statement:
sql - **an** SQL statement that may [...]
The retrieval of multiple resultsets is for (very rare) single(!) statements or stored procedures which return multiple resultsets (as explained in the javadoc of Statement#execute).

SQL multi-command atomicity question

I am trying to create a program that updates 2 different tables using sql commands. The only thing I am worried about is that if the program updates one of the tables and then loses connection or whatever and does NOT update the other table there could be an issue. Is there a way I could either
A. Update them at the exact same time
or
B. Revert the first update if the second one fails.
Yes use a SQL transaction. Here is the tutorial:JDBC Transactions
Depending on the database, I'd suggest using a stored procedure or function based on the operations involved. They're supported by:
MySQL
Oracle
SQL Server
PostgreSQL
These encapsulate a database transaction (atomic in nature -- it either happens, or it doesn't at all), without the extra weight of sending the queries over the line to the database... Because they already exist on the database, the queries are parameterized (safe from SQL injection attacks) which means less data is sent -- only the parameter values.
Most SQL servers support transactions, that is, queueing up a set of actions and then having them happen atomically. To do this, you wrap your queries as such:
START TRANSACTION;
*do stuff*
COMMIT;
You can consult your server's documentation for more information about what additional features it supports. For example, here is a more detailed discussion of transactions in MySQL.

Java JDBC Lazy-Loaded ResultSet

Is there a way to get a ResultSet you obtain from running a JDBC query to be lazily-loaded? I want each row to be loaded as I request it and not beforehand.
Short answer:
Use Statement.setFetchSize(1) before calling executeQuery().
Long answer:
This depends very much on which JDBC driver you are using. You might want to take a look at this page, which describes the behavior of MySQL, Oracle, SQL Server, and DB2.
Major take-aways:
Each database (i.e. each JDBC driver) has its own default behavior.
Some drivers will respect setFetchSize() without any caveats, whereas others require some "help".
MySQL is an especially strange case. See this article. It sounds like if you call setFetchSize(Integer.MIN_VALUE), then it will download the rows one at a time, but it's not perfectly clear.
Another example: here's the documentation for the PostgreSQL behavior. If auto-commit is turned on, then the ResultSet will fetch all the rows at once, but if it's off, then you can use setFetchSize() as expected.
One last thing to keep in mind: these JDBC driver settings only affect what happens on the client side. The server may still load the entire result set into memory, but you can control how the client downloads the results.
Could you not achieve this by setting the fetch size for your Statement to 1?
If you only fetch 1 row at a time each row shouldn't be loaded until you called next() on the ResultSet.
e.g.
Statement statement = connection.createStatement();
statement.setFetchSize(1);
ResultSet resultSet = statement.executeQuery("SELECT .....");
while (resultSet.next())
{
// process results. each call to next() should fetch the next row
}
There is an answer provided here.
Quote:
The Presto JDBC driver never buffers the entire result set in memory. The server API will return at most ~1MB of data to the driver per request. The driver will not request more data from the server until that data is consumed (by calling the next() method on ResultSet an appropriate number of times).
Because of how the server API works, the driver fetch size is ignored (per the JDBC specification, it is only a hint).
Prove that the setFetchSize is ignored
I think what you would want to do is defer the actually loading of the ResultSet itself. You would need to implement that manually.
You will find this a LOT easier using hibernate. You will basically have to roll-your-own if you are using jdbc directly.
The fetching strategies in hibernate are highly configurable, and will most likely offer performance options you weren't even aware of.

Categories

Resources