3

This is the situation

In jOOQ, there is a lot of need for abstraction over JDBC. I want the jOOQ client code to be unaware of the fact, that some data is retrieved from a simple ResultSet, some data is retrieved from SQLInput (for UDTs), or from CallableStatements (for stored procedures/functions). Hence, I want to add abstraction over these JDBC types:

java.sql.ResultSet
java.sql.CallableStatement
java.sql.SQLInput
java.sql.SQLOutput

Now they all work pretty much the same way. They usually have a get and set method for every data type in java.sql.Types. For example, they ship with methods like

BigDecimal getBigDecimal(int index);
int getInt(int index);

And they all have methods like

boolean wasNull();

The problem

Unfortunately, these JDBC interfaces don't extend a single common interface making lives easier for those who want to write generic JDBC code like this snippet here (just an example to support my question):

// As JDBC has no support for BigInteger types directly,
// read a BigDecimal and transform it to a BigInteger
BigDecimal result = null;

if (source instanceof ResultSet) {
    result = ((ResultSet) source).getBigDecimal(index);
}
else if (source instanceof CallableStatement) {
    result = ((CallableStatement) source).getBigDecimal(index);
}
else if (source instanceof SQLInput) {
    result = ((SQLInput) source).readBigDecimal();
}

return result == null ? null : result.toBigInteger();

The above code needs to be written for all three of them, ResultSet, CallableStatement, SQLInput. And there are lots of similar examples

My question is

  • Does anyone know a JDBC extension library that solves this problem elegantly?
  • Or should I write a simple wrapper class (or adapter) for all of these types myself?
  • Or would you just accept that fact and keep duplicating internal library code?

Which solution do you prefer and why? Thanks for any feedback

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509

3 Answers3

3
  • Does anyone know a JDBC extension library that solves this problem elegantly?

No, someone has to invent it yet.


  • Or should I write a simple wrapper class (or adapter) for all of these types myself?

I'd definitely go for this. Start with a common wrapper interface.

public interface DataProvider {
    public BigInteger getBigInteger(int columnIndex);
    // ...
}

Let all concrete wrappers implement it.

public class ResultSetDataProvider implements DataProvider {
    private ResultSet resultSet;

    public ResultSetDataProvider(ResultSet resultSet) {
        this.resultSet = resultSet;
    }

    public BigInteger getBigInteger(int columnIndex) {
        BigDecimal bigDecimal = resultSet.getBigDecimal(columnIndex);
        return bigDecimal != null ? bigDecimal.toBigInteger() : null;
    }

    // ...
}

And use it instead.

try {
    // Acquire ResultSet.
    DataProvider dataProvider = new ResultSetDataProvider(resultSet);
    // Process DataProvider.
} finally {
    // Close ResultSet.
}

  • Or would you just accept that fact and keep duplicating internal library code?

No, I wouldn't. Keep your code DRY.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I guess the wrapper is the way to go. Thanks for your elaborations! – Lukas Eder Dec 19 '10 at 23:00
  • I don't see how that's DRY. Your `CallableStatementDataProvider` would have to replicate the `public BigInteger getBigInteger(int columnIndex)` method using a `CallableStatement` private field (instead of a `ResultSet` one)! I think there's no fix for that as JDBC library authors failed to define a common subtype of both `ResultSet` and `CallableStatement` – Marcus Junius Brutus May 04 '17 at 15:53
1

Personally, I wouldn't use such a thing. I don't see that you're making my life much easier with this abstraction.

I don't think there's a reason for SQL abstractions to leak out from a persistence tier. I make the call, map into objects, and close the SQL abstractions. You sound like you want them to be sticking around, which is a bad idea.

I think the Spring folks have made using JDBC about as easy as it can be. I could be wrong, but I don't see a reason to go down the path you're suggesting.

If I look at the javadocs for SQLInput, I see this:

This interface, used only for custom mapping, is used by the driver behind the scenes, and a programmer never directly invokes SQLInput methods.

I'm not sure why you see it necessary to expose this interface.

As for ResultSet and CallableStatement (or any Statement, for that matter), those can ultimately return one or more ResultSets to return query results. I'd rather see an abstraction around that. I believe you're muddying the waters by exposing the others. I wouldn't recommend it.

Perhaps the fact that it's never been done is another indication that it shouldn't be done. But you're welcome to do it and see if the marketplace votes you a winner.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • I'm not sure I understand what "SQL abstractions" you are referring to. Is it jOOQ in general or just the above statements? Because the above statements are an example of a rather internal part of jOOQ. Of course, all JDBC objects are correctly closed, but for the purpose of the example, I left out 99% of the jOOQ Code ;-) Besides, jOOQ goes way beyond what the spring folks did with JDBC. Compare Spring JDBC examples (http://static.springsource.org/spring/docs/2.0.x/reference/jdbc.html) with jOOQ Examples (https://sourceforge.net/apps/trac/jooq/wiki/Examples) – Lukas Eder Dec 19 '10 at 17:19
  • Might not be a proper comparison, since Spring is up to version 3.1. Not sure I'm buying it in any case, because Spring has had far more traction than jOOQ. This is the first time I've heard of it. I'm sure that I'm not interested right now on the basis of what I've seen, but perhaps someday. – duffymo Dec 19 '10 at 17:26
  • You're right about the Spring version. My bad. The feature set doesn't seem fundamentally different in 3.1, though. Of course you're entitled to your own opinion about any third party software, but since you've taken the time to express that opinion, maybe you could still consider the actual question itself? :) It was a rather technical question, actually. – Lukas Eder Dec 19 '10 at 17:34
  • I did consider the actual question. Neither I nor anyone else who has answered knows of such a thing; some have suggested that it needs to be invented. I wouldn't be interested in doing such a thing or using it if someone did. The abstractions I've got available to me are sufficient for my needs. – duffymo Dec 19 '10 at 21:23
  • Hello duffymo! I've checked the Javadoc, you are right about SQLInput. That's quite surprising and confusing to me as it seems to be the perfect way to create custom mappings for UDTs. In our application (not jOOQ), we use Oracle Advanced Queues for communication between servers. Oracle AQ send data from the database to a Java callback method using UDT's. And implementing SQLData, reading from SQLInput seemed to us to be the perfect way to receive that data. Let's see, if these abstractions prove useful for others... Thanks again for your time! – Lukas Eder Dec 19 '10 at 22:50
  • In case you're interested in a follow-up on the UDT-SQLInput question, I've asked a new question for that specific problem: http://stackoverflow.com/questions/4485746/whats-the-best-way-to-read-a-udt-from-a-database-with-java – Lukas Eder Dec 19 '10 at 23:26
0
  1. Not sure of an extension for JDBC.
  2. A wrapper class or the visitor pattern might be useful depending on the rest of your design.
  3. What about extending each one and making them implement the same interface that has the same method signatures to get the data you want.
jzd
  • 23,473
  • 9
  • 54
  • 76
  • 1
    I'm not sure about the visitor pattern. As far as I know, that's mainly used for visiting nodes in a data-structure and usually overkill for any design. Extending each one, having the extension implement the same interface will precisely be a "wrapper class". – Lukas Eder Dec 19 '10 at 14:29
  • A visitor pattern can be used beyond visiting nodes, but again I don't know what the rest of your design looks like so this might not be appropriate. A wrapper class would just contain an instance of the class and make calls to it. That is different than extending the object and implementing an additional interface. – jzd Dec 19 '10 at 14:40
  • OK, you're right about the visitor and the extension. But extension is not an option here. We're talking about ResultSet, CallableStatement, etc. The concrete implementation is a JDBC-driver-specific class, e.g. oracle.jdbc.driver.OracleResultSetImpl. I cannot/do not want to extend that class. – Lukas Eder Dec 19 '10 at 14:57